@cotal-ai/delivery 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.
- package/dist/delivery.d.ts +11 -8
- package/dist/delivery.d.ts.map +1 -1
- package/dist/delivery.js +71 -31
- package/dist/delivery.js.map +1 -1
- package/dist/evict-exec.d.ts +20 -3
- package/dist/evict-exec.d.ts.map +1 -1
- package/dist/evict-exec.js +77 -30
- package/dist/evict-exec.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/membership.d.ts +15 -15
- package/dist/membership.d.ts.map +1 -1
- package/dist/membership.js +66 -59
- package/dist/membership.js.map +1 -1
- package/dist/sys-creds.d.ts +139 -0
- package/dist/sys-creds.d.ts.map +1 -0
- package/dist/sys-creds.js +163 -0
- package/dist/sys-creds.js.map +1 -0
- package/package.json +3 -3
package/dist/delivery.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ParsedArgs, type SecretStore } from "@cotal-ai/core";
|
|
2
|
-
import {
|
|
3
|
-
/** Re-exported for hosted compositions: the
|
|
4
|
-
* {@link
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
2
|
+
import { DELIVERY_CREDS_KIND, deliveryCredsKey } from "@cotal-ai/workspace";
|
|
3
|
+
/** Re-exported for hosted compositions: the daemon cred's KIND, and the builder that turns it into
|
|
4
|
+
* the {@link SecretStore} key for a space. Defined once in workspace (the layout is the workspace's)
|
|
5
|
+
* so the writer, the renewal owner, and this reader can never drift apart. As of P7 the kind is NOT
|
|
6
|
+
* the key — the key is per-space — so a hosted store must be keyed with the builder; the flat kind
|
|
7
|
+
* is the pre-P7 key and putting there leaves this daemon reading an empty location. */
|
|
8
|
+
export { DELIVERY_CREDS_KIND, deliveryCredsKey };
|
|
8
9
|
/**
|
|
9
10
|
* Run the delivery daemon: the server-side Plane-3 durable backstop. A thin composition root that
|
|
10
11
|
* builds a scoped `delivery` endpoint, acquires the single-flight lease, and runs the existing
|
|
@@ -16,8 +17,10 @@ export { DELIVERY_CREDS_KEY };
|
|
|
16
17
|
*
|
|
17
18
|
* `store` is the hosted-composition seam: a closed composition root calls this export directly and
|
|
18
19
|
* injects its own {@link SecretStore} (KMS/Vault…) holding the daemon cred under
|
|
19
|
-
* {@link
|
|
20
|
-
*
|
|
20
|
+
* {@link deliveryCredsKey}`(space, …)` AND the membership feed's rw cred under the matching
|
|
21
|
+
* `membership-rw.creds` key — both PER-SPACE as of P7, so the bare kinds are the pre-P7 spelling and
|
|
22
|
+
* are not read; the registered `deliver` command passes none and gets the workstation FS store (or
|
|
23
|
+
* `--creds`). The
|
|
21
24
|
* renewal owner's write side (`remintDaemonCreds`) is threaded through the manager's
|
|
22
25
|
* `ManagerOptions.secretStore` too, so a hosted composition renews both daemon kinds end-to-end when
|
|
23
26
|
* the manager and this daemon are handed the SAME store.
|
package/dist/delivery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAA0B,gBAAgB,EAAiF,MAAM,qBAAqB,CAAC;AAMnL;;;;wFAIwF;AACxF,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;AA0GjD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAmNtF"}
|
package/dist/delivery.js
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
1
|
import { basename, dirname, join, resolve } from "node:path";
|
|
2
2
|
import { CotalEndpoint, DEFAULT_SERVER, LEASE_TTL_MS, accountFromCreds, credsClaims, idFromCreds, isReachable, mintCreds, newIdentity, } from "@cotal-ai/core";
|
|
3
|
-
import {
|
|
3
|
+
import { DELIVERY_CREDS_KIND, FsSecretStore, authDir, deliveryCredsKey, findCotalRoot, loadSpaceAuth, segmentedKey, soleSpaceOf, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
4
4
|
import { startMembership } from "./membership.js";
|
|
5
5
|
import { executeEviction, executePlaneLiveness, executePrincipalLiveness } from "./evict-exec.js";
|
|
6
|
-
/** Re-exported for hosted compositions: the
|
|
7
|
-
* {@link
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
6
|
+
/** Re-exported for hosted compositions: the daemon cred's KIND, and the builder that turns it into
|
|
7
|
+
* the {@link SecretStore} key for a space. Defined once in workspace (the layout is the workspace's)
|
|
8
|
+
* so the writer, the renewal owner, and this reader can never drift apart. As of P7 the kind is NOT
|
|
9
|
+
* the key — the key is per-space — so a hosted store must be keyed with the builder; the flat kind
|
|
10
|
+
* is the pre-P7 key and putting there leaves this daemon reading an empty location. */
|
|
11
|
+
export { DELIVERY_CREDS_KIND, deliveryCredsKey };
|
|
12
|
+
/**
|
|
13
|
+
* THE ORDERING-CRITICAL HALF of the cred-source decision, split out so it cannot drift below the
|
|
14
|
+
* ambient reads. With an injected store the store is the ONLY credential source: every local-source
|
|
15
|
+
* flag (`--creds`, `--dev-mint`) is rejected loudly HERE, before `runDelivery` derives a space —
|
|
16
|
+
* because that derivation itself reads the local signer under `--dev-mint`. A hosted composition
|
|
17
|
+
* must never cross back into workstation trust material, not even for a space label, and the space
|
|
18
|
+
* is now an INPUT to the key, which is exactly the pressure that would otherwise pull the
|
|
19
|
+
* derivation above this check.
|
|
20
|
+
*/
|
|
21
|
+
function assertNoLocalCredSourceFlags(v, injected) {
|
|
22
|
+
if (!injected)
|
|
23
|
+
return;
|
|
24
|
+
const local = ["creds", "dev-mint"].filter((f) => v[f] !== undefined);
|
|
25
|
+
if (local.length)
|
|
26
|
+
throw new Error(`delivery: ${local.map((f) => `--${f}`).join(" and ")} cannot be combined with an injected secret store — the store is the cred's only source`);
|
|
27
|
+
}
|
|
28
|
+
/** Where the daemon's pre-minted cred lives — exactly ONE source: an injected {@link SecretStore}
|
|
29
|
+
* (a hosted composition), an explicit `--creds <file>` (e.g. a read-only container mount) as an FS
|
|
30
|
+
* store over that exact file, or the default workstation location. `where` is the human label used
|
|
31
|
+
* in error messages so a local operator still sees a path, not an abstract key.
|
|
32
|
+
*
|
|
33
|
+
* Called AFTER {@link assertNoLocalCredSourceFlags} has settled the injected/local conflict, which
|
|
34
|
+
* is what lets it take the derived `space`. The workstation arm goes through the per-kind RESOLVER:
|
|
35
|
+
* it is the reader whose "absent" answer sends `ensureDelivery` off to mint, so reading the
|
|
36
|
+
* canonical location past an unmigrated cred is how a root ends up with two live delivery creds.
|
|
37
|
+
* The injected arm builds the key without touching a filesystem it does not have. `--creds` names
|
|
38
|
+
* ONE file and is per-space by the operator's own choice of path, so it is neither segmented nor
|
|
39
|
+
* migrated — the store there is rooted at that file's own directory. */
|
|
40
|
+
function resolveCredsStore(v, space, injected) {
|
|
20
41
|
if (injected) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
throw new Error(`delivery: ${local.map((f) => `--${f}`).join(" and ")} cannot be combined with an injected secret store — the store is the cred's only source`);
|
|
24
|
-
return { store: injected, key: DELIVERY_CREDS_KEY, where: `secret-store key "${DELIVERY_CREDS_KEY}"`, injected: true };
|
|
42
|
+
const key = segmentedKey(DELIVERY_CREDS_KIND, space);
|
|
43
|
+
return { store: injected, key, where: `secret-store key "${key}"`, injected: true };
|
|
25
44
|
}
|
|
26
45
|
if (v.creds !== undefined) {
|
|
27
46
|
const p = resolve(v.creds);
|
|
28
47
|
return { store: new FsSecretStore(dirname(p)), key: basename(p), where: p, injected: false };
|
|
29
48
|
}
|
|
30
49
|
const root = findCotalRoot();
|
|
31
|
-
|
|
50
|
+
const key = deliveryCredsKey(space, { injected: false, root });
|
|
51
|
+
return { store: workspaceSecretStore(root), key, where: join(root, ".cotal", key), injected: false };
|
|
32
52
|
}
|
|
33
53
|
/** The daemon's scoped `delivery` creds — the PRODUCTION path reads a PRE-MINTED cred through the
|
|
34
54
|
* {@link SecretStore} seam ({@link resolveCredsStore}; locally the CLI's `ensureDelivery` setup helper
|
|
@@ -68,7 +88,7 @@ async function loadDeliveryCreds(src, v) {
|
|
|
68
88
|
};
|
|
69
89
|
}
|
|
70
90
|
if (src.injected)
|
|
71
|
-
throw new Error(`delivery: no cred in the injected secret store under key "${
|
|
91
|
+
throw new Error(`delivery: no cred in the injected secret store under key "${key}" — the hosted composition must put it before starting the daemon (local sources are never consulted when a store is injected). The key is PER-SPACE as of P7; a cred put under the bare kind "${DELIVERY_CREDS_KIND}" is not read here.`);
|
|
72
92
|
if (v["dev-mint"] !== undefined) {
|
|
73
93
|
// Space-blind dev path: the root must name exactly one space (soleSpaceOf fails loud on several).
|
|
74
94
|
const devRoot = authDir(findCotalRoot());
|
|
@@ -95,8 +115,10 @@ async function loadDeliveryCreds(src, v) {
|
|
|
95
115
|
*
|
|
96
116
|
* `store` is the hosted-composition seam: a closed composition root calls this export directly and
|
|
97
117
|
* injects its own {@link SecretStore} (KMS/Vault…) holding the daemon cred under
|
|
98
|
-
* {@link
|
|
99
|
-
*
|
|
118
|
+
* {@link deliveryCredsKey}`(space, …)` AND the membership feed's rw cred under the matching
|
|
119
|
+
* `membership-rw.creds` key — both PER-SPACE as of P7, so the bare kinds are the pre-P7 spelling and
|
|
120
|
+
* are not read; the registered `deliver` command passes none and gets the workstation FS store (or
|
|
121
|
+
* `--creds`). The
|
|
100
122
|
* renewal owner's write side (`remintDaemonCreds`) is threaded through the manager's
|
|
101
123
|
* `ManagerOptions.secretStore` too, so a hosted composition renews both daemon kinds end-to-end when
|
|
102
124
|
* the manager and this daemon are handed the SAME store.
|
|
@@ -108,13 +130,16 @@ export async function runDelivery(args, store) {
|
|
|
108
130
|
if (shards !== 1 || shard !== 0)
|
|
109
131
|
throw new Error(`delivery: sharded operation is not supported (N=1 only; got shard=${shard} shards=${shards}). ` +
|
|
110
132
|
"The partition() seam ships but operating shards>1 needs the channel-prefix grammar — see core-sub-fabric.md.");
|
|
111
|
-
//
|
|
112
|
-
// store
|
|
113
|
-
|
|
133
|
+
// Reject local-source flags FIRST — before the ambient space derivation below — so an injected
|
|
134
|
+
// store can never reach the workstation signer. The cred KEY is per-space as of P7, so resolving
|
|
135
|
+
// the source now needs the space that this check must precede; the check is therefore split out
|
|
136
|
+
// and stays here, ahead of everything ambient.
|
|
137
|
+
assertNoLocalCredSourceFlags(v, store);
|
|
114
138
|
// Space comes from --space (the CLI passes it). Only --dev-mint may derive it from the local signer.
|
|
115
139
|
const space = v.space ?? (v["dev-mint"] !== undefined ? soleSpaceOf(authDir(findCotalRoot())) : undefined);
|
|
116
140
|
if (!space)
|
|
117
141
|
throw new Error("delivery: --space is required (the scoped creds file does not encode it)");
|
|
142
|
+
const credsSrc = resolveCredsStore(v, space, store);
|
|
118
143
|
const server = v.server ?? DEFAULT_SERVER;
|
|
119
144
|
const creds = await loadDeliveryCreds(credsSrc, v); // pre-minted scoped cred; NO signer/loadSpaceAuth in this path
|
|
120
145
|
let latestCreds = creds.initial; // freshest renewal — the broker-reachability poll below presents it
|
|
@@ -133,10 +158,22 @@ export async function runDelivery(args, store) {
|
|
|
133
158
|
// of the WRONG account is indistinguishable from "the principal is gone" — a healthy-looking
|
|
134
159
|
// answer that authorizes eviction and gate reconciliation. Resolving the root per request from
|
|
135
160
|
// `process.cwd()` let that drift; and a daemon started in a foreign mesh root would sweep a
|
|
136
|
-
// foreign tenant while looking entirely well. So: the root is fixed at start, and the
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
161
|
+
// foreign tenant while looking entirely well. So: the root is fixed at start, and the OBSERVER
|
|
162
|
+
// CRED is cross-checked against the account this daemon's OWN credential authenticates as — two
|
|
163
|
+
// facts, neither of which comes from that root, which is what makes it an independent check.
|
|
164
|
+
// The $SYS pair resolves through the SAME injected store, so a hosted composition needs no
|
|
165
|
+
// `.cotal/` file for it. `injected` is this composition root's own fact — recorded here, where it
|
|
166
|
+
// is known for certain, rather than inferred later by probing the store or sniffing the
|
|
167
|
+
// filesystem, both of which report "workstation" for a hosted daemon and would emit a CLI repair
|
|
168
|
+
// the host cannot run. It selects the REPAIR IDIOM only; failure semantics never fork on it.
|
|
169
|
+
const scanRoot = findCotalRoot();
|
|
170
|
+
const scanTarget = {
|
|
171
|
+
root: scanRoot,
|
|
172
|
+
expectedAccount: accountFromCreds(creds.initial),
|
|
173
|
+
source: store === undefined
|
|
174
|
+
? { secrets: workspaceSecretStore(scanRoot), space, injected: false, root: scanRoot }
|
|
175
|
+
: { secrets: store, space, injected: true },
|
|
176
|
+
};
|
|
140
177
|
console.error(`• delivery: $SYS sweeps bound to ${join(scanTarget.root, ".cotal")} (account ${scanTarget.expectedAccount})`);
|
|
141
178
|
const ep = new CotalEndpoint({
|
|
142
179
|
space,
|
|
@@ -218,7 +255,10 @@ export async function runDelivery(args, store) {
|
|
|
218
255
|
// an arbitrary `--creds` path, whereas membership-rw lives under the workstation `.cotal/` key. With
|
|
219
256
|
// no injected store, startMembership falls back to the workstation FS store.
|
|
220
257
|
// Fail-soft, but never fault-FORGETFUL: whichever way the feed fails to come up, keep the reason.
|
|
221
|
-
|
|
258
|
+
// `accountId` is the account THIS daemon's own cred authenticates as (pinned above), not a
|
|
259
|
+
// `.cotal/membership.json` read: the file was never an independent source (same directory as the
|
|
260
|
+
// creds, so a wrong root was wrong for both) and a hosted composition has none.
|
|
261
|
+
({ handle: membership, down: membershipDown } = await startMembership({ space, server, accountId: scanTarget.expectedAccount }, store));
|
|
222
262
|
}
|
|
223
263
|
catch (e) {
|
|
224
264
|
membershipDown = e.message;
|
package/dist/delivery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"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"}
|
package/dist/evict-exec.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type EvictionResult, type PlaneLivenessResult, type PrincipalLivenessResult } from "@cotal-ai/core";
|
|
2
|
+
import { type SysCredsSource } from "./sys-creds.js";
|
|
2
3
|
/**
|
|
3
4
|
* THE SCAN TARGET, resolved fail-closed and BOUND TO THE DAEMON'S OWN TENANCY.
|
|
4
5
|
*
|
|
@@ -12,9 +13,17 @@ import { type EvictionResult, type PlaneLivenessResult, type PrincipalLivenessRe
|
|
|
12
13
|
*
|
|
13
14
|
* Two independent guards, because either alone leaves a real case open:
|
|
14
15
|
* - the root is PINNED AT DAEMON START, so it cannot drift with `process.cwd()` between requests;
|
|
15
|
-
* - the
|
|
16
|
+
* - the OBSERVER CRED is CROSS-CHECKED against the account the daemon's own credential
|
|
16
17
|
* authenticates as. Pinning alone cannot notice a daemon that started in the wrong root to
|
|
17
|
-
* begin with; the tenancy check can, because
|
|
18
|
+
* begin with; the tenancy check can, because neither side of it is read from that root.
|
|
19
|
+
*
|
|
20
|
+
* The second guard used to compare `.cotal/membership.json` against `expectedAccount`. It now
|
|
21
|
+
* compares the OBSERVER'S OWN CONNZ PERMISSION against it (`sys-creds.ts`), which is strictly
|
|
22
|
+
* stronger: the file sat in the same directory as the creds, so a wrong root was wrong for both, and
|
|
23
|
+
* its independence came from `expectedAccount` being derived from the cred anyway — the cred was
|
|
24
|
+
* always the real authority and the file was the thing being checked. The disk file is still read as
|
|
25
|
+
* a SECOND source on the workstation path, where the root genuinely can drift; it is no longer
|
|
26
|
+
* required, and a hosted composition has none.
|
|
18
27
|
*
|
|
19
28
|
* A mismatch REFUSES LOUD and names both sides. Note the asymmetry that makes this necessary: an
|
|
20
29
|
* observer scoped to account A with accountId B under-reports (broker-denied → `unknown`, safe),
|
|
@@ -26,6 +35,9 @@ export interface ScanTarget {
|
|
|
26
35
|
root: string;
|
|
27
36
|
/** The account the daemon's own delivery credential authenticates as. */
|
|
28
37
|
expectedAccount: string;
|
|
38
|
+
/** Where the $SYS pair is read from, and whether that store was injected (hosted) — the latter
|
|
39
|
+
* selects the repair idiom only, never the failure semantics. */
|
|
40
|
+
source: SysCredsSource;
|
|
29
41
|
}
|
|
30
42
|
/**
|
|
31
43
|
* The delivery daemon's LIVE-EVICTION executor (D5 slice 6, on the privileged delivery-admin rail):
|
|
@@ -44,13 +56,18 @@ export declare function executeEviction(server: string, target: ScanTarget, prin
|
|
|
44
56
|
* connection tuples; anything else refuses loudly. A space provisioned before the observer existed
|
|
45
57
|
* refuses with the regeneration step — the auth plane treats that refusal as UNKNOWN and never
|
|
46
58
|
* reclaims over it (fail-closed).
|
|
59
|
+
*
|
|
60
|
+
* Asks for the observer ONLY, so the KICK cred is not even read into this process's memory on a
|
|
61
|
+
* read-only path. The cost is that the torn-rotation check cannot run here (it needs both halves);
|
|
62
|
+
* that is safe rather than a gap — a torn pair whose observer is the stale half is refused by the
|
|
63
|
+
* broker, and this path reads any refusal as UNKNOWN, which is already fail-closed.
|
|
47
64
|
*/
|
|
48
65
|
export declare function executePlaneLiveness(server: string, target: ScanTarget, query: unknown): Promise<PlaneLivenessResult>;
|
|
49
66
|
/**
|
|
50
67
|
* The delivery daemon's FREEZE-HOLDER LIVENESS probe (#391, on the privileged delivery-admin rail):
|
|
51
68
|
* answer whether ONE principal still holds a live connection, via the $SYS CONNZ observer cred
|
|
52
69
|
* (opened per call; READ-ONLY — the KICK evictor cred never enters this path, and is not even read
|
|
53
|
-
* from
|
|
70
|
+
* from the store here).
|
|
54
71
|
*
|
|
55
72
|
* This is the READ half of {@link executeEviction}, and it exists because the write half cannot
|
|
56
73
|
* serve as its own precheck: a repair that must REFUSE while the holder is alive would, using
|
package/dist/evict-exec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evict-exec.d.ts","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"evict-exec.d.ts","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAA0E,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG7H;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,eAAe,EAAE,MAAM,CAAC;IACxB;sEACkE;IAClE,MAAM,EAAE,cAAc,CAAC;CACxB;AAqED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmBpH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAc3H;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAmBvI"}
|
package/dist/evict-exec.js
CHANGED
|
@@ -1,23 +1,72 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { evictDeniedPrincipalWithCreds, isPlaneConnTuple, isPrincipalOwnerToken, observePlaneLivenessWithCreds, observePrincipalLivenessWithCreds, parsePrincipalKey, } from "@cotal-ai/core";
|
|
4
|
-
import { findCotalRoot } from "@cotal-ai/workspace";
|
|
4
|
+
import { CONNECTION_EVICTOR_CREDS_KIND, findCotalRoot, membershipConfigPath, MEMBERSHIP_OBSERVER_CREDS_KIND } from "@cotal-ai/workspace";
|
|
5
|
+
import { loadSysPair, observerTenancyProblem, repairAdvice, tornRotationProblem } from "./sys-creds.js";
|
|
6
|
+
/**
|
|
7
|
+
* Pin check + the WORKSTATION-ONLY second source.
|
|
8
|
+
*
|
|
9
|
+
* The account to sweep is `target.expectedAccount` — the daemon's own credential, pinned at start.
|
|
10
|
+
* `membership.json` is no longer consulted for it. On the workstation path the file is still
|
|
11
|
+
* cross-checked WHEN PRESENT, because there a wrong root is a real, enumerated failure mode
|
|
12
|
+
* (see the cases above) and a second independent-ish source costs nothing. It is no longer
|
|
13
|
+
* REQUIRED: its absence is not an error, and a hosted composition never has one.
|
|
14
|
+
*/
|
|
5
15
|
function resolveScan(target, verb) {
|
|
6
16
|
const live = findCotalRoot();
|
|
7
17
|
if (live !== target.root)
|
|
8
18
|
throw new Error(`${verb}: the mesh root resolved at this request (${live}) is not the one this daemon started in (${target.root}); ` +
|
|
9
19
|
"the scan account would be read from a different workspace than the one this daemon serves. Refusing — a sweep of the wrong account looks exactly like a dead principal");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if (!target.source.injected) {
|
|
21
|
+
// Through the kind's resolver (P7 §2 rule 1), not a hand-composed `.cotal/membership.json`:
|
|
22
|
+
// the file is per-space now, and reading the canonical location past an unmigrated copy would
|
|
23
|
+
// silently drop this second source — turning a wrong-root refusal into a confident sweep.
|
|
24
|
+
// `target.source.space` is the space the daemon started in, pinned with the rest of the source.
|
|
25
|
+
const cfgPath = membershipConfigPath(target.root, target.source.space);
|
|
26
|
+
if (existsSync(cfgPath)) {
|
|
27
|
+
// A malformed file on a path that no longer needs it must not take eviction down; only a file
|
|
28
|
+
// that names a DIFFERENT account is evidence of the drift this check exists to catch.
|
|
29
|
+
let onDisk;
|
|
30
|
+
try {
|
|
31
|
+
onDisk = JSON.parse(readFileSync(cfgPath, "utf8")).accountId;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
onDisk = undefined;
|
|
35
|
+
}
|
|
36
|
+
if (onDisk !== undefined && onDisk !== target.expectedAccount)
|
|
37
|
+
throw new Error(`${verb}: ${cfgPath} names account ${onDisk}, but this daemon's own credential authenticates as ${target.expectedAccount}. ` +
|
|
38
|
+
"That disk material belongs to a different mesh, and sweeping it would return a confident, WRONG answer (a complete sweep of the wrong account is indistinguishable from a gone principal). Refusing — start the daemon from the workspace root whose account it serves");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { accountId: target.expectedAccount };
|
|
42
|
+
}
|
|
43
|
+
/** Read the $SYS material for one verb and run every pre-connect check, or THROW.
|
|
44
|
+
*
|
|
45
|
+
* Fail-loud is the whole posture here (design §4.2): a missing key is a REFUSAL, full stop. In
|
|
46
|
+
* particular a missing evictor must NOT silently fall back to deny-new-only inside the executor —
|
|
47
|
+
* that posture is the CALLER's documented degradation, reached by handling this refusal, never
|
|
48
|
+
* something the executor may choose on its own. And absence (`undefined` per the `SecretStore`
|
|
49
|
+
* contract) is the only thing treated as "not provisioned": a `get()` that throws is a refusal,
|
|
50
|
+
* not an absence, and propagates untouched from `loadSysPair`. */
|
|
51
|
+
async function loadCheckedSys(target, verb, need) {
|
|
52
|
+
const sys = await loadSysPair(target.source, need);
|
|
53
|
+
if (sys.missing.length)
|
|
54
|
+
throw new Error(`${verb}: the $SYS ${need === "both" ? "observer/evictor creds are" : "observer creds are"} not provisioned here ` +
|
|
55
|
+
`(missing ${sys.missing.join(", ")}; a space created before live eviction) - ${repairAdvice(target.source, sys.missing)}. ` +
|
|
56
|
+
"Until then removal is deny-new-only (durable reauth)");
|
|
57
|
+
const observer = sys.observer;
|
|
58
|
+
const tenancy = observerTenancyProblem(observer, target.expectedAccount);
|
|
59
|
+
if (tenancy)
|
|
60
|
+
throw new Error(`${verb}: ${tenancy}. Refusing`);
|
|
61
|
+
if (sys.evictor !== undefined) {
|
|
62
|
+
// The torn-rotation check now covers this path too. It used to exist only in the feed, so
|
|
63
|
+
// eviction could open a half-rotated pair and get a bare "Authorization Violation" from the
|
|
64
|
+
// broker with nothing naming the cause.
|
|
65
|
+
const torn = tornRotationProblem(observer, sys.evictor, () => repairAdvice(target.source, [MEMBERSHIP_OBSERVER_CREDS_KIND, CONNECTION_EVICTOR_CREDS_KIND]));
|
|
66
|
+
if (torn)
|
|
67
|
+
throw new Error(`${verb}: ${torn}. Refusing`);
|
|
68
|
+
}
|
|
69
|
+
return { observer, ...(sys.evictor !== undefined ? { evictor: sys.evictor } : {}) };
|
|
21
70
|
}
|
|
22
71
|
/**
|
|
23
72
|
* The delivery daemon's LIVE-EVICTION executor (D5 slice 6, on the privileged delivery-admin rail):
|
|
@@ -37,15 +86,12 @@ export async function executeEviction(server, target, principal) {
|
|
|
37
86
|
const parsed = parsePrincipalKey(principal);
|
|
38
87
|
if (!parsed || !isPrincipalOwnerToken(parsed.owner))
|
|
39
88
|
throw new Error(`evictPrincipal: "${principal}" is not a real owner.actor principal (owner must be \`local\` or a derived \`u_…\` token — the only shapes CONNZ attribution can surface)`);
|
|
40
|
-
const {
|
|
41
|
-
const
|
|
42
|
-
const evPath = join(dir, "connection-evictor.creds");
|
|
43
|
-
if (!existsSync(obsPath) || !existsSync(evPath))
|
|
44
|
-
throw new Error("evictPrincipal: the $SYS observer/evictor creds are not provisioned here (a space created before live eviction); mint them with `cotal down` then `cotal up --rotate-sys`. Until then removal is deny-new-only (durable reauth)");
|
|
89
|
+
const { accountId } = resolveScan(target, "evictPrincipal");
|
|
90
|
+
const sys = await loadCheckedSys(target, "evictPrincipal", "both");
|
|
45
91
|
return evictDeniedPrincipalWithCreds({
|
|
46
92
|
servers: server,
|
|
47
|
-
observerCreds:
|
|
48
|
-
evictorCreds:
|
|
93
|
+
observerCreds: sys.observer,
|
|
94
|
+
evictorCreds: sys.evictor,
|
|
49
95
|
accountId,
|
|
50
96
|
principal,
|
|
51
97
|
});
|
|
@@ -58,6 +104,11 @@ export async function executeEviction(server, target, principal) {
|
|
|
58
104
|
* connection tuples; anything else refuses loudly. A space provisioned before the observer existed
|
|
59
105
|
* refuses with the regeneration step — the auth plane treats that refusal as UNKNOWN and never
|
|
60
106
|
* reclaims over it (fail-closed).
|
|
107
|
+
*
|
|
108
|
+
* Asks for the observer ONLY, so the KICK cred is not even read into this process's memory on a
|
|
109
|
+
* read-only path. The cost is that the torn-rotation check cannot run here (it needs both halves);
|
|
110
|
+
* that is safe rather than a gap — a torn pair whose observer is the stale half is refused by the
|
|
111
|
+
* broker, and this path reads any refusal as UNKNOWN, which is already fail-closed.
|
|
61
112
|
*/
|
|
62
113
|
export async function executePlaneLiveness(server, target, query) {
|
|
63
114
|
const q = query;
|
|
@@ -65,13 +116,11 @@ export async function executePlaneLiveness(server, target, query) {
|
|
|
65
116
|
Object.keys(q).some((k) => k !== "ledger" && k !== "records") || // closed top-level shape
|
|
66
117
|
!isPlaneConnTuple(q.ledger) || !isPlaneConnTuple(q.records))
|
|
67
118
|
throw new Error("planeConnLiveness: the query must be exactly { ledger, records } connection tuples ({ serverId, cid, userNkey }); refusing a malformed or wider query");
|
|
68
|
-
const {
|
|
69
|
-
const
|
|
70
|
-
if (!existsSync(obsPath))
|
|
71
|
-
throw new Error("planeConnLiveness: the $SYS observer creds are not provisioned here (a space created before live observation); mint them with `cotal down` then `cotal up --rotate-sys`");
|
|
119
|
+
const { accountId } = resolveScan(target, "planeConnLiveness");
|
|
120
|
+
const sys = await loadCheckedSys(target, "planeConnLiveness", "observer");
|
|
72
121
|
return observePlaneLivenessWithCreds({
|
|
73
122
|
servers: server,
|
|
74
|
-
observerCreds:
|
|
123
|
+
observerCreds: sys.observer,
|
|
75
124
|
accountId,
|
|
76
125
|
query: { ledger: q.ledger, records: q.records },
|
|
77
126
|
});
|
|
@@ -80,7 +129,7 @@ export async function executePlaneLiveness(server, target, query) {
|
|
|
80
129
|
* The delivery daemon's FREEZE-HOLDER LIVENESS probe (#391, on the privileged delivery-admin rail):
|
|
81
130
|
* answer whether ONE principal still holds a live connection, via the $SYS CONNZ observer cred
|
|
82
131
|
* (opened per call; READ-ONLY — the KICK evictor cred never enters this path, and is not even read
|
|
83
|
-
* from
|
|
132
|
+
* from the store here).
|
|
84
133
|
*
|
|
85
134
|
* This is the READ half of {@link executeEviction}, and it exists because the write half cannot
|
|
86
135
|
* serve as its own precheck: a repair that must REFUSE while the holder is alive would, using
|
|
@@ -100,13 +149,11 @@ export async function executePrincipalLiveness(server, target, principal) {
|
|
|
100
149
|
const parsed = parsePrincipalKey(wanted);
|
|
101
150
|
if (!parsed || !isPrincipalOwnerToken(parsed.owner))
|
|
102
151
|
throw new Error(`principalLiveness: "${wanted}" is not a real owner.actor principal (owner must be \`local\` or a derived \`u_…\` token — the only shapes CONNZ attribution can surface)`);
|
|
103
|
-
const {
|
|
104
|
-
const
|
|
105
|
-
if (!existsSync(obsPath))
|
|
106
|
-
throw new Error("principalLiveness: the $SYS observer creds are not provisioned here (a space created before live observation); mint them with `cotal down` then `cotal up --rotate-sys`");
|
|
152
|
+
const { accountId } = resolveScan(target, "principalLiveness");
|
|
153
|
+
const sys = await loadCheckedSys(target, "principalLiveness", "observer");
|
|
107
154
|
return observePrincipalLivenessWithCreds({
|
|
108
155
|
servers: server,
|
|
109
|
-
observerCreds:
|
|
156
|
+
observerCreds: sys.observer,
|
|
110
157
|
accountId,
|
|
111
158
|
principal: wanted,
|
|
112
159
|
});
|
package/dist/evict-exec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evict-exec.js","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,EAChB,qBAAqB,EACrB,6BAA6B,EAC7B,iCAAiC,EACjC,iBAAiB,GAKlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"evict-exec.js","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DELIVERY_CREDS_KIND, deliveryCredsKey, runDelivery } from "./delivery.js";
|
|
2
2
|
import { runFeedbackIntake } from "./feedback-intake.js";
|
|
3
|
-
export {
|
|
3
|
+
export { DELIVERY_CREDS_KIND, deliveryCredsKey, runDelivery };
|
|
4
4
|
export { runFeedbackIntake };
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AA4CzD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `@cotal-ai/manager` or `@cotal-ai/cli` (one-way tiering).
|
|
7
7
|
*/
|
|
8
8
|
import { registry } from "@cotal-ai/core";
|
|
9
|
-
import {
|
|
9
|
+
import { DELIVERY_CREDS_KIND, deliveryCredsKey, runDelivery } from "./delivery.js";
|
|
10
10
|
import { runFeedbackIntake } from "./feedback-intake.js";
|
|
11
11
|
const deliveryCommands = [
|
|
12
12
|
{
|
|
@@ -46,6 +46,6 @@ const deliveryCommands = [
|
|
|
46
46
|
},
|
|
47
47
|
];
|
|
48
48
|
registry.register(...deliveryCommands);
|
|
49
|
-
export {
|
|
49
|
+
export { DELIVERY_CREDS_KIND, deliveryCredsKey, runDelivery };
|
|
50
50
|
export { runFeedbackIntake };
|
|
51
51
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAgB,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/membership.d.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { type MembershipFeedHandle, type SecretStore } from "@cotal-ai/core";
|
|
2
2
|
/**
|
|
3
|
-
* The delivery daemon's thin composition root for the broker-sourced graph-membership feed.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* ({@link startMembershipFeed}, which owns the two connections + the poll loop + the rw 75% renewal).
|
|
3
|
+
* The delivery daemon's thin composition root for the broker-sourced graph-membership feed. Every
|
|
4
|
+
* credential it needs — the DATA-account rw cred AND the SYSTEM-account observer — now resolves
|
|
5
|
+
* through the {@link SecretStore} seam, so a hosted composition can supply all of them with no file
|
|
6
|
+
* on disk. `store` is that injection point; with none, the workstation FS store is used (keys = the
|
|
7
|
+
* filenames under `.cotal/`), so a local `cotal up` is byte-for-byte unchanged. It hands the two
|
|
8
|
+
* creds + the account id to the core feed engine ({@link startMembershipFeed}, which owns the two
|
|
9
|
+
* connections + the poll loop + the rw 75% renewal).
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* The ACCOUNT ID is no longer read from `.cotal/membership.json`. It arrives as `opts.accountId` —
|
|
12
|
+
* the account the daemon's own delivery cred authenticates as, pinned once at start — and the
|
|
13
|
+
* observer is checked against it intrinsically (see `sys-creds.ts`). That file was never an
|
|
14
|
+
* independent source: it sat in the same directory as the creds, so a wrong root was wrong for both.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* migrates; the manager re-signs only it.
|
|
16
|
+
* Deliberately ISOLATED from Plane-3: a separate module, separate connections, and a fail-soft
|
|
17
|
+
* contract — if the creds aren't provisioned (a pre-feature space) or the feed can't start, it logs
|
|
18
|
+
* and returns `{ down }`; the graph degrades to traffic-only and delivery is untouched.
|
|
20
19
|
*/
|
|
21
20
|
/** Why the feed is, or is not, running. `down` carries the DIAGNOSIS to the caller, because the
|
|
22
21
|
* daemon's log line is not the only place it is needed: an adoption reply that says only "the feed is
|
|
@@ -32,5 +31,6 @@ export type MembershipStart = {
|
|
|
32
31
|
export declare function startMembership(opts: {
|
|
33
32
|
space: string;
|
|
34
33
|
server: string;
|
|
34
|
+
accountId: string;
|
|
35
35
|
}, store?: SecretStore): Promise<MembershipStart>;
|
|
36
36
|
//# sourceMappingURL=membership.d.ts.map
|
package/dist/membership.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAIrH;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;+FAG+F;AAC/F,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,oBAAoB,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAExH,wBAAsB,eAAe,CACnC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAC1D,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,CAAC,CA8G1B"}
|
package/dist/membership.js
CHANGED
|
@@ -1,78 +1,85 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { findCotalRoot, MEMBERSHIP_RW_CREDS_KEY, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
1
|
+
import { inspectCredHealth, startMembershipFeed } from "@cotal-ai/core";
|
|
2
|
+
import { CONNECTION_EVICTOR_CREDS_KIND, findCotalRoot, membershipRwCredsKey, MEMBERSHIP_OBSERVER_CREDS_KIND, MEMBERSHIP_RW_CREDS_KIND, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
3
|
+
import { loadSysPair, observerTenancyProblem, repairAdvice, tornRotationProblem } from "./sys-creds.js";
|
|
5
4
|
export async function startMembership(opts, store) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
// `injected` is the composition root's own fact, decided here before any I/O — never inferred by
|
|
6
|
+
// probing the store or sniffing `.cotal/`, which would report "workstation" for a hosted daemon
|
|
7
|
+
// and emit a CLI repair the host cannot run (design §4.1). The workstation arm carries the root
|
|
8
|
+
// it resolved, because that is what lets `repairAdvice` ask whether the command it would name is
|
|
9
|
+
// one this root can actually run; a hosted composition has no root and names no command.
|
|
10
|
+
// Spelled as a branch, not a `??`, so the root is still resolved ONLY when no store was injected.
|
|
11
|
+
let source;
|
|
12
|
+
if (store === undefined) {
|
|
13
|
+
const root = findCotalRoot();
|
|
14
|
+
source = { secrets: workspaceSecretStore(root), space: opts.space, injected: false, root };
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
source = { secrets: store, space: opts.space, injected: true };
|
|
18
|
+
}
|
|
19
|
+
// Through the per-kind resolver (P7 §2 rule 1) — the source's arms ARE the composition it takes.
|
|
20
|
+
// The rw cred is absent-means-MINT on the `up` side, so reading the canonical location past an
|
|
21
|
+
// unmigrated copy would report the bundle incomplete and let the next `up` mint a second one.
|
|
22
|
+
const rwKey = membershipRwCredsKey(opts.space, source);
|
|
23
|
+
const rw = await source.secrets.get(rwKey);
|
|
24
|
+
// Ask for both $SYS creds: the observer is REQUIRED, the evictor only feeds the torn-rotation
|
|
25
|
+
// check below. A space with no evictor still runs a perfectly good feed (eviction refuses on its
|
|
26
|
+
// own path, loudly), so its absence must not take the feed down.
|
|
27
|
+
const sys = await loadSysPair(source, "both");
|
|
12
28
|
const missing = [
|
|
13
|
-
rw === undefined ?
|
|
14
|
-
|
|
15
|
-
existsSync(cfgPath) ? undefined : "membership.json",
|
|
29
|
+
rw === undefined ? MEMBERSHIP_RW_CREDS_KIND : undefined,
|
|
30
|
+
...sys.missing.filter((k) => k !== CONNECTION_EVICTOR_CREDS_KIND),
|
|
16
31
|
].filter((f) => f !== undefined);
|
|
17
32
|
if (missing.length) {
|
|
18
33
|
// Name the missing piece AND a repair that reaches it. The bundle has two halves with two
|
|
19
34
|
// different repairs, and naming the wrong one costs an operator a full mesh stop for nothing.
|
|
20
35
|
// The $SYS-signed half (observer, evictor) can only be minted while the never-persisted system
|
|
21
|
-
// signing seed is in memory, so it takes a rotation. The DATA half (rw cred
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
? "run `cotal up` to provision the data-account half (no rotation needed)"
|
|
34
|
-
: "the $SYS-signed creds can only be re-minted while the system account is being provisioned: `cotal down` then `cotal up --rotate-sys`"}`;
|
|
36
|
+
// signing seed is in memory, so it takes a rotation. The DATA half (the rw cred) is signed by
|
|
37
|
+
// the data account, whose seed IS persisted, so a plain `cotal up` heals it — that is what
|
|
38
|
+
// `healMembershipDataCreds` in `up` does, on every path rather than only a fresh space.
|
|
39
|
+
// Only the REPAIR tail forks on `injected`; this diagnosis half is identical in both.
|
|
40
|
+
const sysMissing = missing.filter((m) => m === MEMBERSHIP_OBSERVER_CREDS_KIND);
|
|
41
|
+
const down = missing.length === 1 && missing[0] === MEMBERSHIP_OBSERVER_CREDS_KIND
|
|
42
|
+
? `the $SYS observer cred is missing (kind "${MEMBERSHIP_OBSERVER_CREDS_KIND}") - ${repairAdvice(source, sysMissing)}`
|
|
43
|
+
: `the membership bundle is incomplete here (missing ${missing.join(", ")}) - ${sysMissing.length === 0
|
|
44
|
+
? source.injected
|
|
45
|
+
? `re-sign the data-account half into the store under ${rwKey} (no rotation needed)`
|
|
46
|
+
: "run `cotal up` to provision the data-account half (no rotation needed)"
|
|
47
|
+
: `the $SYS-signed creds can only be re-minted while the system account is being provisioned: ${repairAdvice(source, sysMissing)}`}`;
|
|
35
48
|
console.error(`• membership: ${down}. The graph falls back to traffic-only; delivery is unaffected.`);
|
|
36
49
|
return { down };
|
|
37
50
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
51
|
+
const obsCreds = sys.observer; // required above; absence already returned
|
|
52
|
+
// TENANCY, before connecting. The observer names its own DATA account in its CONNZ permission, so
|
|
53
|
+
// it can be checked against the account this daemon's own cred authenticates as with no adjacent
|
|
54
|
+
// file at all. The broker enforces the same scoping independently — this local read buys the
|
|
55
|
+
// DIAGNOSIS (a line naming both accounts) rather than the guarantee.
|
|
56
|
+
const tenancy = observerTenancyProblem(obsCreds, opts.accountId);
|
|
57
|
+
if (tenancy) {
|
|
58
|
+
console.error(`! membership: ${tenancy}; graph membership degraded, delivery unaffected`);
|
|
59
|
+
return { down: tenancy };
|
|
60
|
+
}
|
|
61
|
+
// A TORN rotation is the other way this cred goes broker-dead without a byte of its own changing:
|
|
62
|
+
// `rotateSystemCreds` commits the trust record, then writes both $SYS creds, so a crash between the
|
|
63
|
+
// two leaves one on the retired system account, and the broker answers the same bare
|
|
64
|
+
// "Authorization Violation" either way. Shared with the eviction path as of this change — it used
|
|
65
|
+
// to live only here, which left eviction opening a half-rotated pair blind.
|
|
66
|
+
if (sys.evictor !== undefined) {
|
|
67
|
+
const torn = tornRotationProblem(obsCreds, sys.evictor, () => repairAdvice(source, [MEMBERSHIP_OBSERVER_CREDS_KIND, CONNECTION_EVICTOR_CREDS_KIND]));
|
|
68
|
+
if (torn) {
|
|
69
|
+
console.error(`! membership: ${torn}; graph membership degraded, delivery unaffected`);
|
|
70
|
+
return { down: torn };
|
|
71
|
+
}
|
|
43
72
|
}
|
|
44
73
|
// Check the OBSERVER's own expiry before connecting. It is `rotation-renewed`, so unlike every
|
|
45
74
|
// renewable cred here nothing re-signs it: at its 30-day horizon the broker simply answers
|
|
46
75
|
// "Authorization Violation", which names neither the credential nor the repair. Reading the JWT
|
|
47
76
|
// costs nothing and turns the daemon's one loud line into the actual diagnosis: the difference
|
|
48
77
|
// between an operator finding this in minutes and finding it in a support thread.
|
|
49
|
-
const obsCreds = readFileSync(obsPath, "utf8");
|
|
50
|
-
// A TORN rotation is the other way this cred goes broker-dead without a byte of its own changing:
|
|
51
|
-
// `rotateSystemCreds` commits the trust record, then writes both $SYS creds, so a crash between the
|
|
52
|
-
// two leaves one file on the retired system account. The broker answers the same bare
|
|
53
|
-
// "Authorization Violation" either way. The daemon cannot ask the trust record which account is
|
|
54
|
-
// current (it deliberately never loads the signer), but it does not need to: the pair is written
|
|
55
|
-
// by one rotation, so two DIFFERENT issuers prove one of them is stale, with no signer read at all.
|
|
56
|
-
// (`doctor auth`, which legitimately holds the record, checks each file against it directly.)
|
|
57
|
-
const evPath = join(dir, "connection-evictor.creds");
|
|
58
|
-
if (existsSync(evPath)) {
|
|
59
|
-
let obsIss, evIss;
|
|
60
|
-
try {
|
|
61
|
-
obsIss = credsClaims(obsCreds).iss;
|
|
62
|
-
evIss = credsClaims(readFileSync(evPath, "utf8")).iss;
|
|
63
|
-
}
|
|
64
|
-
catch { /* an unreadable file is the health check's case just below */ }
|
|
65
|
-
if (obsIss !== undefined && evIss !== undefined && obsIss !== evIss) {
|
|
66
|
-
const down = `the two $SYS creds are signed by DIFFERENT system accounts (observer ${obsIss.slice(0, 12)}…, evictor ${evIss.slice(0, 12)}…) - a system-account rotation did not finish, so one of them is broker-dead: re-run \`cotal down\` then \`cotal up --rotate-sys\` to land a complete generation`;
|
|
67
|
-
console.error(`! membership: ${down}; graph membership degraded, delivery unaffected`);
|
|
68
|
-
return { down };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
78
|
const obs = inspectCredHealth(obsCreds);
|
|
72
79
|
if (obs.state === "expired" || obs.state === "unreadable") {
|
|
73
80
|
const down = obs.state === "expired"
|
|
74
|
-
? `the $SYS observer cred (${
|
|
75
|
-
: `the $SYS observer cred (${
|
|
81
|
+
? `the $SYS observer cred (kind "${MEMBERSHIP_OBSERVER_CREDS_KIND}") EXPIRED ${new Date((obs.exp ?? 0) * 1000).toISOString()} and the broker denies it - it is rotation-renewed, so nothing re-signs it: ${repairAdvice(source, [MEMBERSHIP_OBSERVER_CREDS_KIND])} (agents, creds and data are untouched)`
|
|
82
|
+
: `the $SYS observer cred (kind "${MEMBERSHIP_OBSERVER_CREDS_KIND}") is unreadable (${obs.error})`;
|
|
76
83
|
console.error(`! membership: ${down}; graph membership degraded, delivery unaffected`);
|
|
77
84
|
return { down };
|
|
78
85
|
}
|
|
@@ -80,16 +87,16 @@ export async function startMembership(opts, store) {
|
|
|
80
87
|
const handle = await startMembershipFeed({
|
|
81
88
|
servers: opts.server,
|
|
82
89
|
space: opts.space,
|
|
83
|
-
accountId,
|
|
90
|
+
accountId: opts.accountId,
|
|
84
91
|
// Observer is rotation-renewed ($SYS): a static read — its renewal is a system rotation + restart.
|
|
85
92
|
observerCreds: obsCreds,
|
|
86
93
|
// rw is class-2 standing-renewable: read through the store seam and renewed on a 75% timer that
|
|
87
94
|
// preflight-proves each candidate the manager re-signs into the store (D5 slice 5). The daemon
|
|
88
95
|
// never sees the signer; the manager owns the re-sign.
|
|
89
96
|
rwCreds: async () => {
|
|
90
|
-
const cur = await secrets.get(
|
|
97
|
+
const cur = await source.secrets.get(rwKey);
|
|
91
98
|
if (cur === undefined)
|
|
92
|
-
throw new Error(`membership: the scoped rw cred is gone (
|
|
99
|
+
throw new Error(`membership: the scoped rw cred is gone (kind "${MEMBERSHIP_RW_CREDS_KIND}") — restore it (locally: re-run \`cotal up\`) before the current JWT expires`);
|
|
93
100
|
return cur;
|
|
94
101
|
},
|
|
95
102
|
intervalMs,
|
package/dist/membership.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type SecretStore } from "@cotal-ai/core";
|
|
2
|
+
/**
|
|
3
|
+
* THE `$SYS` PAIR, read through the {@link SecretStore} seam and checked before it is used.
|
|
4
|
+
*
|
|
5
|
+
* Both consumers of the observer/evictor creds — the graph feed (`membership.ts`) and the
|
|
6
|
+
* eviction/liveness executors (`evict-exec.ts`) — resolve them here so the three checks that make a
|
|
7
|
+
* `$SYS` cred trustworthy cannot drift apart or exist on only one path:
|
|
8
|
+
*
|
|
9
|
+
* 1. TENANCY (the observer names its own account — {@link observerTenancyProblem});
|
|
10
|
+
* 2. TORN ROTATION (the pair was signed by ONE system account — {@link tornRotationProblem});
|
|
11
|
+
* 3. STORE-AWARE REPAIR (the advice names something the reader can actually do —
|
|
12
|
+
* {@link repairAdvice}).
|
|
13
|
+
*
|
|
14
|
+
* Check 2 previously existed ONLY in the feed path, so the eviction path could open a half-rotated
|
|
15
|
+
* pair and get a bare "Authorization Violation" from the broker. Sharing it is the point of this
|
|
16
|
+
* module, not a side effect of it.
|
|
17
|
+
*
|
|
18
|
+
* This module DECIDES NOTHING ABOUT POSTURE. It reports problems as strings and absence as a list
|
|
19
|
+
* of missing keys; whether that is a fail-soft `down` (the feed) or a loud throw (eviction) belongs
|
|
20
|
+
* to the caller, because the two postures are deliberately different and flattening them here is
|
|
21
|
+
* exactly the refactor `docs/design/u3-membership-sys-injection.md` §4 exists to prevent.
|
|
22
|
+
*/
|
|
23
|
+
/** The `$SYS` creds' source: the store to read them from, the SPACE they are scoped to, and whether
|
|
24
|
+
* that store was INJECTED. `injected` is the composition root's own fact (`store !== undefined` at
|
|
25
|
+
* the runner) — never inferred by probing the store or sniffing `.cotal/`, both of which report
|
|
26
|
+
* "workstation" for a hosted daemon and would emit CLI advice a host cannot run (design §4.1).
|
|
27
|
+
*
|
|
28
|
+
* A UNION rather than a flag plus an optional root, because the workstation arm cannot do its job
|
|
29
|
+
* without a root and the hosted arm has none: {@link repairAdvice} must ASK the root whether the
|
|
30
|
+
* command it is about to name would run, and a `root?: string` would let a composition build a
|
|
31
|
+
* workstation source with no root and silently fall back to advice nobody checked. Same idiom as
|
|
32
|
+
* the rest of this module — the unsound call is made impossible to express rather than guarded
|
|
33
|
+
* against at runtime. The two arms are exactly workspace's `SpaceMaterialComposition`, which is
|
|
34
|
+
* what {@link loadSysPair} hands the per-kind resolvers.
|
|
35
|
+
*
|
|
36
|
+
* `space` joined it in P7: the pair is per-space material now, so a source that named only a store
|
|
37
|
+
* and a root no longer names a location. It is on BOTH arms because both need it — the workstation
|
|
38
|
+
* arm to migrate and read, the hosted arm to build the key it tells an operator to `put` under. */
|
|
39
|
+
export type SysCredsSource = {
|
|
40
|
+
secrets: SecretStore;
|
|
41
|
+
space: string;
|
|
42
|
+
injected: true;
|
|
43
|
+
root?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
secrets: SecretStore;
|
|
46
|
+
space: string;
|
|
47
|
+
injected: false;
|
|
48
|
+
root: string;
|
|
49
|
+
};
|
|
50
|
+
/** Repair advice for missing/unusable `$SYS` material, in the reader's own idiom.
|
|
51
|
+
*
|
|
52
|
+
* The DIAGNOSIS half (which key, which account) is identical in both compositions and is built by
|
|
53
|
+
* the caller; only this repair TAIL forks. On a workstation the repair is a real command; against a
|
|
54
|
+
* hosted store it is the mint window plus the key to `put` under, because `cotal up --rotate-sys`
|
|
55
|
+
* is unactionable there — and emitting it into a hosted log degrades the diagnosis even when the
|
|
56
|
+
* failure semantics are right. Both halves say the same true thing: the `$SYS` pair can only be
|
|
57
|
+
* minted while the never-persisted system signing seed is in memory.
|
|
58
|
+
*
|
|
59
|
+
* THERE IS A THIRD CASE, and until now this function could not see it: on a root hosting several
|
|
60
|
+
* spaces the workstation advice names two verbs that BOTH refuse. `cotal up --rotate-sys` and
|
|
61
|
+
* `cotal down` are broker-wide and `assertSingleSpaceBroker` turns them away naming the tenants,
|
|
62
|
+
* so the operator whose observer is missing is handed a command, runs it, and is refused — with
|
|
63
|
+
* the refusal itself explaining that the remedy does not exist yet. That is advice which cannot
|
|
64
|
+
* succeed, the same defect `healMembershipDataCreds`'s own comment records, and it was reachable
|
|
65
|
+
* on a real root (`docs/design/space-segmentation-p7-p1.md` §6, probe-executed).
|
|
66
|
+
*
|
|
67
|
+
* So the workstation arm consults the guard before naming its command, and where the guard would
|
|
68
|
+
* refuse it states the truth instead: the guard's own words, then the reason no other command can
|
|
69
|
+
* substitute. NAMING NOTHING IS THE POINT — there is no verb to offer here, and inventing a
|
|
70
|
+
* gentler-sounding one would restore the defect.
|
|
71
|
+
*
|
|
72
|
+
* This makes the function read the filesystem on the workstation arm, which is why the callers
|
|
73
|
+
* pass it as a THUNK: it is built only on a path that has already failed, never on a healthy one. */
|
|
74
|
+
export declare function repairAdvice(source: SysCredsSource, kinds: readonly string[]): string;
|
|
75
|
+
/** The DATA account an observer cred is scoped to, read out of its own `$SYS.REQ.ACCOUNT.<id>.CONNZ`
|
|
76
|
+
* publish permission, or `undefined` if it carries none.
|
|
77
|
+
*
|
|
78
|
+
* This is a LOCAL read of a signed document, and it buys the DIAGNOSIS, not the guarantee: the
|
|
79
|
+
* broker independently refuses a CONNZ request for any other account, because the permission is in
|
|
80
|
+
* the JWT it validates. Doing it here, before connecting, is what turns that refusal from a bare
|
|
81
|
+
* "Authorization Violation" into a line naming both accounts (design §4.3). */
|
|
82
|
+
export declare function connzAccountOf(observerCreds: string): string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* THE TENANCY CHECK — the guard that replaces the eliminated `membership.json` cross-check.
|
|
85
|
+
*
|
|
86
|
+
* Every sweep below resolves an ACCOUNT, and a complete, well-formed sweep of the WRONG account is
|
|
87
|
+
* indistinguishable from "the principal is gone": a healthy-looking answer that authorizes eviction.
|
|
88
|
+
* Note the asymmetry that makes this necessary — an observer scoped to A used with accountId B
|
|
89
|
+
* under-reports (broker-denied → `unknown`, safe), but observer A used with accountId A while the
|
|
90
|
+
* GATE lives on B is internally consistent and answers a confident, WRONG `gone`.
|
|
91
|
+
*
|
|
92
|
+
* Checking the observer against the account the daemon's OWN cred authenticates as is strictly
|
|
93
|
+
* stronger than the file it replaces: the file sat in the same `.cotal/` dir as the creds, so a root
|
|
94
|
+
* that was wrong was wrong for both, and its independence came from `expectedAccount` being derived
|
|
95
|
+
* from the cred anyway. The cred was always the real authority; the file was the thing being checked.
|
|
96
|
+
*
|
|
97
|
+
* An observer carrying NO CONNZ permission is refused rather than trusted: it cannot do the job, and
|
|
98
|
+
* treating "no account named" as "any account" is the exact failure this guard exists to stop.
|
|
99
|
+
*/
|
|
100
|
+
export declare function observerTenancyProblem(observerCreds: string, expectedAccount: string): string | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* THE TORN-ROTATION CHECK — two `$SYS` creds signed by DIFFERENT system accounts.
|
|
103
|
+
*
|
|
104
|
+
* `rotateSystemCreds` commits the trust record, then writes both creds, so a crash between the two
|
|
105
|
+
* leaves one of them on the RETIRED system account. The broker answers the same bare "Authorization
|
|
106
|
+
* Violation" either way, and the daemon cannot ask the trust record which account is current (it
|
|
107
|
+
* deliberately never loads the signer) — but it does not need to: the pair is written by ONE
|
|
108
|
+
* rotation, so two different issuers prove one of them is stale, with no signer read at all.
|
|
109
|
+
*
|
|
110
|
+
* Shared by both paths as of this change. It previously lived only in the feed, which left eviction
|
|
111
|
+
* — the path that actually kills connections — opening a half-rotated pair blind.
|
|
112
|
+
*
|
|
113
|
+
* `advice` is a THUNK because {@link repairAdvice} now reads the root's tenant list: both call sites
|
|
114
|
+
* sit on a healthy path (`loadCheckedSys` runs per eviction), and eagerly building advice nobody
|
|
115
|
+
* will read would put a directory listing on every successful call.
|
|
116
|
+
*/
|
|
117
|
+
export declare function tornRotationProblem(observerCreds: string, evictorCreds: string, advice: () => string): string | undefined;
|
|
118
|
+
/** What {@link loadSysPair} was asked for. The liveness verbs are READ-ONLY and must not even read
|
|
119
|
+
* the KICK cred — least privilege is not only about what a connection may do, but about what the
|
|
120
|
+
* process reads into memory on a path that never needs it. */
|
|
121
|
+
export type SysCredsNeed = "observer" | "both";
|
|
122
|
+
export interface SysPair {
|
|
123
|
+
observer?: string;
|
|
124
|
+
evictor?: string;
|
|
125
|
+
/** Keys the store returned `undefined` for — ABSENCE, per the `SecretStore` contract. A `get()`
|
|
126
|
+
* that THROWS is a refusal, not an absence, and propagates out of {@link loadSysPair}. */
|
|
127
|
+
missing: string[];
|
|
128
|
+
}
|
|
129
|
+
/** Read the `$SYS` pair through the store. Absence is reported; every other failure propagates.
|
|
130
|
+
*
|
|
131
|
+
* The distinction is load-bearing: `undefined` means "not provisioned here", which each caller
|
|
132
|
+
* answers in its own posture, whereas a KMS timeout or a revoked role is a REFUSAL that must not be
|
|
133
|
+
* mistaken for an unprovisioned space and quietly degraded into deny-new-only.
|
|
134
|
+
*
|
|
135
|
+
* Reading per call (rather than once at start) is strictly better than the `readFileSync` it
|
|
136
|
+
* replaces: a hosted store re-keyed by a rotation is picked up on the next eviction with no daemon
|
|
137
|
+
* restart. No renewal timer is added, and none is wanted — these stay `rotation-renewed`. */
|
|
138
|
+
export declare function loadSysPair(source: SysCredsSource, need: SysCredsNeed): Promise<SysPair>;
|
|
139
|
+
//# sourceMappingURL=sys-creds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sys-creds.d.ts","sourceRoot":"","sources":["../src/sys-creds.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAM/D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;oGAeoG;AACpG,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GACzE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAsB3E;;;;;;;;;;;;;;;;;;;;;;;sGAuBsG;AACtG,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAgBrF;AAED;;;;;;gFAMgF;AAChF,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAezG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,GAAG,SAAS,CAazH;AAED;;+DAE+D;AAC/D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;+FAC2F;IAC3F,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;8FAQ8F;AAC9F,wBAAsB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9F"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { credsClaims } from "@cotal-ai/core";
|
|
2
|
+
import { assertSingleSpaceBroker, authDir, connectionEvictorCredsKey, CONNECTION_EVICTOR_CREDS_KIND, membershipObserverCredsKey, MEMBERSHIP_OBSERVER_CREDS_KIND, segmentedKey, } from "@cotal-ai/workspace";
|
|
3
|
+
/** The broker-wide operation `repairAdvice` would send a workstation operator to, phrased so the
|
|
4
|
+
* guard's own refusal reads as a sentence when it is quoted back. */
|
|
5
|
+
const ROTATE_SYS = "re-minting the $SYS pair (`cotal up --rotate-sys`)";
|
|
6
|
+
/** Why the rotation would refuse on this root, or `undefined` if it would run.
|
|
7
|
+
*
|
|
8
|
+
* ASKED OF THE GUARD ITSELF, never re-derived from a tenant count read here. The advice is only
|
|
9
|
+
* honest if it agrees with the thing that actually refuses, and a second implementation of the
|
|
10
|
+
* single-space rule is a second thing to drift. Every throw counts as a refusal, including the
|
|
11
|
+
* fail-CLOSED corrupt-inventory one: an operator sent to a command that refuses for a reason this
|
|
12
|
+
* function could not read is in exactly the state this check exists to end. */
|
|
13
|
+
function rotationRefusal(root) {
|
|
14
|
+
try {
|
|
15
|
+
assertSingleSpaceBroker(authDir(root), ROTATE_SYS);
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return e.message;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Repair advice for missing/unusable `$SYS` material, in the reader's own idiom.
|
|
23
|
+
*
|
|
24
|
+
* The DIAGNOSIS half (which key, which account) is identical in both compositions and is built by
|
|
25
|
+
* the caller; only this repair TAIL forks. On a workstation the repair is a real command; against a
|
|
26
|
+
* hosted store it is the mint window plus the key to `put` under, because `cotal up --rotate-sys`
|
|
27
|
+
* is unactionable there — and emitting it into a hosted log degrades the diagnosis even when the
|
|
28
|
+
* failure semantics are right. Both halves say the same true thing: the `$SYS` pair can only be
|
|
29
|
+
* minted while the never-persisted system signing seed is in memory.
|
|
30
|
+
*
|
|
31
|
+
* THERE IS A THIRD CASE, and until now this function could not see it: on a root hosting several
|
|
32
|
+
* spaces the workstation advice names two verbs that BOTH refuse. `cotal up --rotate-sys` and
|
|
33
|
+
* `cotal down` are broker-wide and `assertSingleSpaceBroker` turns them away naming the tenants,
|
|
34
|
+
* so the operator whose observer is missing is handed a command, runs it, and is refused — with
|
|
35
|
+
* the refusal itself explaining that the remedy does not exist yet. That is advice which cannot
|
|
36
|
+
* succeed, the same defect `healMembershipDataCreds`'s own comment records, and it was reachable
|
|
37
|
+
* on a real root (`docs/design/space-segmentation-p7-p1.md` §6, probe-executed).
|
|
38
|
+
*
|
|
39
|
+
* So the workstation arm consults the guard before naming its command, and where the guard would
|
|
40
|
+
* refuse it states the truth instead: the guard's own words, then the reason no other command can
|
|
41
|
+
* substitute. NAMING NOTHING IS THE POINT — there is no verb to offer here, and inventing a
|
|
42
|
+
* gentler-sounding one would restore the defect.
|
|
43
|
+
*
|
|
44
|
+
* This makes the function read the filesystem on the workstation arm, which is why the callers
|
|
45
|
+
* pass it as a THUNK: it is built only on a path that has already failed, never on a healthy one. */
|
|
46
|
+
export function repairAdvice(source, kinds) {
|
|
47
|
+
const named = kinds.length ? kinds.join(" + ") : "the $SYS pair";
|
|
48
|
+
// The hosted arm names a KEY to `put` under, so it must name the SEGMENTED one — the kind alone is
|
|
49
|
+
// the pre-P7 key and putting there would leave the daemon still reading an empty location. The
|
|
50
|
+
// workstation arm names a command, not a key, so it keeps the kinds an operator recognises.
|
|
51
|
+
// Non-migrating on purpose: this builds a STRING for a store the process cannot reach.
|
|
52
|
+
if (source.injected)
|
|
53
|
+
return `re-mint the $SYS pair at a system-account rotation (the seed is in memory only at that moment) and \`put\` it under ${kinds.length ? kinds.map((k) => segmentedKey(k, source.space)).join(" + ") : "the $SYS pair's per-space keys"}`;
|
|
54
|
+
const refusal = rotationRefusal(source.root);
|
|
55
|
+
if (refusal === undefined)
|
|
56
|
+
return "re-mint it with `cotal down` then `cotal up --rotate-sys`";
|
|
57
|
+
return (`${refusal}. There is no other command that mints ${named}: the $SYS signing seed is discarded at provisioning, ` +
|
|
58
|
+
"so nothing can re-sign it on this root until per-space segmentation lands");
|
|
59
|
+
}
|
|
60
|
+
/** The DATA account an observer cred is scoped to, read out of its own `$SYS.REQ.ACCOUNT.<id>.CONNZ`
|
|
61
|
+
* publish permission, or `undefined` if it carries none.
|
|
62
|
+
*
|
|
63
|
+
* This is a LOCAL read of a signed document, and it buys the DIAGNOSIS, not the guarantee: the
|
|
64
|
+
* broker independently refuses a CONNZ request for any other account, because the permission is in
|
|
65
|
+
* the JWT it validates. Doing it here, before connecting, is what turns that refusal from a bare
|
|
66
|
+
* "Authorization Violation" into a line naming both accounts (design §4.3). */
|
|
67
|
+
export function connzAccountOf(observerCreds) {
|
|
68
|
+
for (const subject of credsClaims(observerCreds).nats?.pub?.allow ?? []) {
|
|
69
|
+
const m = /^\$SYS\.REQ\.ACCOUNT\.(A[A-Z2-7]{55})\.CONNZ$/.exec(subject);
|
|
70
|
+
if (m)
|
|
71
|
+
return m[1];
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* THE TENANCY CHECK — the guard that replaces the eliminated `membership.json` cross-check.
|
|
77
|
+
*
|
|
78
|
+
* Every sweep below resolves an ACCOUNT, and a complete, well-formed sweep of the WRONG account is
|
|
79
|
+
* indistinguishable from "the principal is gone": a healthy-looking answer that authorizes eviction.
|
|
80
|
+
* Note the asymmetry that makes this necessary — an observer scoped to A used with accountId B
|
|
81
|
+
* under-reports (broker-denied → `unknown`, safe), but observer A used with accountId A while the
|
|
82
|
+
* GATE lives on B is internally consistent and answers a confident, WRONG `gone`.
|
|
83
|
+
*
|
|
84
|
+
* Checking the observer against the account the daemon's OWN cred authenticates as is strictly
|
|
85
|
+
* stronger than the file it replaces: the file sat in the same `.cotal/` dir as the creds, so a root
|
|
86
|
+
* that was wrong was wrong for both, and its independence came from `expectedAccount` being derived
|
|
87
|
+
* from the cred anyway. The cred was always the real authority; the file was the thing being checked.
|
|
88
|
+
*
|
|
89
|
+
* An observer carrying NO CONNZ permission is refused rather than trusted: it cannot do the job, and
|
|
90
|
+
* treating "no account named" as "any account" is the exact failure this guard exists to stop.
|
|
91
|
+
*/
|
|
92
|
+
export function observerTenancyProblem(observerCreds, expectedAccount) {
|
|
93
|
+
let scoped;
|
|
94
|
+
try {
|
|
95
|
+
scoped = connzAccountOf(observerCreds);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return `the $SYS observer cred is unreadable (${e.message})`;
|
|
99
|
+
}
|
|
100
|
+
if (scoped === undefined)
|
|
101
|
+
return "the $SYS observer cred carries no `$SYS.REQ.ACCOUNT.<id>.CONNZ` permission, so it names no account to sweep and cannot be checked against this daemon's own tenancy";
|
|
102
|
+
if (scoped !== expectedAccount)
|
|
103
|
+
return (`the $SYS observer cred is scoped to account ${scoped}, but this daemon's own credential authenticates as ${expectedAccount}. ` +
|
|
104
|
+
"That credential belongs to a different mesh, and sweeping it would return a confident, WRONG answer (a complete sweep of the wrong account is indistinguishable from a gone principal)");
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* THE TORN-ROTATION CHECK — two `$SYS` creds signed by DIFFERENT system accounts.
|
|
109
|
+
*
|
|
110
|
+
* `rotateSystemCreds` commits the trust record, then writes both creds, so a crash between the two
|
|
111
|
+
* leaves one of them on the RETIRED system account. The broker answers the same bare "Authorization
|
|
112
|
+
* Violation" either way, and the daemon cannot ask the trust record which account is current (it
|
|
113
|
+
* deliberately never loads the signer) — but it does not need to: the pair is written by ONE
|
|
114
|
+
* rotation, so two different issuers prove one of them is stale, with no signer read at all.
|
|
115
|
+
*
|
|
116
|
+
* Shared by both paths as of this change. It previously lived only in the feed, which left eviction
|
|
117
|
+
* — the path that actually kills connections — opening a half-rotated pair blind.
|
|
118
|
+
*
|
|
119
|
+
* `advice` is a THUNK because {@link repairAdvice} now reads the root's tenant list: both call sites
|
|
120
|
+
* sit on a healthy path (`loadCheckedSys` runs per eviction), and eagerly building advice nobody
|
|
121
|
+
* will read would put a directory listing on every successful call.
|
|
122
|
+
*/
|
|
123
|
+
export function tornRotationProblem(observerCreds, evictorCreds, advice) {
|
|
124
|
+
let obsIss, evIss;
|
|
125
|
+
try {
|
|
126
|
+
obsIss = credsClaims(observerCreds).iss;
|
|
127
|
+
evIss = credsClaims(evictorCreds).iss;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return undefined; // an undecodable cred is the health check's case, reported with its own message
|
|
131
|
+
}
|
|
132
|
+
if (obsIss === undefined || evIss === undefined || obsIss === evIss)
|
|
133
|
+
return undefined;
|
|
134
|
+
return (`the two $SYS creds are signed by DIFFERENT system accounts (observer ${obsIss.slice(0, 12)}…, evictor ${evIss.slice(0, 12)}…) - ` +
|
|
135
|
+
`a system-account rotation did not finish, so one of them is broker-dead: ${advice()} to land a complete generation`);
|
|
136
|
+
}
|
|
137
|
+
/** Read the `$SYS` pair through the store. Absence is reported; every other failure propagates.
|
|
138
|
+
*
|
|
139
|
+
* The distinction is load-bearing: `undefined` means "not provisioned here", which each caller
|
|
140
|
+
* answers in its own posture, whereas a KMS timeout or a revoked role is a REFUSAL that must not be
|
|
141
|
+
* mistaken for an unprovisioned space and quietly degraded into deny-new-only.
|
|
142
|
+
*
|
|
143
|
+
* Reading per call (rather than once at start) is strictly better than the `readFileSync` it
|
|
144
|
+
* replaces: a hosted store re-keyed by a rotation is picked up on the next eviction with no daemon
|
|
145
|
+
* restart. No renewal timer is added, and none is wanted — these stay `rotation-renewed`. */
|
|
146
|
+
export async function loadSysPair(source, need) {
|
|
147
|
+
// The per-kind RESOLVERS (P7 §2 rule 1). The source's two arms ARE the composition they take, so
|
|
148
|
+
// a workstation daemon moves a legacy flat pair on this first touch and a hosted one never
|
|
149
|
+
// touches a filesystem. Reading the canonical location WITHOUT that move is the failure the
|
|
150
|
+
// resolver exists to stop: it answers `undefined`, which every caller here treats as "not
|
|
151
|
+
// provisioned", and sends an operator to a rotation for creds that are sitting on the same disk.
|
|
152
|
+
const observer = await source.secrets.get(membershipObserverCredsKey(source.space, source));
|
|
153
|
+
const evictor = need === "both" ? await source.secrets.get(connectionEvictorCredsKey(source.space, source)) : undefined;
|
|
154
|
+
// Reported as KINDS, never the segmented keys: `missing` is read by operators and compared
|
|
155
|
+
// against by callers (`membership.ts` filters the evictor out of its own diagnosis by name), and
|
|
156
|
+
// a `space.<hex>/` prefix would break both. The hosted arm's `put` key is named by `repairAdvice`.
|
|
157
|
+
const missing = [
|
|
158
|
+
observer === undefined ? MEMBERSHIP_OBSERVER_CREDS_KIND : undefined,
|
|
159
|
+
need === "both" && evictor === undefined ? CONNECTION_EVICTOR_CREDS_KIND : undefined,
|
|
160
|
+
].filter((k) => k !== undefined);
|
|
161
|
+
return { ...(observer !== undefined ? { observer } : {}), ...(evictor !== undefined ? { evictor } : {}), missing };
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=sys-creds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sys-creds.js","sourceRoot":"","sources":["../src/sys-creds.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/delivery",
|
|
3
3
|
"description": "Cotal delivery daemon: the server-side Plane-3 durable backstop (fan-out writer + trusted reader + membership/ACL authority), a scoped least-privilege NATS client co-located with the broker.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.36.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@cotal-ai/core": "0.
|
|
22
|
-
"@cotal-ai/workspace": "0.
|
|
21
|
+
"@cotal-ai/core": "0.36.0",
|
|
22
|
+
"@cotal-ai/workspace": "0.36.0"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|