@cotal-ai/workspace 0.34.0 → 0.36.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.
@@ -0,0 +1,468 @@
1
+ /**
2
+ * The per-agent standing secrets, keyed per space — series P1 of
3
+ * `docs/design/space-segmentation-p7-p1.md` (§1's second inventory, §3's second placement).
4
+ *
5
+ * `<name>.creds`, `<name>.actor-token`, `<name>.sentinel.creds`, their per-incarnation
6
+ * `<name>.<lifecycleUid>.*` counterparts and the non-secret `<base>.auth-health.json` all sat
7
+ * directly under `<root>/.cotal/auth/creds`, which is root-scoped while every one of them is
8
+ * per-tenant in meaning. They now live in `<root>/.cotal/auth/creds/space.<hex>/`, key
9
+ * `auth/creds/space.<hex>/<basename>`.
10
+ *
11
+ * WHY THIS IS ITS OWN MODULE and not the block it used to be in `auth-paths.ts`: the surface now
12
+ * depends on the segmentation foundation (`space-segmentation.ts`), and that module already depends
13
+ * on `auth-paths.ts` for the encoder and the account inventory. Keeping the surface here puts it
14
+ * ABOVE both, so the shared choke point is reachable without inverting an existing dependency or
15
+ * teaching `auth-paths.ts` about rules it does not own. Nothing about the material changed in the
16
+ * move except the space.
17
+ *
18
+ * The migration for roots that already exist is §2's MOVE ON FIRST TOUCH, at the one choke point
19
+ * {@link agentCredsDir}, through the same {@link migrateLegacyMaterialIn} the P7 resolvers call.
20
+ * P1's hazard is milder than P7's — an agent secret read as absent is re-provisioned rather than
21
+ * run beside a live sibling — but it is the same shape and ends in the same place: a canonical
22
+ * mint beside legacy material nothing will ever reap, which is the residue this series exists to
23
+ * end.
24
+ */
25
+ import { existsSync, readdirSync, rmSync } from "node:fs";
26
+ import { basename, dirname, join } from "node:path";
27
+ import { assertLifecycleToken } from "@cotal-ai/core";
28
+ import { authDir, spaceFromSegment, spaceSegment } from "./auth-paths.js";
29
+ import { migrateLegacyMaterialIn, spaceMaterialMigrationRefusal, } from "./space-segmentation.js";
30
+ /** `<root>/.cotal/auth/creds` — the PARENT every space's agent-secret segment sits in, and the
31
+ * directory the pre-P1 layout wrote its files directly into.
32
+ *
33
+ * Resolves nothing and migrates nothing, so it is what the two callers that must NOT move material
34
+ * ask for: {@link agentSecretKeysUnder}, which is a DELETER (§3.1, the same reason `clean`'s sweep
35
+ * addresses `segmentedKey`), and {@link agentCredsDir} itself, which needs the parent to enumerate
36
+ * before it can migrate. Every other caller wants {@link agentCredsDir}.
37
+ *
38
+ * It keeps the name `creds`, which is load-bearing beyond this file: it is the one sibling of the
39
+ * auth dir that `migrateLegacyUserAuthState` excludes BY NAME and that `userAuthSpacesOnDisk` skips,
40
+ * so a space's segment landing inside it (rather than beside it, under `auth/`) leaves both of those
41
+ * statements true as written. That is §3's reason for this placement. */
42
+ export function agentCredsRoot(root) {
43
+ return join(authDir(root), "creds");
44
+ }
45
+ /** THE per-agent file segment — the single guarded encoder under every agent-secret key AND path
46
+ * (the {@link spaceSegment} posture: one encoder, guarded before any key or path exists). The
47
+ * alphabet is the manager's spawn-name discipline (`manager.nameError`); the CLI's `--name`
48
+ * override historically had no such guard, so a path-hostile name is refused HERE, before it can
49
+ * address a key or file outside the creds dir. */
50
+ export function agentSecretSegment(name) {
51
+ if (!/^[A-Za-z0-9_-]+$/.test(name))
52
+ throw new Error(`unsafe agent name ${JSON.stringify(name)} for secret material (allowed: letters, digits, _ -)`);
53
+ return name;
54
+ }
55
+ // One filename source per kind — the store key and the materialized path project the SAME name,
56
+ // so the two can never drift apart (the `delivery.creds` lesson, per kind).
57
+ const agentFile = {
58
+ creds: (base) => `${base}.creds`,
59
+ actorToken: (base) => `${base}.actor-token`,
60
+ sentinelCreds: (base) => `${base}.sentinel.creds`,
61
+ health: (base) => `${base}.auth-health.json`,
62
+ };
63
+ /** The per-INCARNATION filename base `<name>.<lifecycleUid>` (SPEC 13.1 naming discipline brought
64
+ * to the FS): an endpoint-provisioned incarnation's secret family embeds its lifecycle UID, so a
65
+ * stale or replayed teardown for a retired incarnation addresses names a same-alias successor
66
+ * never uses. The separator is a `.` — a character {@link agentSecretSegment} REFUSES in a
67
+ * standing name — so the two families are STRUCTURALLY disjoint: no legal standing alias can
68
+ * spell an incarnation base (`worker-<uid>` the standing name maps to `worker-<uid>.creds`;
69
+ * `worker` at that uid maps to `worker.<uid>.creds`). Disjointness is grammar, not uid entropy —
70
+ * a standing alias is chosen, not sampled, so entropy alone guarantees nothing. Standing
71
+ * OPERATOR secrets (`cotal mint`, setup-seeded creds) stay on the name-keyed builders below —
72
+ * they have no lifecycle. */
73
+ function agentIncarnationBase(name, lifecycleUid) {
74
+ return `${agentSecretSegment(name)}.${assertLifecycleToken(lifecycleUid)}`;
75
+ }
76
+ /** The exact filename-base grammar the two builder families can produce — a standing name
77
+ * ({@link agentSecretSegment}'s alphabet, no `.`) or an incarnation base `<name>.<uid>`
78
+ * ({@link agentIncarnationBase}). Anything else is a stray no valid provisioning wrote. */
79
+ const PROVISIONABLE_BASE = /^[A-Za-z0-9_-]+(\.[a-z0-9]{26,32})?$/;
80
+ /** The three SECRET suffixes, longest first: `<name>.sentinel.creds` must never parse as
81
+ * `<name>.sentinel` + `.creds`. Order is the contract, not a convenience. */
82
+ const SECRET_SUFFIXES = [".sentinel.creds", ".actor-token", ".creds"];
83
+ /** The suffixes a MIGRATION moves — the three secret kinds plus the non-secret health file.
84
+ *
85
+ * The health file is here and NOT in {@link SECRET_SUFFIXES} because the two questions differ. It
86
+ * holds no secret, so it never becomes a store key; but the manager records its path beside the
87
+ * family's (`manager.ts` weighs all of actor-token, sentinel and health as ONE manager-owned
88
+ * family), so a migration that moved the three secrets and left the health file behind would split
89
+ * a family across two layouts and fail exactly that check. */
90
+ const MIGRATABLE_SUFFIXES = [...SECRET_SUFFIXES, ".auth-health.json"];
91
+ /** The base of a filename ending in one of `suffixes`, when the base is one a valid provisioning
92
+ * could have written. `undefined` for a stray — a health file asked about a secret suffix, a name
93
+ * the encoder would refuse, a leftover of some other tool. */
94
+ function provisionableBase(file, suffixes) {
95
+ for (const suffix of suffixes) {
96
+ if (!file.endsWith(suffix))
97
+ continue;
98
+ const base = file.slice(0, -suffix.length);
99
+ // The longest matching suffix decides; a rejected base is a stray either way, so do not fall
100
+ // through and let a shorter suffix re-parse the same name into a different family.
101
+ return PROVISIONABLE_BASE.test(base) ? base : undefined;
102
+ }
103
+ return undefined;
104
+ }
105
+ /** The pre-P1 files sitting DIRECTLY in the creds dir — the set {@link agentCredsDir} migrates.
106
+ *
107
+ * Two filters, and they exclude different things. The suffix check is what keeps a canonical
108
+ * `space.<hex>` segment (and any sibling tenant's) out of the set — no segment name can end in a
109
+ * migratable suffix — because moving one tenant's segment into another's would be the aliasing this
110
+ * layout was designed to make impossible. The `isDirectory` check is what stops a DIRECTORY whose
111
+ * name does end in one (`ghost.creds/`) from handing `renameSync` a whole tree: a directory is never
112
+ * material whatever it is called. */
113
+ function legacyAgentSecretFiles(parent) {
114
+ let entries;
115
+ try {
116
+ entries = readdirSync(parent, { withFileTypes: true });
117
+ }
118
+ catch (e) {
119
+ if (e.code === "ENOENT")
120
+ return []; // no creds dir → nothing minted
121
+ throw e;
122
+ }
123
+ return entries
124
+ .filter((e) => !e.isDirectory() && provisionableBase(e.name, MIGRATABLE_SUFFIXES) !== undefined)
125
+ .map((e) => e.name);
126
+ }
127
+ /**
128
+ * THE CHOKE POINT (§2 rule 1): `<root>/.cotal/auth/creds/space.<hex>` — this space's agent-secret
129
+ * dir, having moved any pre-P1 root-scoped files into it first.
130
+ *
131
+ * Every path and key below resolves through here, which is what makes "migrate on first touch"
132
+ * reach every flow rather than the ones someone remembered to update. A caller that builds
133
+ * `join(agentCredsRoot(root), …)` itself has reintroduced the read-fallback §2 rejects.
134
+ *
135
+ * THE MIGRATION IS PER FILE, and that is rule 2 met rather than dodged. P7 moves a fixed set of five
136
+ * kinds; this set is OPEN — one file per agent per kind, discovered by enumeration — so "the move is
137
+ * one `renameSync`" holds per FILE here instead of per kind. Each file is therefore wholly legacy or
138
+ * wholly canonical across a crash, and a crash midway through the set leaves a mixture that the next
139
+ * first touch simply finishes. That is §2.1's cause one, self-healing, and it needs no repair verb.
140
+ *
141
+ * Rules 3 and 4 come from the shared choke point unchanged, which is the point of calling it: rule 4
142
+ * is re-asked per file rather than once for the set, so the cost is one tenant-count read per file
143
+ * MOVED. That is bounded by the one-time migration of a root — after it, the set is empty and the
144
+ * only cost is the enumeration — and it buys a single implementation of the rule instead of a
145
+ * sweep-shaped copy of it.
146
+ */
147
+ export function agentCredsDir(root, space) {
148
+ const parent = agentCredsRoot(root);
149
+ const canonical = join(parent, spaceSegment(space));
150
+ // Cheap gate first, as the prior art does it: a root with no creds dir has nothing to weigh, and
151
+ // one whose creds dir holds no legacy file (every root created after this series) skips straight
152
+ // to the canonical answer without a tenant-count read.
153
+ for (const file of legacyAgentSecretFiles(parent))
154
+ migrateLegacyMaterialIn(parent, root, space, file);
155
+ return canonical;
156
+ }
157
+ /** The segmented store-key prefix for `space`, resolving the migration on the FS composition first.
158
+ *
159
+ * The composition is the P7 union used unchanged ({@link SpaceMaterialComposition}), for the reason
160
+ * its own comment gives: an optional root would let a workstation caller silently take the hosted
161
+ * answer and write a canonical key beside legacy material — which is not merely a missed migration
162
+ * but a §2 rule 3 stalemate, since the next first touch then finds BOTH locations populated and
163
+ * refuses. Requiring the root on that arm makes the unsound call impossible to express. */
164
+ function agentSecretKeyPrefix(space, composition) {
165
+ if (!composition.injected)
166
+ agentCredsDir(composition.root, space);
167
+ return `auth/creds/${spaceSegment(space)}`;
168
+ }
169
+ /** Canonical {@link SecretStore} keys of the per-agent standing secrets, mirroring the
170
+ * `.cotal/auth/creds/space.<hex>/<name>.<kind>` layout byte-for-byte under the workspace FS
171
+ * composition. `<name>.creds` is the static-auth scoped cred; the actor token + sentinel cred are
172
+ * the user-mode pair a spawn mints. The transient `<name>.auth-health.json` is runtime state, NOT a
173
+ * secret kind — it stays plain-file (and moves with its family, see {@link MIGRATABLE_SUFFIXES}). */
174
+ export const agentCredsKey = (space, name, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.creds(agentSecretSegment(name))}`;
175
+ export const agentActorTokenKey = (space, name, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.actorToken(agentSecretSegment(name))}`;
176
+ export const agentSentinelCredsKey = (space, name, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.sentinelCreds(agentSecretSegment(name))}`;
177
+ /** Lifecycle-keyed {@link SecretStore} keys of one INCARNATION's secret family — the
178
+ * per-incarnation counterparts of the standing name-keyed keys above, for any endpoint that
179
+ * provisions managed lifecycles (the manager is the first client; delivery and future endpoints
180
+ * ride the same seam). See {@link agentIncarnationBase} for why the uid is embedded. */
181
+ export const agentLifecycleCredsKey = (space, name, lifecycleUid, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.creds(agentIncarnationBase(name, lifecycleUid))}`;
182
+ export const agentLifecycleActorTokenKey = (space, name, lifecycleUid, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.actorToken(agentIncarnationBase(name, lifecycleUid))}`;
183
+ export const agentLifecycleSentinelCredsKey = (space, name, lifecycleUid, composition) => `${agentSecretKeyPrefix(space, composition)}/${agentFile.sentinelCreds(agentIncarnationBase(name, lifecycleUid))}`;
184
+ /** The FS materialization paths of one agent's secret family (plus its non-secret health file) —
185
+ * built from the SAME filename source as the key builders. These are the paths subprocesses read
186
+ * (the bearer re-exec's `--token-file`, a launch's creds handoff), never an alternate source of
187
+ * truth: under the local FS composition each path IS its key's storage location. */
188
+ export function agentSecretFilePaths(root, space, name) {
189
+ return familyPaths(agentCredsDir(root, space), agentSecretSegment(name));
190
+ }
191
+ /** The lifecycle-keyed FS materialization paths of one INCARNATION's secret family (plus its
192
+ * non-secret health file) — same projection rule as {@link agentSecretFilePaths}, built from the
193
+ * {@link agentIncarnationBase} so a retired incarnation's teardown can never address a same-alias
194
+ * successor's files. */
195
+ export function agentLifecycleSecretFilePaths(root, space, name, lifecycleUid) {
196
+ return familyPaths(agentCredsDir(root, space), agentIncarnationBase(name, lifecycleUid));
197
+ }
198
+ /** One base's four paths under an already-resolved dir — the one place the family's shape is
199
+ * spelled, so the standing and lifecycle builders cannot project different families. */
200
+ function familyPaths(dir, base) {
201
+ return {
202
+ creds: join(dir, agentFile.creds(base)),
203
+ actorToken: join(dir, agentFile.actorToken(base)),
204
+ sentinelCreds: join(dir, agentFile.sentinelCreds(base)),
205
+ health: join(dir, agentFile.health(base)),
206
+ };
207
+ }
208
+ /**
209
+ * The store key of a materialized agent-secret FILE — the same projection the builders above apply,
210
+ * for callers that hold a RECORDED path (a resume inventory, a manifest ledger) rather than the
211
+ * (name, uid) coordinates: under mixed generations the recorded path is the truth, and its key must
212
+ * be derived from the SAME filename, never re-derived from the name alone (which would silently
213
+ * address a different generation's row).
214
+ *
215
+ * IT TAKES THE SPACE, AND CHECKS IT AGAINST THE PATH — the one signature change in this commit that
216
+ * is not bookkeeping. The segment could be read out of the recorded path instead, and nothing would
217
+ * have to change at a single call site; that is precisely why it must not be. A recorded path is
218
+ * caller-supplied data, and once the segment carries a tenant's identity, deriving it from that data
219
+ * means a record written for tenant A can name tenant B's segment and this function will hand back a
220
+ * key into it — which the manager then reads, overwrites, or DELETES. The space a caller is entitled
221
+ * to comes from its own authority (the manager's `space`, the ledger's), so it is passed in and the
222
+ * path is checked against it. A path outside the space's segment is refused rather than resolved,
223
+ * the same posture as the filename check that was already here.
224
+ */
225
+ export function agentSecretKeyForFile(path, space) {
226
+ const file = basename(path);
227
+ const base = provisionableBase(file, SECRET_SUFFIXES);
228
+ if (base === undefined) {
229
+ if (!SECRET_SUFFIXES.some((s) => file.endsWith(s)))
230
+ throw new Error(`"${file}" is not an agent-secret filename (expected a .creds / .actor-token / .sentinel.creds suffix)`);
231
+ throw new Error(`"${file}" is not a provisionable agent-secret filename (a standing name, or <name>.<lifecycleUid>)`);
232
+ }
233
+ const segment = basename(dirname(path));
234
+ if (segment !== spaceSegment(space))
235
+ throw new Error(`${path} is not in space "${space}"'s agent-secret segment (${spaceSegment(space)}) - ` +
236
+ `${spaceFromSegment(segment) !== undefined ? `it names space "${spaceFromSegment(segment)}"'s` : "its parent directory is not a per-space segment at all"}, ` +
237
+ "and a recorded path may not choose which tenant's material a caller addresses");
238
+ return `auth/creds/${segment}/${file}`;
239
+ }
240
+ /**
241
+ * Enumerate the store keys of every per-agent standing secret currently materialized under this
242
+ * root — the reset/backstop sweep (`clean all`; despawn owns the primary delete). Deliberately
243
+ * filename-driven over the LOCAL creds dir: this surface is the FS composition (a hosted reset rides
244
+ * its own store), and a file only maps to a key if a valid spawn could have written it — health
245
+ * files and strays are left to the caller's raw cleanup.
246
+ *
247
+ * ROOT-WIDE AND MIGRATION-FREE, both deliberate. It takes no space because `clean all` resets the
248
+ * whole root, so it enumerates EVERY tenant's segment; and it is a DELETER (§3.1), so it addresses
249
+ * what is on disk rather than resolving through {@link agentCredsDir} — moving material into the
250
+ * path a sweep is about to delete is work done to undo itself, and a §2 rule 3 or 4 refusal must not
251
+ * fail a reset over material the reset does not care about.
252
+ *
253
+ * It reports BOTH levels: this space's segment (`auth/creds/space.<hex>/<file>`) and any pre-P1 file
254
+ * still sitting flat in the creds dir. Dropping the flat level would be the same defect this series
255
+ * exists to end, reintroduced in the sweeper — a reset that leaves an unmigrated root's agent creds
256
+ * on disk and reports success.
257
+ */
258
+ export function agentSecretKeysUnder(root) {
259
+ const parent = agentCredsRoot(root);
260
+ let entries;
261
+ try {
262
+ entries = readdirSync(parent, { withFileTypes: true });
263
+ }
264
+ catch (e) {
265
+ if (e.code === "ENOENT")
266
+ return []; // no creds dir → nothing minted
267
+ throw e;
268
+ }
269
+ const keys = [];
270
+ for (const e of entries) {
271
+ if (!e.isDirectory()) {
272
+ if (provisionableBase(e.name, SECRET_SUFFIXES) !== undefined)
273
+ keys.push(`auth/creds/${e.name}`);
274
+ continue;
275
+ }
276
+ // Only a canonical segment is descended into. A stray subdirectory is not a tenant's material
277
+ // and its contents are nothing this sweep can name a key for.
278
+ if (spaceFromSegment(e.name) === undefined)
279
+ continue;
280
+ keys.push(...segmentSecretKeys(parent, e.name));
281
+ }
282
+ return keys;
283
+ }
284
+ /** The store keys of the secrets materialized in ONE segment — the file→key projection both the
285
+ * root-wide sweep above and the per-space reap below run, named once so a reap can never disagree
286
+ * with the sweep about which files in a segment are addressable material. */
287
+ function segmentSecretKeys(parent, segment) {
288
+ let files;
289
+ try {
290
+ files = readdirSync(join(parent, segment));
291
+ }
292
+ catch (e) {
293
+ if (e.code === "ENOENT")
294
+ return []; // no segment → nothing minted here
295
+ throw e;
296
+ }
297
+ const keys = [];
298
+ for (const file of files)
299
+ if (provisionableBase(file, SECRET_SUFFIXES) !== undefined)
300
+ keys.push(`auth/creds/${segment}/${file}`);
301
+ return keys;
302
+ }
303
+ /**
304
+ * {@link agentSecretKeysUnder} narrowed to ONE tenant — what `cotal space rm` reaps, and what it can
305
+ * list before it commits to reaping.
306
+ *
307
+ * IT DOES NOT REPORT THE FLAT LEVEL, and that is the whole difference between the two. `clean all`
308
+ * resets the root, so it must name a pre-P1 file sitting directly in the creds dir or leave it
309
+ * behind. A per-space reap must NOT: a flat file names no tenant, so attributing it to the tenant
310
+ * being removed is a guess, and acting on the guess deletes what may be a survivor's live material.
311
+ * That case is refused up front by {@link assertAgentSecretsReapable} rather than resolved here.
312
+ *
313
+ * Migration-free for the same reason as the root-wide sweep: this is a DELETER (§3.1).
314
+ */
315
+ export function agentSecretKeysForSpace(root, space) {
316
+ return segmentSecretKeys(agentCredsRoot(root), spaceSegment(space));
317
+ }
318
+ /** The pre-P1 agent-secret files still sitting flat in this root's creds dir — what
319
+ * {@link assertAgentSecretsReapable} refuses over.
320
+ *
321
+ * It is {@link legacyAgentSecretFiles} at the one parent that matters, exported rather than
322
+ * re-derived so the door and the MIGRATION weigh the same set by construction: the files this
323
+ * refuses over are exactly the files the next {@link agentCredsDir} touch would move, which is the
324
+ * claim the refusal's text makes. Two implementations of "what counts as legacy" would drift at
325
+ * whichever one is not the one someone edits. */
326
+ export function unsegmentedAgentSecrets(root) {
327
+ return legacyAgentSecretFiles(agentCredsRoot(root));
328
+ }
329
+ /**
330
+ * The "what do I do about it" half of the refusal below — ASKED of the shared rule
331
+ * ({@link spaceMaterialMigrationRefusal}), never assumed, for the reason P7's `migrationRemedy`
332
+ * records: advice composed from a guess about who is asking hands the operator a command that
333
+ * cannot succeed.
334
+ *
335
+ * It is P1's OWN remedy rather than a call to P7's, because the two series migrate on different
336
+ * verbs and P7's sentence names the wrong one. `cotal up` moves the five P7 kinds; it never touches
337
+ * a P1 resolver (the call sites are `spawn`, `mint`, `doctor auth`, the manager and the manifest
338
+ * ledger), so telling an operator to run `up` and retry would leave these files exactly where they
339
+ * are. `doctor auth` is named because it is the read-only one: it resolves this space's segment
340
+ * through {@link agentCredsDir} on every run (`doctor.ts:169`), so it migrates without minting.
341
+ */
342
+ function agentSecretMigrationRemedy(root) {
343
+ const refusal = spaceMaterialMigrationRefusal(root);
344
+ if (!refusal)
345
+ return "Run `cotal doctor auth` for the sole tenant once to move them into its segment, then retry.";
346
+ // The multi-tenant branch, which is the one `space rm` actually reaches (§2.2 step 2 refuses the
347
+ // last tenant, so this precondition is only ever asked on a root holding more than one). There is
348
+ // no verb to offer: every P1 resolver hits the same rule 4 refusal, and no command can attribute a
349
+ // file that names no owner. So the remedy is the manual move, stated as one.
350
+ return (`Migrating them first is not available on this root either: ${refusal.reason}. ` +
351
+ "Decide which tenant each file belongs to and move it into that tenant's segment " +
352
+ "(auth/creds/space.<hex>/), or remove it, then retry.");
353
+ }
354
+ /**
355
+ * `cotal space rm` STEP 1'S PRECONDITION for the step 7 agent-secret reap
356
+ * (`per-space-lifecycle.md` §2.2) — {@link assertSpaceMaterialReapable}'s P1 counterpart, asked at
357
+ * step 1 for the same reason: step 5 is the point of no return, so a precondition discovered at step
358
+ * 7 would refuse after the tenant's data is already gone, leave the journal entry that gates every
359
+ * other verb standing, and fail identically on every re-run of the removal it is supposed to let a
360
+ * crash finish.
361
+ *
362
+ * WHAT IT REFUSES: a root still holding pre-P1 agent secrets flat in `auth/creds`. P7's counterpart
363
+ * refuses over the same unattributability — the files name no tenant, so reaping around them strands
364
+ * what may be the departing tenant's and reaping them may take a survivor's — and that argument
365
+ * applies here unchanged.
366
+ *
367
+ * BUT P1'S CASE IS SHARPER, and this is the part that is not a translation of P7's. Those flat files
368
+ * are inert TODAY only because §2 rule 4 refuses to migrate on a multi-tenant root. Step 7 deletes
369
+ * the departing tenant's account record; on a two-tenant root that leaves ONE space in the inventory,
370
+ * which is exactly the condition under which rule 4 stops refusing. So the next `cotal spawn`, `mint`
371
+ * or `doctor auth` by the survivor resolves {@link agentCredsDir} and MOVES the departed tenant's
372
+ * secrets into the survivor's segment — where the layout now reads as an assertion that they are the
373
+ * survivor's. Removing a tenant is what converts material that is legibly unowned into a confident
374
+ * attribution nobody made, and it does so silently, later, from an unrelated verb. Refusing here is
375
+ * the only place that can be stopped while the evidence still exists.
376
+ *
377
+ * NOT YET CALLED: `cotal space rm` does not exist as a command today (§2.2 designs it; no `space`
378
+ * verb is implemented). This lands with the material it guards so the verb cannot be written without
379
+ * it, the same reason {@link assertSpaceMaterialReapable} landed with P7's reap.
380
+ */
381
+ export function assertAgentSecretsReapable(root, space, operation) {
382
+ const present = unsegmentedAgentSecrets(root);
383
+ if (present.length === 0)
384
+ return;
385
+ // The set is OPEN (one file per agent per kind), unlike P7's five named kinds, so an unmigrated
386
+ // root can hold a great many. Name enough to recognize the material and count the rest.
387
+ const shown = present.slice(0, 4);
388
+ const listed = shown.join(", ") + (present.length > shown.length ? `, +${present.length - shown.length} more` : "");
389
+ throw new Error(`${operation} refuses: this root holds ${present.length} pre-P1 agent secret file(s) directly in auth/creds (${listed}), which name no space. ` +
390
+ `Removing "${space}" would leave them for a surviving tenant to inherit, and on a two-tenant root it makes that inheritance automatic: ` +
391
+ "the removal leaves one space in the inventory, which is the condition under which the migration rules stop refusing, " +
392
+ "so the survivor's next agent-secret touch moves them into the survivor's segment and records an owner nobody chose. " +
393
+ agentSecretMigrationRemedy(root));
394
+ }
395
+ /**
396
+ * `cotal space rm` STEP 7 (`per-space-lifecycle.md` §2.2): reap ONE tenant's per-agent standing
397
+ * secrets — the residue that step used to LIST and leave behind, because before this series the
398
+ * creds dir named no tenant and reaping one space's would have risked a sibling's.
399
+ *
400
+ * IT CANNOT REFUSE, the same contract {@link reapSpaceMaterial} carries and for the same reason: it
401
+ * runs past step 5's point of no return, where a throw strands the journal entry and recurs
402
+ * identically on every re-run. Seam failures are returned, not thrown, and the caller must read
403
+ * `failed` or the material silently survives the tenant. Its precondition is
404
+ * {@link assertAgentSecretsReapable}, asked at step 1.
405
+ *
406
+ * THE ENUMERATION IS ALSO GUARDED, which P7's reap does not need. P7 sweeps a fixed array of four
407
+ * kinds; this set is OPEN and discovered by reading the segment, so the reap has an I/O step BEFORE
408
+ * its first seam call, and an unreadable segment there would throw straight out of a function that
409
+ * has promised not to. It is reported like any other failure instead. (The one input that could
410
+ * still throw is the space name, encoded once up front before anything has happened — and it comes
411
+ * from the verb's own inventory read, not from disk.)
412
+ *
413
+ * The seam deletes come FIRST and are addressed by the on-disk key, never through
414
+ * {@link agentCredsDir}: a reaper is a DELETER (§3.1), so it must not migrate material into the path
415
+ * it is about to remove — and on the multi-tenant root this verb runs on, that migration is the very
416
+ * mis-attribution the precondition exists to prevent. The store is asked because under a
417
+ * non-plain-file composition the key is where the material lives; the directory removal after it is
418
+ * what takes the non-key files in the segment (the `.auth-health.json` runtime state, which is never
419
+ * a store key) and anything a stray left behind.
420
+ *
421
+ * It removes only THIS space's segment, never `auth/creds` itself. That parent is the shared one —
422
+ * every tenant's segment is a child of it — so the glob-shaped reach a reader arrives at after
423
+ * seeing a directory removal would take every survivor's live agent secrets and report success.
424
+ *
425
+ * WHAT IT DOES NOT REACH: an INJECTED composition. The reap enumerates from the filesystem because
426
+ * {@link SecretStore} has no list operation and P1's key set is open — there is no fixed array to
427
+ * sweep the way P7 sweeps its four kinds — so a hosted deployment reaps a tenant's agent secrets
428
+ * through its own store's teardown. That is the same boundary {@link agentSecretKeysUnder} already
429
+ * draws for `clean all`, stated here rather than left to be discovered: a `removed` list from a root
430
+ * with no local segment is empty because there was nothing local, not because a store was checked.
431
+ */
432
+ export async function reapAgentSecrets(root, space, secrets) {
433
+ const removed = [];
434
+ const failed = [];
435
+ const segment = spaceSegment(space);
436
+ const dir = join(agentCredsRoot(root), segment);
437
+ let keys = [];
438
+ try {
439
+ keys = agentSecretKeysForSpace(root, space);
440
+ }
441
+ catch (e) {
442
+ failed.push(`${segment}: enumerating this space's agent secrets: ${e instanceof Error ? e.message : String(e)}`);
443
+ }
444
+ for (const key of keys) {
445
+ try {
446
+ // Idempotent on an absent key by the seam's contract, and the `get` keeps the report honest
447
+ // rather than claiming a removal for material that was never there.
448
+ if ((await secrets.get(key)) !== undefined) {
449
+ await secrets.delete(key);
450
+ removed.push(`.cotal/${key}`);
451
+ }
452
+ }
453
+ catch (e) {
454
+ failed.push(`${key}: ${e instanceof Error ? e.message : String(e)}`);
455
+ }
456
+ }
457
+ try {
458
+ if (existsSync(dir)) {
459
+ rmSync(dir, { recursive: true, force: true });
460
+ removed.push(`.cotal/auth/creds/${segment} (this space's agent secrets)`);
461
+ }
462
+ }
463
+ catch (e) {
464
+ failed.push(`${segment}: ${e instanceof Error ? e.message : String(e)}`);
465
+ }
466
+ return { removed, failed };
467
+ }
468
+ //# sourceMappingURL=agent-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-secrets.js","sourceRoot":"","sources":["../src/agent-secrets.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,16 @@ export declare function spaceKey(space: string): string;
13
13
  * dir (`broker.json`, `account.<hex>.json`, `server.conf`, `creds/`): none of those start with
