# Storage How sherlock persists secrets and runtime state. ## Decisions | Topic | Decision | |---|---| | Token storage | OS keyring via [`github.com/zalando/go-keyring`](https://github.com/zalando/go-keyring). No on-disk credential files, no `age` blobs, no plaintext. | | Pre-flight | Both `sherlock` and `sherlock-broker` call `keyring.Probe()` at startup. A missing/locked keyring fails fast with a platform-appropriate hint and exit code `3`. | | Service name | `sherlock` for real tokens; `sherlock-preflight` for the probe sentinel. | | Account key | The Authentik `sub` claim once logged in, `default` before. | | Runtime files | `$XDG_RUNTIME_DIR/sherlock.sock`, `sherlock.pid`, `sherlock.log`, `sherlock/.mcp.json`. All `0600` (files) / `0700` (dirs). | | Config files | `$XDG_CONFIG_HOME/sherlock/agents.d/*.toml` (operator overrides) and `services.d/*.toml` (Phase 2+). | ## TokenSet What we store under the `sherlock` keyring service: ```go type TokenSet struct { IDToken string AccessToken string RefreshToken string IDExpiresAt time.Time RefreshExpAt time.Time Issuer string Subject string Email string Name string } ``` Serialized as a single JSON blob — the keyring exposes one secret per `(service, account)` pair and we don't want to round-trip multiple secrets. ## Pre-flight semantics `keyring.Probe()`: 1. Writes a fixed sentinel value under service `sherlock-preflight`, account `probe`. 2. Reads it back, verifies the round-trip. 3. Deletes it. If any step fails the call returns `keyring.ErrUnavailable` wrapping the underlying cause. `keyring.IsUnavailable(err)` is the test; `keyring.RemediationHint()` returns a per-OS one-liner. CLI behaviour: any failure prints the wrapped error and the hint, then exits with code `3`. ## Platform notes | OS | Backend | Common setup snag | |---|---|---| | Linux | Secret Service (D-Bus) | `gnome-keyring-daemon`, KWallet, or `keepassxc` with Secret Service enabled must be running for the session. Headless boxes need `gnome-keyring-daemon --components=secrets` started inside the session bus. | | macOS | Keychain | Works out of the box. First write may prompt for unlock. | | Windows | Credential Manager | Works out of the box. | ## Why not files We considered an age-encrypted token blob and dropped it: the keyring gives us OS-managed locking, session affinity, and consistent multi-user behaviour for free, and avoids inventing a new key management story. The trade-off — Linux headless setups need a deliberate session keyring — is the right one for a homelab operator tool where the operator already has a desktop session.