Expand description
OS-keychain-backed storage for browser-pane HTTP Basic-Auth credentials, scoped per identity so one agent’s saved site credential is never reachable through another identity’s browsing session.
Reuses crate::identity::secret_store’s existing keyring-crate
wrapper (already backing SecretRef::Keychain for account API keys) —
this module only builds the right account-key string and JSON-encodes
the {username, password} pair; no new keychain code, no filesystem
path involved (so agentmux_common::data_paths::sanitize_path_segment
genuinely doesn’t apply here — nothing is written to disk).
See docs/status/majestic-painting-minsky plan (credential-isolated browser-pane auto-fill) and docs/specs/SPEC_BROWSER_PANE_HTTP_BASIC_AUTH_2026_05_18.md (the v1 spec that named “Persisted credential store (OS keyring)” as deferred future work — this module is that follow-up).
Structs§
- Browser
Credential - A saved browser credential.
Zeroizes on drop so a decoded value doesn’t linger in memory longer than the caller actually needs it — mirrorssecret_store::get’s ownZeroizing<String>discipline for the raw keychain read.
Functions§
- account_
key 🔒 - Build the keychain account string for one (identity, protection-space)
pair. Deliberately NOT naive
:-concatenation of the raw fields —originitself contains:(https://host:8443) andrealmis server-supplied, effectively attacker-influenced, free text, so two different (origin, realm) pairs could otherwise concatenate to the same string (e.g.origin="https://a:1"+realm="b"vsorigin="https://a"+realm="1:b"). Hashing a NUL-separated tuple removes any field-boundary ambiguity — NUL can’t appear in either field in practice, but even if it could, the hash input’s own delimiter collision risk is the same NUL-in-a-field edge case for both operands, which cancels out (unlike:, which is valid content inorigin). - delete
- Delete the stored credential for this identity + protection-space.
Idempotent (a missing entry is success) — same contract as
secret_store::delete. Called when a stored credential turns out to be wrong (a re-challenge shortly after an auto-fill — see the credential_broker orchestration in agentmux-cef). - load
- Look up a stored credential.
Ok(None)means “nothing saved for this identity + protection-space” (not an error) — same contract assecret_store::get_optional, which this wraps directly so a transient keychain failure (locked keychain, no Secret Service daemon, permission denied) is distinguishable from “genuinely never saved” at the call site, the same distinctionsecret_store’s own doc comment argues for. - save
- Store (or overwrite)
username/passwordfor this identity + protection-space. Unbounded (no timeout) — seesecret_store’s doc comment for why a write must not race a timed-out-but-later-completing keychain call.