Add system keyring credential caching - #865
Draft
wlynch wants to merge 3 commits into
Draft
Conversation
Add a credential cache backed by the OS keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service) via zalando/go-keyring, so signing credentials (ephemeral private key + Fulcio cert/chain) can be reused for the lifetime of the certificate without running the gitsign-credential-cache daemon. Enable with gitsign.credentialCacheMode=keyring (or GITSIGN_CREDENTIAL_CACHE_MODE=keyring). Credentials are cached per identity, keyed by a hash of the configuration used to obtain them (Fulcio URL, OIDC issuer, client ID, connector ID, and committer email), so multiple identities can be stored concurrently. Expired or invalid entries are deleted lazily on read, and keyring failures (locked keychain, headless hosts) fall through to the normal OIDC flow. Chain data is chunked across entries to stay under the Windows credential blob size limit. The existing daemon cache is consolidated onto the same building blocks: - A shared cache.Cache interface, credential key derivation, cert validation, and credential encode/decode helpers are used by both backends. The daemon client now uses the config-derived identity key instead of hostname@cwd (the key is opaque to the daemon, so mixed client/daemon versions interoperate), giving the daemon per-identity, multi-identity caching. - The daemon stores entries with a TTL matching the certificate lifetime instead of a fixed 10 minutes, overwrites on re-store instead of erroring, and rejects already-expired certs. - Plain cache misses are reported as a sentinel error so first use no longer prints "error getting cached creds". A new `gitsign credentials list` / `gitsign credentials clear [--all]` subcommand inspects and removes cached credentials for whichever backend is configured (keyring directly, or the daemon via new List/Delete RPCs; old daemons get a clear upgrade error). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
gitsign.credentialCacheMode now accepts "system" (or "socket") - the "keyring" alias is removed before the option ships. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
wlynch
marked this pull request as draft
August 4, 2026 23:03
Replace zalando/go-keyring with 99designs/keyring for the system keyring credential cache: - Keys() enumeration removes the need for the best-effort index entry that powered `gitsign credentials list` - entries are now enumerated directly from the keyring. - Storage is restricted to native OS credential stores (Windows Credential Manager, Secret Service, KWallet) - no file/pass fallbacks that would need their own password prompts. - 99designs' macOS Keychain backend requires cgo, but gitsign is built with CGO_ENABLED=0 everywhere (releases cross-compile darwin on Linux runners). On macOS, use a small backend implementing the keyring.Keyring interface on top of the /usr/bin/security CLI instead (the same approach zalando/go-keyring uses). This keeps behavior identical across release binaries and source builds regardless of CGO settings. - Tests inject keyring.NewArrayKeyring instead of relying on process-global mock state; a live unavailable-keyring stub covers soft-fail behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a credential cache backed by the OS keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service), so signing credentials (ephemeral private key + Fulcio cert/chain) can be reused for the lifetime of the certificate without running the
gitsign-credential-cachedaemon:(or
GITSIGN_CREDENTIAL_CACHE_MODE=system;socketselects the existing daemon.)Design
user.email). Per-repo config naturally selects different cached identities.NotAfteris recorded at store time; expired or invalid entries are deleted lazily on read and re-validated against the Fulcio roots (same checks as the daemon client, incl. the 30s expiry window).error getting cached creds(for either backend).99designs/keyringfor native OS credential stores (Windows Credential Manager, Secret Service, KWallet), with itsKeys()API powering enumeration. Its macOS Keychain backend requires cgo, which gitsign builds don't use (releases cross-compile darwin on Linux runners), so on macOS a small backend implements thekeyring.Keyringinterface on top of the/usr/bin/securityCLI — behavior is identical across release binaries and source builds regardless of CGO settings.Daemon consolidation
Both backends now share the same building blocks (
cache.Cache/cache.Managerinterfaces, key derivation, cert validation, credential encode/decode):hostname@cwd. The key is opaque to the daemon, so mixed client/daemon versions interoperate; the daemon gains per-identity, multi-identity caching. Behavior change: repos sharing identical identity config now share a cached credential instead of caching per working directory (seecmd/gitsign-credential-cache/README.mdupdate).Management
New
gitsign credentials list/gitsign credentials clear [--all]subcommands inspect and remove cached credentials for whichever backend is configured (keyring directly viaKeys()enumeration; daemon via newListCredentials/DeleteCredential/DeleteAllCredentialsRPCs — old daemons get an explicit "upgrade the daemon" error).Docs
docs/keyring-cache.md: setup, multi-identity semantics, platform notes, and security considerations (per-session process access, encrypted-at-rest persistence across reboots, config-derived key caveat).docs/cli/.Reviewer notes
StoreCredentialRequest.Metafield on old daemons; cache keys are opaque strings; the client maps "not found" errors to the miss sentinel by message match (net/rpc flattens errors to strings).internal/cache/key_test.go) pins the key derivation so accidental changes that would orphan users' stored entries fail CI.gitsign credentialswas verified against a live daemon over its socket.darwin && cgobackend using Security.framework ACLs so only the gitsign binary reads silently.Testing
go build ./...,go vet ./..., fullgo test ./...pass.🤖 Generated with Claude Code