@cotal-ai/auth 0.0.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/dist/callout.d.ts +108 -0
  3. package/dist/callout.d.ts.map +1 -0
  4. package/dist/callout.js +219 -0
  5. package/dist/callout.js.map +1 -0
  6. package/dist/commands.d.ts +2 -0
  7. package/dist/commands.d.ts.map +1 -0
  8. package/dist/commands.js +359 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/derive.d.ts +15 -0
  11. package/dist/derive.d.ts.map +1 -0
  12. package/dist/derive.js +72 -0
  13. package/dist/derive.js.map +1 -0
  14. package/dist/idp.d.ts +63 -0
  15. package/dist/idp.d.ts.map +1 -0
  16. package/dist/idp.js +125 -0
  17. package/dist/idp.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +13 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/issuer.d.ts +78 -0
  23. package/dist/issuer.d.ts.map +1 -0
  24. package/dist/issuer.js +137 -0
  25. package/dist/issuer.js.map +1 -0
  26. package/dist/ledger.d.ts +108 -0
  27. package/dist/ledger.d.ts.map +1 -0
  28. package/dist/ledger.js +399 -0
  29. package/dist/ledger.js.map +1 -0
  30. package/dist/login.d.ts +69 -0
  31. package/dist/login.d.ts.map +1 -0
  32. package/dist/login.js +338 -0
  33. package/dist/login.js.map +1 -0
  34. package/dist/permissions.d.ts +28 -0
  35. package/dist/permissions.d.ts.map +1 -0
  36. package/dist/permissions.js +50 -0
  37. package/dist/permissions.js.map +1 -0
  38. package/dist/provider.d.ts +18 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +213 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/service.d.ts +10 -0
  43. package/dist/service.d.ts.map +1 -0
  44. package/dist/service.js +288 -0
  45. package/dist/service.js.map +1 -0
  46. package/dist/store.d.ts +82 -0
  47. package/dist/store.d.ts.map +1 -0
  48. package/dist/store.js +208 -0
  49. package/dist/store.js.map +1 -0
  50. package/dist/token.d.ts +68 -0
  51. package/dist/token.d.ts.map +1 -0
  52. package/dist/token.js +128 -0
  53. package/dist/token.js.map +1 -0
  54. package/package.json +35 -5
  55. package/README.md +0 -4