14
14
  * `space.`, and the hex body cannot smuggle one in. Consumed by the state dir AND the auth
15
15
  * secret-store key builders; two independently-guarded encoders were the defect generator (one
16
- * gains a rule the other doesn't), so keep exactly one. */
16
+ * gains a rule the other doesn't), so keep exactly one.
17
+ *
18
+ * The non-collision claim now spans a WIDER namespace than the auth dir. Segmenting the root-scoped
19
+ * P7 material puts a `space.<hex>` directly under `.cotal/`, so the guarantee must hold against the
20
+ * children of `.cotal/` too (`agents`, `auth`, `nats`, `manifests`, the `*.creds`/`*.pid`/`*.log`
21
+ * files, and `auth-service.<spaceKey>.pid`, which is the nearest miss). It does, for the same
22
+ * reason: no `.cotal` child begins with `space.`. That held by ACCIDENT until something asserted
23
+ * it — `RESERVED_COTAL_CHILDREN` in `space-segmentation.ts` carries the list and
24
+ * `smoke:space-segmentation` is the guard, so a future `.cotal` child named `space.*` fails a suite
25
+ * instead of silently aliasing a tenant's segment. */
17
26
  export declare function spaceSegment(space: string): string;
18
27
  /** The space a canonical segment encodes, or undefined when the name is not one {@link spaceSegment}
19
28
  * wrote (wrong prefix, non-hex body, or a body that does not round-trip). Enumeration and the
@@ -45,58 +54,6 @@ export declare function hasUserAuthState(root: string, space: string): boolean;
45
54
  * legacy dirs are reported too (decoded from their verbatim names), so a guard reading this stays
46
55
  * fail-closed before the one-time migration has run. */
47
56
  export declare function userAuthSpacesOnDisk(dir: string): string[];
48
- /** The dir every per-agent standing secret materializes under (`<root>/.cotal/auth/creds`) —
49
- * space-INDEPENDENT today (one root serves one space); multi-space-per-root re-keys this layout
50
- * as a caller change, same as {@link userAuthStateDir}'s note. */
51
- export declare function agentCredsDir(root: string): string;
52
- /** THE per-agent file segment — the single guarded encoder under every agent-secret key AND path
53
- * (the {@link spaceSegment} posture: one encoder, guarded before any key or path exists). The
54
- * alphabet is the manager's spawn-name discipline (`manager.nameError`); the CLI's `--name`
55
- * override historically had no such guard, so a path-hostile name is refused HERE, before it can
56
- * address a key or file outside the creds dir. */
57
- export declare function agentSecretSegment(name: string): string;
58
- /** Canonical {@link SecretStore} keys of the per-agent standing secrets, mirroring today's
59
- * `.cotal/auth/creds/<name>.<kind>` layout byte-for-byte under the workspace FS composition.
60
- * `<name>.creds` is the static-auth scoped cred; the actor token + sentinel cred are the
61
- * user-mode pair a spawn mints. The transient `<name>.auth-health.json` is runtime state, NOT a
62
- * secret kind — it stays plain-file. */
63
- export declare const agentCredsKey: (name: string) => string;
64
- export declare const agentActorTokenKey: (name: string) => string;
65
- export declare const agentSentinelCredsKey: (name: string) => string;
66
- /** Lifecycle-keyed {@link SecretStore} keys of one INCARNATION's secret family — the
67
- * per-incarnation counterparts of the standing name-keyed keys above, for any endpoint that
68
- * provisions managed lifecycles (the manager is the first client; delivery and future endpoints
69
- * ride the same seam). See {@link agentIncarnationBase} for why the uid is embedded. */
70
- export declare const agentLifecycleCredsKey: (name: string, lifecycleUid: string) => string;
71
- export declare const agentLifecycleActorTokenKey: (name: string, lifecycleUid: string) => string;
72
- export declare const agentLifecycleSentinelCredsKey: (name: string, lifecycleUid: string) => string;
73
- /** The FS materialization paths of one agent's secret family (plus its non-secret health file) —
74
- * built from the SAME filename source as the key builders. These are the paths subprocesses read
75
- * (the bearer re-exec's `--token-file`, a launch's creds handoff), never an alternate source of
76
- * truth: under the local FS composition each path IS its key's storage location. */
77
- export declare function agentSecretFilePaths(root: string, name: string): {
78
- creds: string;
79
- actorToken: string;
80
- sentinelCreds: string;
81
- health: string;
82
- };
83
- /** The lifecycle-keyed FS materialization paths of one INCARNATION's secret family (plus its
84
- * non-secret health file) — same projection rule as {@link agentSecretFilePaths}, built from the
85
- * {@link agentIncarnationBase} so a retired incarnation's teardown can never address a same-alias
86
- * successor's files. */
87
- export declare function agentLifecycleSecretFilePaths(root: string, name: string, lifecycleUid: string): {
88
- creds: string;
89
- actorToken: string;
90
- sentinelCreds: string;
91
- health: string;
92
- };
93
- export declare function agentSecretKeyForFile(path: string): string;
94
- /** Enumerate the store keys of every per-agent standing secret currently materialized under this
95
- * root — the reset/backstop sweep (`clean all`; despawn owns the primary delete). Deliberately
96
- * filename-driven over the LOCAL creds dir: this surface is the FS composition (a hosted reset
97
- * rides its own store), and a file only maps to a key if a valid spawn could have written it —
98
- * health files and strays are left to the caller's raw cleanup. */
99
- export declare function agentSecretKeysUnder(root: string): string[];
100
57
  /** Materialize a store secret into a 0600 file for a process that can only read files — the
101
58
  * agent-bearer re-exec (`--token-file`) and the launch-time creds handoff. The STORE is the
102
59
  * source of truth; the file is a caller-owned projection at an explicit path (under the local FS
@@ -240,6 +197,22 @@ export declare function accountInventory(dir: string): {
240
197
  * names come from each record's authoritative `space`. Callers that must act on the blast radius use
241
198
  * {@link assertSingleSpaceBroker} / {@link soleSpaceOf}, which also refuse on an unreadable record. */
242
199
  export declare function listSpaceAccounts(dir: string): string[];
200
+ /** The accounts a broker config must PRELOAD: every tenant this auth dir holds, with `current` first.
201
+ *
202
+ * The MEMORY resolver is a whole-broker map, so the rendered config IS the tenant list - it does not
203
+ * add the space being booted to whatever the running broker already trusts, it replaces the set.
204
+ * Rendering from the one space a `cotal up` was asked for therefore evicts every sibling silently:
205
+ * the broker starts, that space works, and every cred minted under the others is refused with
206
+ * nothing printed anywhere. A render reads this, never a single space.
207
+ *
208
+ * `current` comes from the CALLER because the caller's copy can be fresher than the disk - a space
209
+ * minted moments ago, or an account whose trust record this same boot just rotated - and rendering
210
+ * the stale on-disk copy of the space being booted is the other half of the same bug.
211
+ *
212
+ * Fail-CLOSED on a corrupt record, like the guards above: a file that occupies the account namespace
213
+ * but will not validate may be a real tenant, and rendering without it is precisely the silent
214
+ * eviction this exists to prevent. */
215
+ export declare function preloadSpaceAccounts(dir: string, current: SpaceAccountAuth): SpaceAccountAuth[];
243
216
  /** THE store key of the broker trust record. Under the workspace store (rooted at `.cotal`) it
244
217
  * resolves byte-for-byte to `.cotal/auth/broker.json`. One source for every signer-bearing tier -
245
218
  * a drifted literal would silently split the kind. */
@@ -1 +1 @@
1
- {"version":3,"file":"auth-paths.d.ts","sourceRoot":"","sources":["../src/auth-paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth-paths.d.ts","sourceRoot":"","sources":["../src/auth-paths.ts"],"names":[],"mappings":"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"}