/** Pluggable credential vault interface. v2.9+ may add Keychain / Vault impls. */ export interface TokenStore { read(agentName: string): Promise; write(agentName: string, token: string): Promise; delete(agentName: string): Promise; } /** * Resolve the vault directory for the active instance. Mirrors * `resolveInstanceDbPath()` so DB and vault always live together — no * split-brain where the daemon serves per-instance DB while the vault sits * in legacy. * * Returns: `/agents` for multi-instance, or * `~/.bot-relay/agents` in single-instance legacy mode. */ export declare function resolveAgentVaultDir(): string; export declare class FileTokenStore implements TokenStore { /** Override for tests. Production uses `resolveAgentVaultDir()`. */ private readonly vaultDir; constructor(opts?: { vaultDir?: string; }); /** Vault file path for a given agent name. */ pathFor(agentName: string): string; /** * Read the cached token for `agentName`. Returns null on miss, malformed * content, or any IO error. Never throws on the read path — every failure * mode is treated as a cache miss so the caller falls through to the * `register_agent` path cleanly. * * Token shape is validated to defense-in-depth against a tampered file * (an attacker-writable home dir is already game-over, but we don't want * a malformed string flowing through to AppleScript embedding or HTTP * headers downstream). */ read(agentName: string): Promise; /** * v2.6.1 R1 — sync read variant for the daemon hot path * (`src/server.ts:resolveToken`). Codex caught that v2.6.1 R0 wrote to the * vault but never CONSUMED it on the daemon side: stdio MCP servers fork * with whatever env they inherit, and the SessionStart hook's `export * RELAY_AGENT_TOKEN` only mutates the hook subprocess. The daemon must * fall through to a vault read when the env-supplied token is empty. * * Sync to avoid cascading every auth-gated tool to async — vault read is * a single-line file (microseconds). Same shape semantics as `read`: * never throws on miss/malformed/IO error; always returns null in those * cases so the caller falls cleanly through to "no token". */ readSync(agentName: string): string | null; /** * Atomic write: tmp file + rename. The rename step is atomic on POSIX * filesystems and on NTFS for same-volume operations (always true here — * tmp is a sibling of the target). Concurrent spawns of the same agent * name converge on the last-writer's value without the file ever appearing * partially written. * * Perms: file 0o600, parent dir 0o700. On Windows, `chmod` is a best-effort * no-op; the parent dir under `%USERPROFILE%` already inherits a user- * restricted ACL by default (documented in SECURITY.md). v2.9+ Windows * Credential Manager helper will move beyond profile-dir defaults. */ write(agentName: string, token: string): Promise; /** * Delete the cached token. Idempotent — missing file returns clean. * Used by `relay recover` to scrub stale credentials before the operator * re-bootstraps. */ delete(agentName: string): Promise; /** * v2.6.2 R1 — sync delete variant for synchronous handler call sites * (`src/tools/identity.ts:handleRevokeToken`). Mirrors the readSync ↔ read * pair. Same idempotent ENOENT-swallow semantics. Using sync here avoids * cascading the revoke_token handler to async (which would propagate * through the dispatcher and into every test that exercises revoke). Sync * unlink of a single file is microseconds — no perf concern. */ deleteSync(agentName: string): void; } export declare function defaultTokenStore(): FileTokenStore; /** Test seam: drop the cached singleton so the next call re-resolves. */ export declare function _resetDefaultTokenStoreForTests(): void; //# sourceMappingURL=token-store.d.ts.map