@cotal-ai/workspace 0.35.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.
- package/dist/agent-secrets.d.ts +206 -0
- package/dist/agent-secrets.d.ts.map +1 -0
- package/dist/agent-secrets.js +468 -0
- package/dist/agent-secrets.js.map +1 -0
- package/dist/auth-paths.d.ts +10 -53
- package/dist/auth-paths.d.ts.map +1 -1
- package/dist/auth-paths.js +18 -134
- package/dist/auth-paths.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/renewal.d.ts +22 -15
- package/dist/renewal.d.ts.map +1 -1
- package/dist/renewal.js +27 -21
- package/dist/renewal.js.map +1 -1
- package/dist/space-segmentation.d.ts +252 -0
- package/dist/space-segmentation.d.ts.map +1 -0
- package/dist/space-segmentation.js +421 -0
- package/dist/space-segmentation.js.map +1 -0
- package/dist/system-rotation.d.ts +7 -6
- package/dist/system-rotation.d.ts.map +1 -1
- package/dist/system-rotation.js +25 -10
- package/dist/system-rotation.js.map +1 -1
- package/package.json +2 -2
|
@@ -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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"}
|
package/dist/auth-paths.d.ts
CHANGED
|
@@ -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
|
package/dist/auth-paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-paths.d.ts","sourceRoot":"","sources":["../src/auth-paths.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth-paths.d.ts","sourceRoot":"","sources":["../src/auth-paths.ts"],"names":[],"mappings":"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"}
|