Documentation
¶
Overview ¶
Package secrets dispatches provider API-key lookups across pluggable backends.
Keyget is invoked from Claude Code's apiKeyHelper, so it must keep the key bytes off of disk, environment, and logs: the caller (cc-fleet keyget command) writes the result to stdout exactly once and exits. Nothing in this package may log the key bytes themselves. (Round-robin rotation persists a small monotonic counter to <provider>.rotation — that integer is NOT a key, so keeping it on disk does not weaken this contract.)
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoEnabledKey = errors.New("no enabled API key")
ErrNoEnabledKey is returned by the file backend when a provider has no enabled API key to hand out (empty key set, or every entry disabled). keyget surfaces it without writing any key bytes.
Functions ¶
func IsMultiKey ¶
IsMultiKey reports whether provider is in multi-key mode (a <provider>.keys.json exists). Used to guard the CLI `edit --api-key` path, which only manages the legacy single key.
func Keyget ¶
Keyget resolves the API key for provider by looking up its providers.toml entry and delegating to the configured secret backend.
The returned bytes have trailing CR/LF stripped so the caller can write them to stdout verbatim. The key is never logged.
func MaskKey ¶
MaskKey renders a key for display so the full secret never reaches the screen or a log. Keys of length >= 8 show the first and last 3 runes (e.g. "sk-…238"); shorter keys (including empty) are fully obscured by bullets (at least 3) so neither the middle nor any prefix/suffix leaks.
func RemoveKeySet ¶
RemoveKeySet best-effort deletes a provider's multi-key store and rotation counter (<provider>.keys.json + <provider>.rotation). A missing file is not an error — this is idempotent cleanup invoked from userops.Remove. It returns a non-nil error only on a real removal failure (e.g. permissions).
func SafeRef ¶
SafeRef rejects a file-backend secret_ref that could escape SecretsDir when joined onto it. A file-backend ref must name a single flat file *inside* the secrets dir, so a path separator, a ".."/"." component, or an absolute path is refused. It is the secret_ref analogue of safeProviderName and is enforced on every file-backend read/write path (userops.writeFileSecret / removeFileSecret / Add / Edit and loadLegacyKeySet below) so a hand-edited providers.toml can never turn a ref like "../../etc/shadow" into a read or write outside the secrets dir.
It applies ONLY to the file backend: pass / 1password / vault / keyring refs legitimately contain "/" (e.g. "secret/data/x", "op://vault/item/field") and are never used to build a SecretsDir path. The error names neither the ref nor any key — the ref is a filename, not a secret, but the message is kept content-free for consistency with the no-leak discipline.
func SaveKeySet ¶
SaveKeySet atomically writes ks to <provider>.keys.json (0600, secrets dir 0700). Writing here is what migrates a provider from legacy single-key to multi-key mode: the caller seeds ks[0] from LoadKeySet (which returns the legacy key) and appends the new entries before saving.
A nil/empty ks is persisted as "[]" (an explicit empty store), never "null".
Types ¶
type KeyEntry ¶
type KeyEntry struct {
Label string `json:"label"`
Key string `json:"key"`
Enabled bool `json:"enabled"`
}
KeyEntry is one API key inside a provider's multi-key store. The JSON tags are the public on-disk schema: scripts/tests may hand-write keys.json.
Label is a human-readable name shown in the TUI (empty renders as "keyN"); it is NOT a secret. Key is the secret. Enabled gates per-key selection.
func LoadKeySet ¶
LoadKeySet resolves a provider's key set by priority:
- <provider>.keys.json exists -> parse it (multi-key mode; authoritative).
- else the legacy secret_ref file exists -> one enabled entry from it.
- else -> empty set (keyget then reports "no enabled API key").
A keys.json that fails to parse is a hard error (we do NOT silently fall back to the legacy file — that could hand out the wrong key). The error wraps ONLY the json error, never the file bytes or any KeyEntry (key-safety).