1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! The credential store seam and the injected clock (auth §5, §5.4; arch §6.4,
//! §6.5). `Secret` redacts at the type level; a `Cred`'s variant IS the
//! token-kind discriminant (no `token_type` flag) and `expires_at` is absolute
//! (no `is_valid` flag — freshness is a query) — so this module stores only what
//! cannot be computed. No IO lives here: the XDG-backed file store and the system
//! clock are `bz`-side impls behind these traits; the in-memory `CredStore` and
//! `FakeClock` doubles live in `testing`. Ambient discovery (auth §5.5) splits the
//! same way — the pure `parse_ambient` (foreign bytes → `Cred`) is here, its file
//! read + `$HOME` expansion are the `bz` `discover` impl.
use fmt;
use io;
use ;
use crateModel;
/// A plaintext secret whose `Debug`/`Display` redact and whose only plaintext
/// reads are `expose()` (the single audited site) and `Serialize` (reached only
/// by `CredStore::put` writing the 0600 file) — auth §5.3.
;
/// The stored secret bundle for one provider (auth §5.1). The variant IS the
/// token-kind discriminant; `expires_at` is ABSOLUTE unix-seconds; there is no
/// `is_valid` flag (freshness is the `now + SKEW >= expires_at` query) and no
/// provider name (the file path is the name).
/// Persist and retrieve one secret bundle per provider (auth §5.2, §5.5). `get`
/// returns `None` for a missing cred (the no-creds path), never an error; refresh
/// is `OAuth2::apply` using get+put (freshness is a query); list/delete are
/// control-plane. `discover` is the third primitive (auth §5.5): read a *foreign*
/// credential source named by an [`AmbientSpec`] — a file (Claude Code's `~/.claude/…`)
/// or a process env var (a vendor key alias) — into a brazen `Cred`. The IO — a file
/// read with `$HOME` expansion, or an env read — lives in the `bz` impl; the format
/// parse is the pure [`parse_ambient`]; a store with no ambient backing returns `None`.
/// The per-provider model-list cache (model-discovery §5.1) — filesystem state, so
/// like `CredStore` it lives behind an injected trait; the pure lib never touches the
/// disk. A SIBLING of `CredStore`, not folded into it: a secret and a regenerable
/// model list are different facts with different files. The `bz` bin backs it with
/// one JSON file per provider under `$XDG_CACHE_HOME/brazen/models/<provider>.json`
/// (the `{"models":[{id,default}]}` shape `list-models --json` emits, reused); the
/// in-memory double lives in [`testing`](crate::testing).
///
/// Regenerable: a miss — or an unreadable/corrupt/garbage file — is `None`, never an
/// error, so a cold or corrupt cache degrades to `select_model`'s verbatim path and
/// self-heals on the next `bz --list-models`. `put` has two callers — `--list-models`
/// (wholesale replace) and the generation data plane (learn-on-success append of the one
/// model a 2xx used, model-discovery §5.4) — and is atomic (temp + rename so a concurrent
/// reader never sees a half-written file) and best-effort: a write failure warns but does
/// not fail the request.
/// An ambient credential source as row DATA (auth §5.5): `path` is the source
/// LOCATOR the `bz` impl reads — a `~`/`$HOME`-expanded filesystem path for a file
/// format, an environment-variable NAME for `api_key_env` — and `format` selects the
/// pure parser that maps its bytes to a `Cred`. Neither lives in core code, so
/// deleting the row's `ambient` block deletes the capability (severability).
/// Which foreign credential format an [`AmbientSpec`] names (auth §5.5). A closed
/// enum, not a JSON-pointer DSL: each shape needs a parser anyway, so one variant
/// per known source is less mechanism than a speculative mapping language. The
/// variant ALSO tells the `bz` `discover` impl where to read: a file (`ClaudeCode`)
/// or a process env var (`ApiKeyEnv`).
/// Map a foreign credential source's bytes to a brazen `Cred` (auth §5.5) — the pure
/// half of discovery, so the `bz` impl does only the IO. `None` for malformed or
/// incomplete input (the no-creds path, like `get`). The `claude_code` case reads
/// `claudeAiOauth` into a `Cred::OAuth2`: `expiresAt` is MILLISECONDS, divided to
/// absolute unix-seconds once here (the single home for that unit mismatch), and
/// `scopes` join into the `scope` string (`None` when empty); `account_id` is `None`
/// (Anthropic binds no account id). The `api_key_env` case is the env var's value as a
/// raw `Cred::ApiKey` (trimmed; empty/non-UTF-8 ⇒ `None`).
/// The `api_key_env` parse: the source's bytes ARE the raw API key. Trim incidental
/// surrounding whitespace (a `$(cat keyfile)` trailing newline), and treat empty or
/// non-UTF-8 as `None` — the no-creds path, so an `ANTHROPIC_API_KEY=` never writes a
/// blank auth header.
/// The one injected time source in the data plane (auth §5.4): unix seconds. The
/// library never calls `SystemTime::now`; `bz` wires `SystemClock`, tests wire
/// `FakeClock`.