car-secrets 0.53.0

Cross-platform secret store for Common Agent Runtime
Documentation
# car-secrets

Cross-platform secret store for [Common Agent Runtime](https://github.com/Parslee-ai/car).

## What it does

Unifies OS-native secure storage across the three platforms CAR targets:

| Platform | Backend |
|----------|---------|
| macOS    | `/usr/bin/security` over Keychain Services |
| Windows  | Credential Manager (DPAPI) |
| Linux    | Secret Service — GNOME Keyring / KWallet / KeePassXC / anything speaking `org.freedesktop.secrets` |

The API is intentionally small: `put`, `publish`, `get`, `delete`, `status`,
`list`. Callers choose a `service` (namespace) and an account (key); values are
UTF-8 strings. JSON helpers are provided for structured values.

`publish` is reserved for an already-composed authoritative value that must
replace its predecessor without a visible delete gap. The debug file backend
uses an owner-private staging file plus rename; macOS updates the Keychain item
in place. Windows uses two deterministic Credential Manager generations:

- each chunk carries a publication revision and the root sentinel is written
  only after every inactive-generation chunk exists;
- the previous generation is retained so a reader that already captured its
  root can finish, while a revision mismatch or missing retired chunk causes a
  bounded retry against the current root;
- a non-secret high-water manifest is written before staging, so a crash at
  any phase leaves a fixed, recoverable set of entries instead of a fresh
  unreachable nonce namespace; and
- cleanup after the root commit is best-effort and retryable. A cleanup error
  does not report the already-committed publication as failed, and metadata
  errors before commit leave the only good generation untouched.

`publish` calls for the same ref must be serialized by the caller (Parslee auth
uses its cross-process coordinator lock); readers may run concurrently with a
publisher.

## macOS approval persistence

Every macOS operation uses the Apple-signed `/usr/bin/security` helper. The
in-process `keyring`/Security.framework backend is not compiled into
`car-secrets` on macOS, so CAR has one stable Keychain identity across app,
daemon, CLI, agent, release, and debug builds.

`put` and `publish` use `add-generic-password -U -A`. A new item grants the
stable helper access; an existing item is updated in place so its ACL, partition
list, and every approval the user already granted remain intact. A read that
shows a password or access dialog is read-only even after the user approves it:
CAR does not automatically delete, recreate, or rewrite that item, and does not
run `set-generic-password-partition-list` from an unattended process. The grant
therefore remains attached instead of being discarded by CAR's next repair.

Only an explicit caller operation mutates an existing item: `put`/`publish`
updates its value and `delete` removes it. The internal availability probe may
delete only the unique non-secret sentinel it just wrote. Automatic recovery
never rewrites an item, including one created by another application.

## Availability

On headless Linux without a Secret Service daemon, `put` / `get` / `delete` return `SecretError::Unavailable`. **There is no silent plaintext fallback.** Callers should probe `is_available()` before relying on the store, or handle `Unavailable` with their own fallback.

## Security boundary

Secrets never enter CAR memory, state, or prompt context unless a caller **explicitly** reads them and passes them into one of those systems. The store treats a missing backend as a hard error so misconfigured environments are loud, not silently insecure.

## Where it fits

Surfaced via the WebSocket `secret.*` methods. Used by `car-integrations` for OAuth token storage and by any user app that needs to persist credentials gathered through its own auth flow.

On macOS, `car-secrets` uses the Apple-signed `/usr/bin/security` helper for
reads, writes, status checks, and deletes. Every child has a bounded runtime and
is killed and reaped at the deadline, so a Keychain authorization prompt cannot
wedge an async CAR service indefinitely. Reads parse the helper's
byte-preserving `-g` output so valid UTF-8 values, including trailing newlines,
round-trip without repeated Keychain prompts when rebuilt CAR helper binaries
get new CDHashes.