Changelog
All notable changes to Gunnar are listed here, one section per version published on crates.io. Each entry names the merge commit on master it comes from. The format follows Keep a Changelog, and Gunnar uses Semantic Versioning.
Each fixed vulnerability is listed under Security with its RUSTSEC, CVE or GHSA id. To report a vulnerability, see SECURITY.md.
Unreleased
Changes on master after 1.6.0.
[1.6.0] - 2026-10-31
Published from the master commit the tag v1.6.0 names. Changes since 1.5.1.
Crate versions
gunnar,gunnar-cli(now on crates.io),gunnar-crypt,gunnar-wire,gunnar-http,gunnar-client,gunnar-store,gunnar-policy,gunnar-lfs,gunnar-forgeandgunnar-pairing-doorare 1.6.0.gunnar-core,gunnar-proto,gunnar-repair,gunnar-common,gunnar-grpc,gunnar-serverandgunnar-sshare 3.0.0: cargo-semver-checks against crates.io's 2.0.0 finds removed or changed public items in all but gunnar-ssh, which moves with the types it exposes.gunnar-recipeis 0.2.0 (its suites and rows changed).- A dependent asks the version it needs: gunnar-crypt 1.6.0 (the
pqkeys and the security key's v2 wrap), the 3.0.0 crates by name.
Security
- RA5-32: the router's IdentityService believes the client field only from the console it relays for (ClientTrust::EveryPeer over the listener's mTLS); nornir-identity 0.2.3 believes it only from loopback by default.
- nornir-git 0.2.4: the older russh (0.63.3) leaves every lock (GHSA-j7h2-66j4-ghjf, GHSA-4wc5-f2rc-q74m).
- gunnar-ssh and gunnar-client run on russh 0.64.1 (GHSA-j7h2-66j4-ghjf, GHSA-4wc5-f2rc-q74m) (034ff65a).
- RA5-34: the forge door on tcp/2222 is edda's one TCP forward, with a connection cap, an idle timeout and a refused address (c3dc9d27). RA5-36: a store change persists a copy and swaps it in, so a failed write changes nothing (32085850).
- The root lock and every detached lock that carried them take the fixed releases (3d9aaf24): pulldown-cmark 0.13.5 (RUSTSEC-2026-0414), rustls-webpki 0.103.16 (RUSTSEC-2026-0382), tokio 1.54.1 (RUSTSEC-2026-0386), wasmtime 49.0.4 (RUSTSEC-2026-0378, RUSTSEC-2026-0379), webpki-roots and webpki-root-certs 1.0.11 (RUSTSEC-2026-0381, RUSTSEC-2026-0383), and in the bench h2 0.4.20 (RUSTSEC-2026-0258). The
Cargo.lockpackaged with 1.5.1 still holds tokio 1.53.1, rustls-webpki 0.103.15, pulldown-cmark 0.13.4 and webpki-roots 1.0.9. Acargo install gunnarwithout--lockedalready resolves the fixed versions. - The gunnar-front and gunnar-monetize-guest locks move to skidbladnir 0.2.3, with zstd 0.14 and zbus 6 (RUSTSEC-2026-0370 to RUSTSEC-2026-0372, RUSTSEC-2026-0391, RUSTSEC-2026-0398, RUSTSEC-2026-0403, RUSTSEC-2026-0410) (3d9aaf24).
- The bench, installtest and gunnar-growbench locks take zstd 0.14.3 (RUSTSEC-2026-0391, RUSTSEC-2026-0398, RUSTSEC-2026-0403, RUSTSEC-2026-0410) and replace the yanked chacha20 0.10.1 with 0.10.2 (776554a1).
- basic-toml (RUSTSEC-2026-0374, unmaintained, no fixed release) is ignored in
deny.tomlwith its reason: it is only a compile-time dependency of age's i18n macros and links into no binary (3d9aaf24).
Added
- The release signing key in
slsa/: the owner's YubiKey key (PIV slot 9c, P-256, PIN once, touch always), key idd24d3785e8281bcd37e8f2c40a3a85e8152d3ce2200a5c929a1a5ca636e8249e, with the YubiKey's attestation of it and the attestation CA (f62cf899). - SLSA Build L3 release provenance:
docs/SLSA.md, the release key inslsa/, and the attestations inattestations/(72b99016). docs/FEATURES.md: Gunnar's features with status, date, source path and commit (9b7d1d85).Repos.ListDeletedanswers the default retention days (d0fb62cf).- Company SSO: domains are proven by DNS, and
GUNNAR_SSO_DNS_RESOLVERnames the resolver for that proof (96bdc862, d6a76a69). gunnar crypt level [quantum-safe]: says an encrypted repository's level, or re-wraps a classic one to every member's quantum-safe key and rotates the ring (--key NAME=…,--from-server); a member without a quantum-safe key is named and nothing changes. The level only goes up (fac1b4e7).- gunnar-crypt: feature
pq(the quantum-safe key and ring, native and wasm32, without SSH),Level::NEW,Members::at,Members::to_quantum_safe,userkey::wrap_is_quantum_safeanduserkey::SSH_WRAP_IS_CLASSICAL(fac1b4e7).
Changed
-
A security key's wrap is quantum-safe. The person's encryption key is wrapped to a security key symmetrically: HKDF-SHA256 over the WebAuthn PRF / FIDO2 hmac-secret output with a per-wrap salt (info
gunnar-crypt user-key wrap v2), then XChaCha20-Poly1305 (gunnar_crypt::skwrap). There is no X25519 step. An older X25519 (v1) wrap still opens, and the CLI and the console re-wrap it to v2 on the next unlock; gunnar replaces the v1 wrap and never takes a v1 over a v2. The CLI and the console mark a v2 security key quantum-safe; the SSH key's wrap stays marked not quantum-safe (CRYPT-PQ-SK). -
Quantum-safe is the default for encrypted repositories.
gunnar crypt initmakes a quantum-safe repository (the key ring wrapped only to ML-KEM-768 + X25519age1pq1…keys) unless--level classicis given, andgunnar crypt key newmakes a quantum-safe key unless--classicis given.--quantum-safeis gone from both. A person with a classic key is told to use--level classic, and nothing is written. The SSH key's wrap of a quantum-safe key is classical (X25519 or RSA-OAEP), and the CLI says so when it makes one;gunnar crypt key statusmarks each way in's wrap. Existing repositories keep their level: a members file without alevelline is classic, as before (fac1b4e7). -
gunnar-crypt:
Members::new()andLevel::default()are quantum-safe; thewrapfeature ispqplus the SSH method (fac1b4e7). -
Gunnar runs on edda's shared identity core,
nornir-identity0.2 (70ab1c7a, 43fae822). -
Company SSO is gunnar-server's own default-off feature
sso(c237f47e). -
An import is held to the memory this server may really use (216447e5).
-
gunnar-server stays generic: a console setting fed again is stored as an opaque setting (ff52a93b).
Fixed
- git protocol v0:
deepen-relativeand the ACK that closes the haves (5dd1c40a). - A shallow fetch with a filter sends a named want (ee5f7f9b).
- A person's browser approval of a forge event works again: Gunnar attests the event from a stepped-up passkey session (a12df55f).
- A soft delete reaches the twin and a restored box, so the repository stays deleted there (0ac13ea0).
- The main stores the public-site switch it is fed, so the public site of gunnar.rs can be switched on (39d41950).
- A laid main keeps the mail relay it is fed (0ecda8af, fef22e02).
- SAML: a save without metadata keeps the stored entity id, sign-on URL and certificates (688d27f0).
1.5.1 - 2026-10-27
Published from 6eedc2d6. Changes since 1.5.0.
Security
- The CLI pins each person's encryption public key per forge host and account, and refuses a forge answer that differs. Changes to unlock methods over the SSH door are in the account audit, and the CLI zeroizes its secrets (b0fea9a8, review rows RA4-1 high, RA4-3, RA4-15).
- A provisioned SCIM or CSV employee is claimed only on the admin's binding or on an e-mail address the identity provider asserts is verified (264c56e9, review row A34-2 high).
- The CLI's gRPC path through the front keeps the console's rules and never carries
Bootstrap.Enroll, so an unenrolled box cannot be claimed through it (a68c3df9). - A server setting change goes to the hash-chained audit trail before it is applied, and is refused when the audit record cannot be written (c27902d7, 13b84581).
- SSO administration calls need a fresh, user-verified passkey touch (9f607d80).
- A refused SSO claim names nobody in the answer or the logs (16627e47).
- crypto-bigint 0.7.5 (yanked) is replaced by 0.7.10 in the root lock and seven detached locks (197f5a6a).
Added
- One encryption key per person for encrypted repositories, with YubiKey and security key unlock (FIDO2 hmac-secret or WebAuthn PRF) (513ca0fc). This removes
gunnar unlockandupgrade-pq; there is no reader for the old format. - Background work runs as named jera jobs (f3a94013).
gunnar control --at https://<site>reaches the control plane through the front's port 443, with the caller's own credential (bfbb8483).cargo xtask idptests company SSO against a real Keycloak in a container (d05729ff).- An
mcpfeature on gunnar-front, off by default (f730eb5e).
Changed
- Default builds are airgap builds: no outbound connection unless configured (efda2e01, 253498b3).
- One version of each dependency where Gunnar chooses, and the plain default build leaves out company SSO (bcfaba7c).
- WebAuthn and step-up come from edda's
nornir-auth-webauthnandnornir-identity-sessioncrates (3b409609, 873ec53b). - Gunnar moves to the shared monetize 0.3.0 crates, and gunnar-monetize-guest 2.0.0 speaks the six-operation product contract (fee665e2, 21435f46).
Fixed
- An import is DONE only when its references are present; otherwise it FAILS with the reason (e95cc71a).
- A panic in a pump call is caught and the puller is rebuilt (57ae54fe).
- The job ledger closes rows that a stopped or crashed run left running (13143ba7).
- The front's update re-admits boxes that were left at their install listener (2cd07aed, c8857a5c).
Known advisories
- RUSTSEC-2023-0071 (
rsa, no fixed release) is ignored with its reason: Gunnar's SSH host key is ed25519 only. - The packaged
Cargo.lockholds the four versions listed under Unreleased; they are fixed onmaster.
1.5.0 - 2026-10-22
Published from 3aa3acdb. The git tag v1.5.0 marks the earlier release cut of 2026-10-12 (fe49c32f). Changes since the tag v1.4.0 (2026-09-10), which was not published on crates.io.
Security
- Fixes from the security review of 2026-09-11, each with a test: a revoked key loses the session it already opened (1d62d861); a deleted principal's tokens do not come back with its name (d6b93161); a remote cannot choose the contents of
.git/HEAD(e0621e77); the forge cannot choose where a token is sent (995d68ec); a mirror may only point where its caller could go (25b2a783), and its reference batch passes the same gate as a push (57b7b8b5); the HTTP listener has a time limit and a size limit (ac4b6b6e); a sealed slot no longer reads whatever is put in it (9184f9ac); an upload path no longer prints a secret (ffd644df); bootstrap writes a private key only where there was none (915eb0a7); a missing want is refused like a forbidden one (833732eb). - rustls 0.23.45 and then 0.23.46 (RUSTSEC-2026-0285, RUSTSEC-2026-0346, RUSTSEC-2026-0348, RUSTSEC-2026-0349) (9d8fe5fa, 07a3b79a).
- wasmtime 49 replaces wasmtime 45 (RUSTSEC-2026-0222, RUSTSEC-2026-0269, RUSTSEC-2026-0316) (0ad8e16e), and wasmtime 49.0.3 follows (RUSTSEC-2026-0353 to RUSTSEC-2026-0364) (07a3b79a).
- proc-macro-error2 (RUSTSEC-2026-0173, unmaintained, compile-time only) is ignored in
deny.tomlwith its reason (061a536d).
Added
cargo install gunnarinstalls a server that starts with no configuration: SSH and smart HTTP on loopback, and/healthz(df52a9d2).- A lone clone builds the server with a plain
cargo build --release(3aa3acdb). - Runtime growth of the data set:
gunnar control grow start|status|resume(4008f65b, 2b5251e6). - Repository import from GitHub and Codeberg (623cc236).
gunnar create, and a first push in sha256 creates a sha256 repository (ef4ec6f6).gunnar settings(visibility, default branch) over the forge's SSH door (ba0822e7).- History shows all branches (
Refs.Log all_refs) (3f0f4d61). - The admin's list of principals shows each person's last sign-in and last operation (cd07bc2f, c240b2e2).
Changed
- The twin only stores: it applies the control plane and keeps the data frames as bytes (954c74d6).
- The appliance's PID 1, DHCP and clock come from edda's
appliance-pid1andestate-clockcrates (7e33c0f7, 20cb318f). - The pairing door runs the estate's WireGuard hub (3fa5c9ad).
- gix 0.88, ureq 3, and the toml and base64 majors (0ad8e16e, c3616f31).
- The licence files name the joint 50/50 ownership by Vetra Automation AB and Ignalina ApS (0242b074).
Earlier versions
The tag v1.4.0 (2026-09-10) marks a git release only. No version of the gunnar crate before 1.5.0 was published on crates.io.