package/dist/ledger.js ADDED
@@ -0,0 +1,399 @@
1
+ /**
2
+ * The local ACTOR LEDGER — the server-side authority over which (owner, actor) pairs may run agents
3
+ * in this space, and with what capability scope + channel ACL (plan gate 4: "define the local actor
4
+ * ledger + channel ACL resolver used by both the IdP bridge and callout permission supplier").
5
+ *
6
+ * This is THE single authorization source on the user-auth path — both trust boundaries read it:
7
+ * - the IdP bridge's `authorizeActor` (Plane 1, bearer MINT time): is this actor granted, and what
8
+ * `scope`/`parent` does its bearer carry;
9
+ * - the callout's `authorizeActor` + `AclResolver` (Plane 2, CONNECT time): is the actor STILL
10
+ * granted (a revoked row kills new connects even while an old bearer lives), has its granted scope
11
+ * been narrowed since the bearer was minted, and what channel read/post ACL + role gets minted.
12
+ *
13
+ * TWO DISJOINT ROW SPACES, split by construction (the gate-1 review convergence): `actors/` holds
14
+ * INTERACTIVE rows (written by `cotal actor grant`, exchanged via a human IdP proof) and
15
+ * `managed-actors/` holds MANAGED-AGENT rows (written only by the spawn path, carrying the hash of
16
+ * the per-agent secret, exchanged only via that secret). The IdP path reads only the interactive
17
+ * space and the agent path only the managed space, so no corruption, partial write, or misrepair
18
+ * can reclassify a managed child as human-exchangeable — the fail-open class is unrepresentable,
19
+ * not detected. Writers refuse a duplicate (owner, actor) across the two spaces; the CONNECT
20
+ * boundary (which must serve bearers minted by either path) reads both through ONE helper that
21
+ * fails closed on a duplicate.
22
+ *
23
+ * There is NO allow-by-default anywhere: a missing row is a deny, an empty ledger denies everyone.
24
+ * Rows are keyed (owner, actor); the owner is the opaque derived `u_…` token — the ledger holds no
25
+ * IdP subject/email (grant-time derivation maps sub → owner; an optional operator label is for
26
+ * `list` legibility only). Reads are FRESH per call (the delivery-daemon posture): a grant/revoke is
27
+ * live at the very next exchange/connect with no daemon restart.
28
+ *
29
+ * Storage is ONE FILE PER ROW (`<space>/<owner>.<actor>.json`, atomic tmp+rename writes) — the mesh
30
+ * registry's lost-update posture: concurrent grant/revoke (an operator command racing the manager's
31
+ * spawn-time grant) never read-modify-write a shared file, and a crash damages at most one row.
32
+ * UNLIKE the mesh registry, a corrupt row FAILS CLOSED with a thrown sentence — an auth ledger never
33
+ * skips what it cannot read (a "skipped" row would silently revoke or, in a list, silently hide a
34
+ * live grant from the operator).
35
+ */
36
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
37
+ import { existsSync, readdirSync, readFileSync, renameSync, rmSync } from "node:fs";
38
+ import { join } from "node:path";
39
+ import { assertDerivedOwnerToken, assertValidChannel, assertValidOwnerToken, mkSecretDir, patternInAllow, writeSecretFile, } from "@cotal-ai/core";
40
+ const ACTORS_DIR = "actors";
41
+ const MANAGED_DIR = "managed-actors";
42
+ const LEDGER_VER = 1;
43
+ function spaceDir(dir, kind) {
44
+ return join(dir, kind === "interactive" ? ACTORS_DIR : MANAGED_DIR);
45
+ }
46
+ /** The row's filename IS its principal dot-form + `.json` — both segments are grammar-asserted
47
+ * (owner `u_` + base32, actor a plain token), so the name is filesystem-safe by construction. */
48
+ function rowPath(dir, kind, owner, actor) {
49
+ assertDerivedOwnerToken(owner);
50
+ assertValidOwnerToken(actor);
51
+ return join(spaceDir(dir, kind), `${owner}.${actor}.json`);
52
+ }
53
+ /** Read + validate one row file against ITS space's shape. FAIL-CLOSED: unreadable/malformed/
54
+ * unknown-version/wrong-shape throws a sentence naming the file — never a skip, never a
55
+ * reclassification (an interactive row carrying a token hash was written by the wrong path; a
56
+ * managed row without a valid one has a corrupted grant — both deny with the repair). */
57
+ function readRow(path, kind) {
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(readFileSync(path, "utf8"));
61
+ }
62
+ catch (e) {
63
+ throw new Error(`${path}: unreadable actor grant (${e instanceof Error ? e.message : String(e)}) - fix or remove the row; a broken row denies its actor and fails ledger listings`);
64
+ }
65
+ if (parsed === null || typeof parsed !== "object" || parsed.ver !== LEDGER_VER)
66
+ throw new Error(`${path}: unknown actor-grant version ${String(parsed?.ver)} (expected ${LEDGER_VER}) - refusing to guess at an authorization row`);
67
+ assertDerivedOwnerToken(parsed.owner);
68
+ assertValidOwnerToken(parsed.actor);
69
+ if (!Array.isArray(parsed.scope) || !Array.isArray(parsed.allowSubscribe) || !Array.isArray(parsed.allowPublish))
70
+ throw new Error(`${path}: actor grant is missing explicit scope/allowSubscribe/allowPublish lists`);
71
+ if (kind === "interactive" && parsed.tokenHash !== undefined)
72
+ throw new Error(`${path}: an interactive grant must not carry an agent token hash - it was written by the wrong path; remove the row and re-grant (\`cotal actor grant\`)`);
73
+ if (kind === "managed-agent" && !(typeof parsed.tokenHash === "string" && /^[0-9a-f]{64}$/.test(parsed.tokenHash)))
74
+ throw new Error(`${path}: managed-agent grant has a missing/corrupted secret hash - respawn the agent (\`cotal spawn\`) to rewrite it`);
75
+ const { ver: _ver, ...row } = parsed;
76
+ return row;
77
+ }
78
+ /** Every granted row in BOTH spaces, read fresh, tagged with its kind. Missing dirs = EMPTY ledger
79
+ * (deny-all). A row that cannot be read throws (fail closed) rather than being silently omitted
80
+ * from an authorization listing. */
81
+ export function loadActorLedger(dir) {
82
+ const out = [];
83
+ for (const kind of ["interactive", "managed-agent"]) {
84
+ const d = spaceDir(dir, kind);
85
+ if (!existsSync(d))
86
+ continue;
87
+ for (const f of readdirSync(d).filter((f) => f.endsWith(".json")).sort())
88
+ out.push({ ...readRow(join(d, f), kind), kind });
89
+ }
90
+ return out;
91
+ }
92
+ /** Find one row in ONE space, reading only that row's file. Undefined = not granted there. A
93
+ * corrupt file for THIS principal throws — deny with the reason, not a miss. */
94
+ function findIn(dir, kind, owner, actor) {
95
+ const p = rowPath(dir, kind, owner, actor);
96
+ if (!existsSync(p))
97
+ return undefined;
98
+ return readRow(p, kind);
99
+ }
100
+ export function findInteractiveActor(dir, owner, actor) {
101
+ return findIn(dir, "interactive", owner, actor);
102
+ }
103
+ export function findManagedActor(dir, owner, actor) {
104
+ return findIn(dir, "managed-agent", owner, actor);
105
+ }
106
+ /** The CONNECT boundary's unified read — a bearer minted by EITHER exchange path authorizes here,
107
+ * so both spaces are consulted. A (owner, actor) present in BOTH is a broken ledger (the writers
108
+ * refuse it; only manual tampering produces it) and DENIES, fail-closed, naming both files. */
109
+ export function findActorUnified(dir, owner, actor) {
110
+ const interactive = findIn(dir, "interactive", owner, actor);
111
+ const managed = findIn(dir, "managed-agent", owner, actor);
112
+ if (interactive && managed)
113
+ throw new Error(`actor "${actor}" is granted in BOTH the interactive and managed row spaces - a broken ledger denies; remove one of ${rowPath(dir, "interactive", owner, actor)} / ${rowPath(dir, "managed-agent", owner, actor)}`);
114
+ if (interactive)
115
+ return { ...interactive, kind: "interactive" };
116
+ if (managed)
117
+ return { ...managed, kind: "managed-agent" };
118
+ return undefined;
119
+ }
120
+ function writeRow(dir, kind, row) {
121
+ const path = rowPath(dir, kind, row.owner, row.actor);
122
+ mkSecretDir(dir);
123
+ mkSecretDir(spaceDir(dir, kind));
124
+ const tmp = `${path}.${process.pid}.tmp`;
125
+ writeSecretFile(tmp, JSON.stringify({ ver: LEDGER_VER, ...row }, null, 2));
126
+ renameSync(tmp, path);
127
+ }
128
+ function assertRowInputs(row) {
129
+ if (row.parent !== undefined) {
130
+ const parts = row.parent.split(".");
131
+ if (parts.length !== 2)
132
+ throw new Error(`grantActor: parent "${row.parent}" is not a principal (<owner>.<actor>)`);
133
+ assertDerivedOwnerToken(parts[0]);
134
+ assertValidOwnerToken(parts[1]);
135
+ }
136
+ if (!Array.isArray(row.scope) || !Array.isArray(row.allowSubscribe) || !Array.isArray(row.allowPublish))
137
+ throw new Error("grantActor: explicit scope/allowSubscribe/allowPublish lists are required");
138
+ // Policy-grammar validation at the WRITE boundary (mirroring the mint chokepoint's re-assert in
139
+ // core `permissionsFor`): a row's ACL lists are the delegation envelope for everything under it,
140
+ // so a malformed entry (`a.>.b`) must never be stored where containment would later read it.
141
+ for (const ch of [...row.allowSubscribe, ...row.allowPublish])
142
+ assertValidChannel(ch);
143
+ // Scope entries are capability TOKENS (compared by equality, embedded in bearers and repair
144
+ // commands) — an open vocabulary (`spawn`, `admin`, `role:<r>`, …), but a closed grammar: no
145
+ // whitespace, no shell/subject metacharacters, nothing `--scope` CSV can't round-trip.
146
+ for (const s of row.scope)
147
+ if (!/^[A-Za-z0-9_:-]+$/.test(s))
148
+ throw new Error(`grantActor: scope entry "${s}" must be a plain capability token ([A-Za-z0-9_:-])`);
149
+ // The role is embedded in `role:<r>` capability tokens and `svc_<role>` durable names — same
150
+ // closed grammar, validated where it enters the ledger.
151
+ if (row.role !== undefined && !/^[A-Za-z0-9_-]+$/.test(row.role))
152
+ throw new Error(`grantActor: role "${row.role}" must be a plain token ([A-Za-z0-9_-])`);
153
+ }
154
+ /** Grant (or update — an upsert, so re-granting narrows/widens in place) an INTERACTIVE (owner,
155
+ * actor) row. Refuses a token hash (that's the managed space's shape, written only by the spawn
156
+ * path) and refuses to shadow an existing managed-agent row — the two spaces stay disjoint at the
157
+ * write, so the read sides never disambiguate.
158
+ *
159
+ * NOT attenuated by design: interactive rows are OPERATOR-authored (the local `cotal actor grant`
160
+ * CLI is the only writer), and the operator is the authority the envelope rule bottoms out in. An
161
+ * optional `parent` here is an audit link only — do not build a delegated-USER write path on this
162
+ * function; user-authored delegation belongs in the managed space, where the chain walk enforces
163
+ * the envelope (an interactive row that carries a parent still gets link-checked when a managed
164
+ * chain passes THROUGH it). */
165
+ export function grantActor(dir, row) {
166
+ assertRowInputs(row);
167
+ if (row.tokenHash !== undefined)
168
+ throw new Error("grantActor: an interactive grant cannot carry an agent token hash - managed-agent rows are written by the spawn path only");
169
+ const managedRefusal = () => new Error(`actor "${row.actor}" is a managed agent - its grant is owned by the spawn lifecycle (respawn rewrites it, despawn revokes it); pick another actor name for an interactive grant`);
170
+ if (findIn(dir, "managed-agent", row.owner, row.actor))
171
+ throw managedRefusal();
172
+ const full = { ...row, grantedAt: new Date().toISOString() };
173
+ writeRow(dir, "interactive", full);
174
+ // Post-write compensation for the check-then-write race (an operator grant racing a spawn for
175
+ // the same name): if the OTHER space's row appeared meanwhile, remove the just-written row and
176
+ // refuse — at most one row survives, without a cross-process lock protocol. The unified connect
177
+ // read's duplicate deny stays the backstop for a crash landing exactly between these two lines.
178
+ if (findIn(dir, "managed-agent", row.owner, row.actor)) {
179
+ revokeIn(dir, "interactive", row.owner, row.actor);
180
+ throw managedRefusal();
181
+ }
182
+ return full;
183
+ }
184
+ /** Delegation attenuation — the ENVELOPE rule: everything under an owner stays within the
185
+ * spawner's own grant. A spawn-scoped actor can delegate only a SUBSET of what it holds — channel
186
+ * ACLs by NATS-pattern containment ({@link patternInAllow}), capability scope by set inclusion —
187
+ * never more; a spawner whose row carries `admin` is the operator authority and is exempt, and a
188
+ * row with NO parent is an operator/roster boot (the operator is the authority). Enforced at BOTH
189
+ * managed-row boundaries: authorship ({@link grantManagedActor}) and every agent bearer exchange
190
+ * ({@link ledgerAuthorizeAgentExchange}) — so narrowing or revoking a spawner bites its agents at
191
+ * their next refresh (≤ {@link AGENT_BEARER_TTL_SEC}s), instead of leaving them orphaned. */
192
+ function assertWithinSpawnerGrant(dir, row, boundary) {
193
+ // Walk the WHOLE delegation chain, not just the immediate link: per-link containment composes
194
+ // transitively (child ⊆ parent at every hop ⇒ leaf ⊆ root), and every link must still EXIST — a
195
+ // single-hop check would let grandchildren outlive a revoked root (their immediate parent row
196
+ // survives the root's deletion) and let a revoked root's still-live child keep minting. The walk
197
+ // stops at an authority root: an admin-scoped ancestor or a parentless (operator/roster) row.
198
+ // `seen` is the cycle guard — legitimate upserts can close a parent loop (re-parent a root under
199
+ // its own descendant), which must deny, not spin.
200
+ const leaf = row.actor;
201
+ let child = row;
202
+ const seen = new Set([`${row.owner}.${row.actor}`]);
203
+ while (child.parent) {
204
+ const parentKey = child.parent;
205
+ const childKey = `${child.owner}.${child.actor}`;
206
+ const deep = child !== row;
207
+ if (seen.has(parentKey))
208
+ throw new Error(`agent "${leaf}": its delegation chain contains a cycle at "${parentKey}" - a broken ledger denies; repair the parent links (\`cotal actor list\`)`);
209
+ seen.add(parentKey);
210
+ const dot = parentKey.indexOf(".");
211
+ const pOwner = parentKey.slice(0, dot);
212
+ const pActor = parentKey.slice(dot + 1);
213
+ const parent = findActorUnified(dir, pOwner, pActor);
214
+ if (!parent)
215
+ throw new Error(boundary === "spawn"
216
+ ? `spawner "${parentKey}"${deep ? ` (an ancestor of "${leaf}")` : ""} has no grant in this space - delegation flows from a granted spawner chain; grant it first (\`cotal actor grant ${pActor} --owner ${pOwner} --scope spawn\`)`
217
+ : `agent "${leaf}": spawner "${parentKey}"${deep ? ` (an ancestor)` : ""} is no longer granted - revoking a spawner revokes everything under it; re-grant it, then respawn (\`cotal spawn\`)`);
218
+ if (parent.scope.includes("admin"))
219
+ return;
220
+ if (child.owner !== pOwner)
221
+ throw new Error(`agent "${childKey}" cannot be delegated by spawner "${parentKey}" of a different owner - cross-owner spawns are operator (admin) launches only`);
222
+ const overScope = child.scope.filter((s) => !parent.scope.includes(s));
223
+ const overSub = child.allowSubscribe.filter((ch) => !patternInAllow(parent.allowSubscribe, ch));
224
+ const overPub = child.allowPublish.filter((ch) => !patternInAllow(parent.allowPublish, ch));
225
+ // A role is RECEIVE reach (bind/consume the shared `svc_<role>` task queue), so it is a
226
+ // delegated capability like any other: the spawner's scope must carry `role:<r>` to hand it
227
+ // down. Rides the scope list — no extra row field, and scope's own per-link inclusion makes
228
+ // role delegation transitive for free.
229
+ const needRole = child.role && !parent.scope.includes(`role:${child.role}`) ? `role:${child.role}` : undefined;
230
+ if (overScope.length || overSub.length || overPub.length || needRole) {
231
+ const wrongs = [
232
+ overScope.length ? `scope [${overScope.join(", ")}] beyond [${parent.scope.join(", ") || "none"}]` : "",
233
+ overSub.length ? `read [${overSub.join(", ")}] beyond [${parent.allowSubscribe.join(", ") || "none"}]` : "",
234
+ overPub.length ? `post [${overPub.join(", ")}] beyond [${parent.allowPublish.join(", ") || "none"}]` : "",
235
+ needRole ? `role "${child.role}" beyond scope [${parent.scope.join(", ") || "none"}] (a role is delegated with the \`${needRole}\` capability)` : "",
236
+ ].filter(Boolean);
237
+ const widen = widenGrantCommand(pOwner, pActor, parent, [...overScope, ...(needRole ? [needRole] : [])], overSub, overPub);
238
+ const who = deep ? `agent "${leaf}": its ancestor "${childKey}"` : `agent "${leaf}"`;
239
+ throw new Error((boundary === "spawn"
240
+ ? `${who} would exceed its spawner's grant - delegation only narrows: `
241
+ : `${who} exceeds its spawner's CURRENT grant (narrowed since spawn): `) +
242
+ wrongs.join("; ") +
243
+ ` - the mesh operator widens the spawner with \`${widen}\`` +
244
+ (boundary === "spawn" ? ", then respawn" : ", or respawn within it"));
245
+ }
246
+ child = parent;
247
+ }
248
+ }
249
+ /** The exact re-grant that would admit the refused delegation: the spawner's CURRENT row with the
250
+ * missing entries unioned in. A grant is an upsert of the WHOLE row, so the repair must carry
251
+ * every field — a bare `--allow-subscribe` would silently narrow the rest. */
252
+ function widenGrantCommand(pOwner, pActor, parent, addScope, addSub, addPub) {
253
+ const union = (base, add) => [...new Set([...base, ...add])];
254
+ // The command is offered for copy-paste into a shell, so every free-form field is POSIX
255
+ // single-quote escaped — a row label like `x'; rm -rf ~` must never become a live command.
256
+ const shq = (s) => `'${s.replaceAll("'", `'\\''`)}'`;
257
+ const parts = [`cotal actor grant ${pActor} --owner ${pOwner}`];
258
+ const scope = union(parent.scope, addScope);
259
+ if (scope.length)
260
+ parts.push(`--scope ${shq(scope.join(","))}`);
261
+ parts.push(`--allow-subscribe ${shq(union(parent.allowSubscribe, addSub).join(","))}`);
262
+ parts.push(`--allow-publish ${shq(union(parent.allowPublish, addPub).join(","))}`);
263
+ if (parent.role)
264
+ parts.push(`--role ${shq(parent.role)}`);
265
+ if (parent.label)
266
+ parts.push(`--label ${shq(parent.label)}`);
267
+ return parts.join(" ");
268
+ }
269
+ /** Author a MANAGED-AGENT row (spawn path only): the same upsert semantics, in the managed space,
270
+ * with the secret hash REQUIRED — and never shadowing an interactive row. */
271
+ export function grantManagedActor(dir, row) {
272
+ assertRowInputs(row);
273
+ if (!/^[0-9a-f]{64}$/.test(row.tokenHash))
274
+ throw new Error("grantManagedActor: tokenHash must be a sha256 hex digest");
275
+ assertWithinSpawnerGrant(dir, row, "spawn");
276
+ const shadowRefusal = () => new Error(`actor "${row.actor}" already has an interactive grant - a managed agent cannot shadow it; revoke it first (\`cotal actor revoke ${row.actor}\`) or spawn under another name`);
277
+ if (findIn(dir, "interactive", row.owner, row.actor))
278
+ throw shadowRefusal();
279
+ const full = { ...row, grantedAt: new Date().toISOString() };
280
+ writeRow(dir, "managed-agent", full);
281
+ // Symmetric post-write compensation (see grantActor) — at most one surviving row per principal.
282
+ if (findIn(dir, "interactive", row.owner, row.actor)) {
283
+ revokeIn(dir, "managed-agent", row.owner, row.actor);
284
+ throw shadowRefusal();
285
+ }
286
+ return full;
287
+ }
288
+ /** Revoke a row in one space. Returns false when there was nothing to revoke. NOTE: this stops NEW
289
+ * bearer mints and NEW connects immediately (both boundaries read fresh); an ALREADY-LIVE
290
+ * connection dies at its bearer-bound JWT expiry — live eviction is the D5 lever, not the ledger's. */
291
+ export function revokeActor(dir, owner, actor) {
292
+ return revokeIn(dir, "interactive", owner, actor);
293
+ }
294
+ export function revokeManagedActor(dir, owner, actor) {
295
+ return revokeIn(dir, "managed-agent", owner, actor);
296
+ }
297
+ function revokeIn(dir, kind, owner, actor) {
298
+ const p = rowPath(dir, kind, owner, actor);
299
+ if (!existsSync(p))
300
+ return false;
301
+ rmSync(p);
302
+ return true;
303
+ }
304
+ /** The IdP bridge's `authorizeActor` hook (bearer-MINT boundary) — INTERACTIVE rows only, by
305
+ * construction. A name that exists only as a managed agent gets the exact managed-path answer
306
+ * (never "not granted", which would read as a permissions problem, and never a mint). */
307
+ export function ledgerAuthorizeGrant(dir) {
308
+ return (owner, actor) => {
309
+ const row = findInteractiveActor(dir, owner, actor);
310
+ if (!row) {
311
+ if (findManagedActor(dir, owner, actor))
312
+ throw new Error(`actor "${actor}" is a managed agent - it authenticates with its own spawn-time secret; interact with it via the mesh, or respawn it with \`cotal spawn\``);
313
+ throw new Error(`actor "${actor}" is not granted for this user - the mesh operator lets them in with \`cotal actor grant ${actor} --owner ${owner}\` (or --sub <their IdP subject>, printed by their \`cotal login\`)`);
314
+ }
315
+ return { scope: row.scope, ...(row.parent ? { parent: row.parent } : {}) };
316
+ };
317
+ }
318
+ /** The callout's `authorizeActor` hook (CONNECT boundary): the row must still exist — in EITHER
319
+ * space (bearers from both exchange paths connect here) — and the bearer's `act.scope` must sit
320
+ * within the row's CURRENT scope — a bearer minted before a scope narrowing is refused at
321
+ * connect, not honored until expiry. */
322
+ export function ledgerAuthorizeConnect(dir) {
323
+ return (t) => {
324
+ const row = findActorUnified(dir, t.owner, t.act.actor);
325
+ if (!row)
326
+ throw new Error(`actor "${t.act.actor}" is not (or no longer) granted for this owner`);
327
+ const granted = new Set(row.scope);
328
+ for (const s of t.act.scope ?? [])
329
+ if (!granted.has(s))
330
+ throw new Error(`bearer scope "${s}" exceeds the actor's current grant - re-login to mint a fresh bearer`);
331
+ };
332
+ }
333
+ /** The channel-ACL resolver over this ledger — the ONE resolver both the callout permission
334
+ * supplier uses today and the IdP bridge shares when it needs channel authority (gate 4's "shared
335
+ * by both"). Serves bearers from both spaces (unified read). A missing row throws (the callout
336
+ * turns it into a signed deny). */
337
+ export function ledgerAclResolver(dir) {
338
+ return (t) => {
339
+ const row = findActorUnified(dir, t.owner, t.act.actor);
340
+ if (!row)
341
+ throw new Error(`actor "${t.act.actor}" has no ledger row - no channel ACL to mint`);
342
+ return { allowSubscribe: row.allowSubscribe, allowPublish: row.allowPublish, ...(row.role ? { role: row.role } : {}) };
343
+ };
344
+ }
345
+ /** Ensure a filename-hostile owner/actor can never traverse (defense-in-depth behind the grammar
346
+ * asserts in {@link rowPath}) — exported for the smoke that pins the property. */
347
+ export function ledgerRowFilename(owner, actor) {
348
+ assertDerivedOwnerToken(owner);
349
+ assertValidOwnerToken(actor);
350
+ return `${owner}.${actor}.json`;
351
+ }
352
+ /** Where the per-row files live under a provider state dir (for tooling/tests). */
353
+ export function actorLedgerDir(dir) {
354
+ return spaceDir(dir, "interactive");
355
+ }
356
+ /** The managed-agent row space (for tooling/tests). */
357
+ export function managedActorLedgerDir(dir) {
358
+ return spaceDir(dir, "managed-agent");
359
+ }
360
+ /** Agent bearers are SHORT by design — a spawned agent's endpoint re-exchanges ahead of every
361
+ * expiry, so revocation (row deletion) bites a live connection within this window even before
362
+ * live eviction lands. There is no upstream IdP proof to cap to; this constant is that cap. */
363
+ export const AGENT_BEARER_TTL_SEC = 300;
364
+ /** Generate a fresh per-agent exchange secret (returned to the spawner ONCE) + its ledger hash. */
365
+ export function newActorToken() {
366
+ const actorToken = randomBytes(32).toString("base64url");
367
+ return { actorToken, tokenHash: hashActorToken(actorToken) };
368
+ }
369
+ export function hashActorToken(actorToken) {
370
+ return createHash("sha256").update(actorToken, "utf8").digest("hex");
371
+ }
372
+ /** Authorize one AGENT exchange (`{ owner, actor, actorToken }`, no IdP proof) — MANAGED rows only,
373
+ * by construction. The presented secret must hash to the row's (constant-time over the two fixed
374
+ * 32-byte digests). Every failure is the SAME sentence — a prober must not learn whether an
375
+ * (owner, actor) row exists, lives in the other space, or got the secret wrong (the sentence
376
+ * still names the operator's likely fix: respawn). */
377
+ export function ledgerAuthorizeAgentExchange(dir, owner, actor, actorToken) {
378
+ const deny = () => new Error("agent exchange refused: unknown agent or wrong secret - if this agent should exist, respawn it (`cotal spawn`) to rotate its grant");
379
+ let row;
380
+ try {
381
+ row = findManagedActor(dir, owner, actor);
382
+ }
383
+ catch {
384
+ throw deny(); // a corrupt row denies exactly like a missing one on this unauthenticated surface
385
+ }
386
+ if (!row?.tokenHash)
387
+ throw deny();
388
+ const presented = Buffer.from(hashActorToken(actorToken), "hex");
389
+ const stored = Buffer.from(row.tokenHash, "hex");
390
+ if (presented.length !== stored.length || !timingSafeEqual(presented, stored))
391
+ throw deny();
392
+ // Post-authentication (the secret matched), so a SPECIFIC refusal is safe on this surface — and
393
+ // required: rechecking the envelope here makes it a STANDING invariant (an operator narrowing or
394
+ // revoking the spawner bites its agents at their next ≤ AGENT_BEARER_TTL_SEC refresh), the same
395
+ // posture as the connect boundary's "bearer scope vs CURRENT row" check.
396
+ assertWithinSpawnerGrant(dir, row, "exchange");
397
+ return { scope: row.scope, ...(row.parent ? { parent: row.parent } : {}) };
398
+ }
399
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /** A cached IdP login. `token` is the IdP session bearer (Better Auth: `session.token`) — an
2
+ * opaque revocable handle, never a JWT. `expiresAt` (unix seconds) is advisory for messages;
3
+ * the server is the authority (a revoked session dies earlier). */
4
+ export interface IdpSession {
5
+ token: string;
6
+ expiresAt: number;
7
+ /** The IdP subject this session proved at login (display + local owner derivation). Absent only
8
+ * in caches written by older builds — consumers that need it fail loud naming `cotal login`. */
9
+ sub?: string;
10
+ }
11
+ /** What the human must be shown to approve the sign-in. */
12
+ export interface DeviceLoginPrompt {
13
+ verificationUri: string;
14
+ verificationUriComplete: string;
15
+ userCode: string;
16
+ expiresInSec: number;
17
+ }
18
+ export interface DeviceLoginOpts {
19
+ /** The IdP auth base URL (Better Auth: `<origin>/api/auth`). */
20
+ idpUrl: string;
21
+ /** The OAuth client id this CLI presents; the IdP may pin it via its validateClient hook. */
22
+ clientId: string;
23
+ /** Shows the human the verification URL + code. Called exactly once, before polling starts. */
24
+ onPrompt: (prompt: DeviceLoginPrompt) => void;
25
+ }
26
+ /** Normalize + guard the IdP base URL: https (or loopback http for dev), no query/hash, no
27
+ * trailing slash — the normalized string is also the session-cache key, so `…/api/auth` and
28
+ * `…/api/auth/` must land on the same entry. */
29
+ export declare function normalizeIdpUrl(idpUrl: string): string;
30
+ /** Best-effort reachability + shape check of an IdP's JWKS. Used at the FIRST `--user-auth` enable
31
+ * so a dead or typo'd `--idp` fails loud BEFORE a space is provisioned around it, instead of only
32
+ * surfacing far away at the first user connect (the pin is written but the JWKS is fetched lazily).
33
+ * Normalizes the URL exactly as the pin does (`<base>/jwks`). Throws a legible sentence on an
34
+ * unreachable host, a non-2xx, or a body that is not a JWKS key set. */
35
+ export declare function probeIdpJwks(idpUrl: string): Promise<void>;
36
+ /** Sign in via the RFC 8628 device flow and return the IdP session. Fail-loud on every non-happy
37
+ * path: a deny, an expiry, or an unknown poll error is a thrown human sentence, never a retry
38
+ * loop. Blocks (polling at the server's stated interval) until the human approves. */
39
+ export declare function deviceLogin(opts: DeviceLoginOpts): Promise<IdpSession>;
40
+ /** Fetch a fresh short-lived IdP user JWT for the cached session — the input to the bridge
41
+ * exchange. A 401 means the session was revoked or expired at the IdP: the error says exactly
42
+ * how to recover. The JWT is returned, never stored. */
43
+ export declare function fetchIdpJwt(idpUrl: string, sessionToken: string): Promise<string>;
44
+ /** The whole login operation, in the only safe order: device sign-in, then PROVE the session
45
+ * mints a user JWT, and only then persist it. A session that can't produce a JWT must never
46
+ * land on disk — it would pass {@link requireIdpSession}'s no-fallback gate as a dud and defer
47
+ * the failure to some later connect. Returns the session plus the JWT's `sub`, and a human
48
+ * `label` (email/name/preferred_username when the IdP mints one) — BOTH display-only: the
49
+ * operator must be able to read WHICH human signed in, but verification is the bridge/callout's
50
+ * job, server-side. */
51
+ export declare function establishIdpSession(opts: DeviceLoginOpts & {
52
+ dir: string;
53
+ }): Promise<{
54
+ session: IdpSession;
55
+ sub: string;
56
+ label?: string;
57
+ }>;
58
+ /** Revoke the session server-side (sign out). A 401 back means the session is already dead —
59
+ * the goal state, treated as success; any other failure is thrown because a still-live
60
+ * server-side session is a real leak the operator must hear about. */
61
+ export declare function revokeIdpSession(idpUrl: string, sessionToken: string): Promise<void>;
62
+ export declare function loadIdpSession(dir: string, idpUrl: string): IdpSession | undefined;
63
+ export declare function saveIdpSession(dir: string, idpUrl: string, session: IdpSession): void;
64
+ /** Remove the cached session. Returns false when there was nothing to remove. */
65
+ export declare function deleteIdpSession(dir: string, idpUrl: string): boolean;
66
+ /** The no-fallback gate the user-mode connect path consumes: a session or a thrown sentence that
67
+ * says exactly how to get one. There is no anonymous degradation when a space requires a user. */
68
+ export declare function requireIdpSession(dir: string, idpUrl: string): IdpSession;
69
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"AAkCA;;oEAEoE;AACpE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB;qGACiG;IACjG,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,QAAQ,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC/C;AAED;;iDAEiD;AACjD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAsBtD;AAqDD;;;;yEAIyE;AACzE,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQhE;AAcD;;uFAEuF;AACvF,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAwF5E;AAED;;yDAEyD;AACzD,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWvF;AAED;;;;;;wBAMwB;AACxB,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,eAAe,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAoB/D;AAED;;uEAEuE;AACvE,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS1F;AAmCD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAUlF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CASrF;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQrE;AAED;mGACmG;AACnG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAQzE"}