@cotal-ai/workspace 0.16.0 → 0.18.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/pid.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * THE pid-attribution contract for machine-local pidfiles, in ONE place.
3
+ *
4
+ * Two copies of "parse a pid" + "is it alive" had drifted (a bounded parser in `auth-proc`, an
5
+ * unbounded `Number.isInteger` parser plus a two-state `isAlive` in `down`), and the gap between a
6
+ * guard's validity predicate and what `process.kill` actually accepts is exactly where a live
7
+ * process gets misread as dead and its pidfile deleted under a clean-stop report. One parser, one
8
+ * tri-state probe, so every surface that decides "is this record a live process, a dead one, or
9
+ * unattributable" decides it the same way.
10
+ *
11
+ * WHO CAN ACTUALLY CONSUME IT, because "consumed everywhere" was never reachable and claiming it
12
+ * hid the gap. This lives in `workspace` (machine-local operator tooling), the widest tier that may
13
+ * hold a local-process concept: the CLI, the manager, the auth service and the web surface all
14
+ * depend on it. `extensions/*` peer-depend `core` ONLY, and a pid probe is not a wire concept, so
15
+ * moving it into core to reach them would leak a local concern into the standard. The two
16
+ * extension-side probes (`connector-opencode`, `connector-hermes`) therefore keep their own copies
17
+ * BY CONSTRUCTION, not by oversight; if they need this contract, the fix is a shared local-process
18
+ * module they may depend on, never a core export.
19
+ */
20
+ /** A Node/POSIX-signalable pid: a positive INTEGER within the signed 32-bit range `process.kill`
21
+ * accepts (it throws `ERR_INVALID_ARG_TYPE`/`ERR_OUT_OF_RANGE` outside it). Anything else -
22
+ * fractional, non-numeric, non-positive, oversized - is undefined: UNATTRIBUTABLE, never a pid to
23
+ * probe or delete a record against. */
24
+ export function parsePid(raw) {
25
+ const n = Number(raw.trim());
26
+ return Number.isInteger(n) && n > 0 && n <= 0x7fffffff ? n : undefined;
27
+ }
28
+ /** The errno-to-state MAPPING, split out from the syscall so it can be tested exhaustively without
29
+ * an environment in the loop. The whole contract turns on one rule: only an actual `ESRCH` proves a
30
+ * process gone. `EPERM` (it exists, it is just another user's, so we cannot signal it) is ALIVE.
31
+ * Anything else - argument/range errors, unfamiliar errnos, a missing code - is UNKNOWN, never dead.
32
+ *
33
+ * This is exported because the old suite could only reach the EPERM rule by probing pid 1 and
34
+ * hoping the process was unprivileged, so as root or in some containers that cell SKIPPED and the
35
+ * suite still printed a passing banner: a wrong implementation reading green. A pure mapping has
36
+ * no fixture to skip. Found by review, not by me. */
37
+ export function livenessFromErrno(code) {
38
+ if (code === "ESRCH")
39
+ return "dead";
40
+ if (code === "EPERM")
41
+ return "alive"; // exists, just not ours to signal
42
+ return "unknown"; // ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE / anything else - cannot attribute
43
+ }
44
+ export function probeLiveness(pid) {
45
+ try {
46
+ process.kill(pid, 0);
47
+ return "alive";
48
+ }
49
+ catch (e) {
50
+ return livenessFromErrno(e.code);
51
+ }
52
+ }
53
+ //# sourceMappingURL=pid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pid.js","sourceRoot":"","sources":["../src/pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;wCAGwC;AACxC,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzE,CAAC;AAED;;;;;;;;sDAQsD;AACtD,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACxD,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,MAAM,CAAC;IACpC,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC,CAAC,kCAAkC;IACxE,OAAO,SAAS,CAAC,CAAC,6EAA6E;AACjG,CAAC;AAgBD,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,iBAAiB,CAAE,CAA2B,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -13,7 +13,12 @@ import type { MeshTarget } from "./mesh-target.js";
13
13
  /** The five distinct ways a preflight fails. Each also carries whether the target OWNS its registry
14
14
  * entry (→ prune): `fromRegistry` means the server+mode came from a registry record (incl. a
15
15
  * `local-recorded` project matched by root), so a definitive failure is a stale-entry signal. */
16
- export type PreflightFailure = "unreachable" | "registry-creds-rejected" | "registry-open-now-auth" | "creds-rejected" | "open-wants-auth" | "stale-auth";
16
+ export type PreflightFailure = "unreachable" | "registry-creds-rejected" | "registry-open-now-auth" | "creds-rejected" | "open-wants-auth" | "stale-auth"
17
+ /** A TLS-required NATS listener still greets (INFO.tls_required) but the TLS probe could not
18
+ * complete the handshake — typically a private CA without `NODE_EXTRA_CA_CERTS`. NEVER a prune
19
+ * signal: INFO is unauthenticated shape evidence, not peer identity; conservatively keep the
20
+ * record and repair client trust rather than re-register. */
21
+ | "tls-trust";
17
22
  /** Pure decision tree — separated from I/O so the whole branch tree is unit-testable (it's the
18
23
  * riskiest logic: a wrong branch prunes a LIVE registry entry). Only a registry-OWNED source
19
24
  * (`registry`/`current`/`flag-space`/`local-recorded`) is ever pruned. A non-registry source —
@@ -1 +1 @@
1
- {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;;;GAUG;AAEH;;kGAEkG;AAClG,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,yBAAyB,GACzB,wBAAwB,GACxB,gBAAgB,GAChB,iBAAiB,GACjB,YAAY,CAAC;AAEjB;;;;;oGAKoG;AACpG,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,EAC5B,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,aAAa,EACtD,OAAO,EAAE,OAAO,GACf;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAkB5C;AAED;;;uFAGuF;AACvF,wBAAsB,eAAe,CACnC,MAAM,EAAE,UAAU,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAuB/E;AA8BD;;+FAE+F;AAC/F,MAAM,WAAW,SAAS;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,SAAS,CAAC,CAW3D"}
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;;;GAUG;AAEH;;kGAEkG;AAClG,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,yBAAyB,GACzB,wBAAwB,GACxB,gBAAgB,GAChB,iBAAiB,GACjB,YAAY;AACd;;;8DAG8D;GAC5D,WAAW,CAAC;AAEhB;;;;;oGAKoG;AACpG,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,EAC5B,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,aAAa,EACtD,OAAO,EAAE,OAAO,GACf;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAkB5C;AAED;;;uFAGuF;AACvF,wBAAsB,eAAe,CACnC,MAAM,EAAE,UAAU,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CA4D/E;AAgFD;;+FAE+F;AAC/F,MAAM,WAAW,SAAS;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,SAAS,CAAC,CAW3D"}
package/dist/preflight.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createConnection } from "node:net";
1
2
  import { mintCreds, newIdentity, probeConnect, isReachable } from "@cotal-ai/core";
2
3
  import { loadMeshes, pruneMesh } from "./mesh-registry.js";
3
4
  /** Pure decision tree — separated from I/O so the whole branch tree is unit-testable (it's the
@@ -40,7 +41,18 @@ export async function preflightTarget(target, probeCreds) {
40
41
  if (target.mode === "user")
41
42
  throw new Error("preflightTarget: a user-mode target cannot be credless-probed - the caller owns the user connect (see preflightOrExit's user branch)");
42
43
  const creds = probeCreds ?? (target.auth ? await mintCreds(target.auth, newIdentity(), "probe") : undefined);
43
- const auth = creds ? { creds } : {};
44
+ // THE RECORDED TLS REQUIREMENT IS PART OF THE PROBE, not decoration on the record.
45
+ //
46
+ // Without it this probe connects to ANY broker on the recorded address, including a plaintext one
47
+ // substituted for the TLS broker the record describes — and reports `ok`. Two independent testers
48
+ // drove exactly that: record a TLS mesh, kill its broker, start a plaintext `nats-server` on the
49
+ // same port, and `cotal status` returned rc=0 and "connection ok".
50
+ //
51
+ // That is worse than a missing feature. A tool that is silent about a substitution is a gap; one
52
+ // that AFFIRMATIVELY REPORTS HEALTHY is a hazard, because the operator's rational response to a
53
+ // green check is to stop looking. The recorded requirement is the only thing that can tell those
54
+ // two brokers apart, since the plaintext one answers perfectly well.
55
+ const auth = { ...(creds ? { creds } : {}), ...(target.tlsRequired ? { tls: true } : {}) };
44
56
  let probe = await probeConnect(target.server, auth);
45
57
  if (probe.ok)
46
58
  return { ok: true };
@@ -55,9 +67,87 @@ export async function preflightTarget(target, probeCreds) {
55
67
  probe = await probeConnect(target.server, { ...auth, timeoutMs: PREFLIGHT_CONFIRM_TIMEOUT_MS });
56
68
  if (probe.ok)
57
69
  return { ok: true };
70
+ // TLS-REQUIRED + "unreachable" is often NOT a dead broker. `probeConnect` maps every non-auth
71
+ // failure (including certificate verification) to `unreachable`. Against a private-CA mesh
72
+ // without `NODE_EXTRA_CA_CERTS`, the TLS handshake fails while the broker is live and still
73
+ // greets with plaintext INFO advertising `tls_required`. Classifying that as unreachable + prune
74
+ // DELETES a healthy tlsRequired registry entry — durable state destroyed on a recoverable trust
75
+ // error. This branch introduced tlsRequired meshes, so the mis-prune is in scope.
76
+ //
77
+ // Discriminator must be stronger than bare liveness. A *different* broker on the recorded port
78
+ // (the classic plaintext-substitute case this file's own comment documents) also answers INFO —
79
+ // bare `isReachable` alone would mis-label that as tls-trust and keep a stale record. Only an
80
+ // INFO that still advertises `tls_required: true` is shape evidence consistent with the record
81
+ // (a TLS-required NATS listener, not a plaintext substitute). INFO is unauthenticated and is NOT
82
+ // peer identity. Anything else falls through to the normal unreachable/prune path.
83
+ //
84
+ // CONFIRM BEFORE CONDEMNING applies here too: a single 1s INFO read on a slow/jittery link would
85
+ // miss a live TLS greeting and fall through to prune — recreating the S10 data-loss under load.
86
+ // First miss only makes it a candidate; a second, longer read must also miss before we prune.
87
+ if (target.tlsRequired && probe.reason === "unreachable") {
88
+ const info = (await readNatsInfoGreeting(target.server)) ??
89
+ (await readNatsInfoGreeting(target.server, PRUNE_CONFIRM_TIMEOUT_MS));
90
+ if (info?.tls_required === true)
91
+ return { ok: false, kind: "tls-trust", prune: false };
92
+ }
58
93
  const { prune, kind } = classifyPreflightFailure(target.source, probe.reason, Boolean(target.auth));
59
94
  return { ok: false, kind, prune };
60
95
  }
96
+ /** Read the pre-auth NATS INFO greeting (plaintext, before any STARTTLS). Returns null if nothing
97
+ * usable answered. Used to distinguish a TLS-required NATS listener from a plaintext substitute
98
+ * without a TLS handshake. It does NOT establish mesh identity — INFO is unauthenticated. */
99
+ async function readNatsInfoGreeting(server, timeoutMs = 1_000) {
100
+ let host;
101
+ let port;
102
+ try {
103
+ const u = new URL(server.includes("://") ? server : `nats://${server}`);
104
+ host = u.hostname;
105
+ port = u.port ? Number(u.port) : 4222;
106
+ if (!host || !Number.isFinite(port))
107
+ return null;
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ return new Promise((resolve) => {
113
+ const sock = createConnection({ host, port });
114
+ let buf = "";
115
+ let done = false;
116
+ const finish = (v) => {
117
+ if (done)
118
+ return;
119
+ done = true;
120
+ try {
121
+ sock.destroy();
122
+ }
123
+ catch { /* */ }
124
+ resolve(v);
125
+ };
126
+ sock.setTimeout(timeoutMs, () => finish(null));
127
+ sock.on("error", () => finish(null));
128
+ sock.on("close", () => finish(null));
129
+ sock.on("data", (chunk) => {
130
+ buf += chunk.toString("utf8");
131
+ const nl = buf.indexOf("\r\n");
132
+ if (nl < 0) {
133
+ if (buf.length > 4096)
134
+ finish(null);
135
+ return;
136
+ }
137
+ const line = buf.slice(0, nl);
138
+ const brace = line.indexOf("{");
139
+ if (!/^INFO\b/.test(line) || brace < 0)
140
+ return finish(null);
141
+ try {
142
+ const j = JSON.parse(line.slice(brace));
143
+ finish(j);
144
+ }
145
+ catch {
146
+ finish(null);
147
+ }
148
+ });
149
+ });
150
+ }
61
151
  /**
62
152
  * Drop registry entries whose broker is gone — a `cotal up` that crashed or was `kill -9`'d without
63
153
  * `cotal down` leaves a record behind. This is liveness-only and we hold NO creds for these meshes,
@@ -1 +1 @@
1
- {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preflight.js","sourceRoot":"","sources":["../src/preflight.ts"],"names":[],"mappings":"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"}
package/dist/render.d.ts CHANGED
@@ -23,6 +23,7 @@ export type WorkspaceError = {
23
23
  kind: "reachable";
24
24
  reason: "auth-required" | "stale-auth" | "unreachable";
25
25
  server: string;
26
+ hasAuth: boolean;
26
27
  };
27
28
  /** Render a workspace failure as the canonical one-line `cotal …` sentence. */
28
29
  export declare function renderWorkspaceError(e: WorkspaceError): string;
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACrF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElG,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAS9D"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACrF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,YAAY,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpH,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAS9D"}
package/dist/render.js CHANGED
@@ -6,7 +6,7 @@ export function renderWorkspaceError(e) {
6
6
  case "preflight":
7
7
  return renderPreflightFailure(e.failure, e.target, e.pruned);
8
8
  case "reachable":
9
- return renderReachable(e.reason, e.server);
9
+ return renderReachable(e.reason, e.server, e.hasAuth);
10
10
  }
11
11
  }
12
12
  /** "Which mesh" resolution failures — maps a {@link MeshTargetError}'s `{code, details}` to copy. */
@@ -62,15 +62,26 @@ function renderPreflightFailure(kind, t, pruned) {
62
62
  return `✗ broker at ${t.server} requires auth, but this mesh is open (no trust material) - use \`--space <name>\` for an auth mesh, or run \`cotal up\` here without \`--open\``;
63
63
  case "stale-auth":
64
64
  return `✗ credentials for "${t.space}" have EXPIRED - this mesh enforces bounded credential lifetimes; run \`cotal doctor auth\` in ${t.root} for the diagnosis + exact repair (the mesh itself is up)`;
65
+ case "tls-trust":
66
+ // INFO advertised tls_required (shape evidence only — unauthenticated). Never claims removal
67
+ // or peer identity; conservatively keep the record and point at the trust-store repair.
68
+ return `✗ mesh "${t.space}" at ${t.server} requires TLS but this client could not complete the handshake (untrusted or missing CA?) - set \`NODE_EXTRA_CA_CERTS\` to the issuing CA for a private CA, or fix the trust store; a TLS-required NATS listener still greets there (INFO is unauthenticated — not mesh identity) so the registry entry was conservatively kept`;
65
69
  }
66
70
  }
67
71
  /** Plain reachability for a RAW (off-registry) probe — the `--creds` / `--server`+unregistered-`--space`
68
72
  * escape hatch, which never touches the registry (no prune, no stale-entry wording). */
69
- function renderReachable(reason, server) {
73
+ function renderReachable(reason, server, hasAuth) {
70
74
  if (reason === "stale-auth")
71
75
  return `✗ credential EXPIRED at ${server} - bounded lifetime reached (credential death); run \`cotal doctor auth\` where the mesh runs, or re-mint the credential`;
72
- return reason === "auth-required"
76
+ if (reason === "unreachable")
77
+ return `✗ can't reach a broker at ${server} - is it running? (\`cotal up\`)`;
78
+ // `auth-required` is two different user situations with two different repairs, and this path used
79
+ // to collapse them. The registry path never did: `classifyPreflightFailure` branches on the same
80
+ // bit into `creds-rejected` vs `open-wants-auth`. Told "credentials rejected" after sending NO
81
+ // credentials, a caller goes looking for what is wrong with creds they never had, when the actual
82
+ // next step is to obtain some.
83
+ return hasAuth
73
84
  ? `✗ credentials rejected at ${server} - check your creds, or the broker wants different auth`
74
- : `✗ can't reach a broker at ${server} - is it running? (\`cotal up\`)`;
85
+ : `✗ broker at ${server} requires auth, but no credentials were supplied - pass \`--creds <file>\`, or \`cotal join\` with a link/token`;
75
86
  }
76
87
  //# sourceMappingURL=render.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { type SpaceAuth } from "@cotal-ai/core";
2
+ /**
3
+ * The class-3 ($SYS) renewal owner's half, the counterpart to `renewal.ts`, which owns the class-2
4
+ * standing renewal and deliberately EXCLUDES these two files.
5
+ *
6
+ * `membership-observer.creds` and `connection-evictor.creds` are `rotation-renewed`: they are signed
7
+ * by the system-account seed, which is never persisted (`putSpaceAuth` strips it), so no running
8
+ * process can re-sign them for their existing identity the way `remintDaemonCreds` does. Their only
9
+ * renewal is a system-account ROTATION: a fresh $SYS account under the SAME broker operator, fresh
10
+ * creds minted from its in-memory seed, and a broker that reloads the new operator/system JWTs.
11
+ *
12
+ * That rotation is NOT destructive. `rotateSystemAccount` re-signs the operator JWT with a new
13
+ * `system_account` using the operator's own (unchanged) seed, so the space's DATA account, its
14
+ * signing key, every agent credential minted from it, and the JetStream store all survive untouched.
15
+ * What dies is exactly what should: the retired system account and any copy of the old $SYS creds.
16
+ *
17
+ * The one thing this module cannot do is make the broker load the result. Rotation is only complete
18
+ * once the broker restarts on a `server.conf` rendered from the rotated record, which is why the
19
+ * operator surface is `cotal up --rotate-sys` (the one command that already renders that config and
20
+ * boots the broker + daemons from it) rather than a standalone verb that would leave the mesh in a
21
+ * half-rotated state.
22
+ *
23
+ * WORKSTATION ONLY, deliberately: unlike `renewal.ts`, nothing here takes a {@link SecretStore}. The
24
+ * $SYS pair lives on the raw FS with no store seam, and the multi-tenant guard below reads the FS
25
+ * account records, which an injected store could neither supply nor be enumerated for. See
26
+ * {@link rotateSystemCreds}.
27
+ */
28
+ /** The two $SYS credential files, by the same key↔filename convention `renewal.ts` uses. They stay on
29
+ * the raw FS, with no secret-store seam: renewing them means rewriting the broker config too, so the
30
+ * pair and the trust record move together or not at all, which is not something a store can hold half
31
+ * of. Named here so the rotation writer, the staleness check and `cotal clean`'s removal list cannot
32
+ * drift apart. */
33
+ export declare const SYSTEM_CREDS_FILES: readonly ["membership-observer.creds", "connection-evictor.creds"];
34
+ export interface SystemRotationResult {
35
+ /** The broker record's new system-account generation (the successor discriminator `putSpaceAuth` guards). */
36
+ gen: number;
37
+ /** The rotated bundle. The caller MUST render `server.conf` from this, not from its pre-rotation copy. */
38
+ auth: SpaceAuth;
39
+ /** Expiry (epoch sec) of the freshly minted $SYS creds, so the caller can print the next rotation date. */
40
+ expiresAt?: number;
41
+ }
42
+ /**
43
+ * Rotate the space's system account and re-mint both $SYS creds against it.
44
+ *
45
+ * `expectedSpace` is validated by `getSpaceAuth` against the store's signer, the same cross-space
46
+ * guard `remintDaemonCreds` runs: rotating with a foreign space's operator would re-issue an operator
47
+ * JWT this broker does not trust and strand every account under it.
48
+ *
49
+ * Ordering is the safety property, and it is a DIRECTIONAL one, not atomicity. All MINTING (the
50
+ * rotation and both creds) happens in memory before anything is persisted, so a mint failure changes
51
+ * nothing on disk. Then the trust record commits, where `putSpaceAuth`'s generation guard refuses a
52
+ * stale or non-successor write. Then the creds land. What this buys is a single failure direction: a
53
+ * cred file is never overwritten for a system account the record does not already carry, because the
54
+ * inverse order could clobber the last-good creds with creds for an authority the broker will never
55
+ * load, which is the availability loss `remintDaemonCreds` guards on its own class.
56
+ *
57
+ * It does NOT make the persistence atomic. `putSpaceAuth` is two puts and the creds are two more, so
58
+ * a crash leaves the record AHEAD of the creds. That state is detected rather than prevented (see
59
+ * {@link staleSystemCreds}) and is repaired by re-running the rotation.
60
+ *
61
+ * THROWS rather than degrading: a stripped signer (no operator seed) cannot rotate, and a caller that
62
+ * ignored the throw would boot a broker whose config still carries the retired system account.
63
+ */
64
+ export declare function rotateSystemCreds(root: string, expectedSpace: string): Promise<SystemRotationResult>;
65
+ /** A $SYS credential file on disk that the persisted trust record does NOT authorize. */
66
+ export interface StaleSystemCred {
67
+ file: string;
68
+ /** The retired system account that signed it; absent when the file could not be parsed. */
69
+ iss?: string;
70
+ }
71
+ /**
72
+ * The $SYS cred files whose issuer is not `sysPub`: the ONE staleness question, asked in one place
73
+ * so every surface answers it identically.
74
+ *
75
+ * This exists because expiry cannot see the failure. A rotation that committed the trust record and
76
+ * then died leaves credential files that parse, are nowhere near their expiry, and are refused by the
77
+ * broker, because the account that signed them no longer exists as far as the successor config is
78
+ * concerned. Comparing the two files to each other is not enough either: a crash BEFORE either write
79
+ * leaves them stale but mutually consistent. Only the persisted record settles it, which is why this
80
+ * takes `sysPub` and why the callers are the two surfaces that hold it: the boot path, which warns
81
+ * before it renders a config the files cannot serve, and `cotal doctor auth`, which must never call
82
+ * this state healthy. The delivery daemon is deliberately NOT a caller: it never loads the signer.
83
+ *
84
+ * Absent files are not stale (an unprovisioned space is a different, separately reported state), and
85
+ * an unreadable file is reported with no `iss` rather than throwing, because a diagnosis surface must not
86
+ * crash on the corruption it exists to describe.
87
+ */
88
+ export declare function staleSystemCreds(root: string, sysPub: string): StaleSystemCred[];
89
+ //# sourceMappingURL=system-rotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-rotation.d.ts","sourceRoot":"","sources":["../src/system-rotation.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;mBAImB;AACnB,eAAO,MAAM,kBAAkB,oEAAqE,CAAC;AAErG,MAAM,WAAW,oBAAoB;IACnC,6GAA6G;IAC7G,GAAG,EAAE,MAAM,CAAC;IACZ,0GAA0G;IAC1G,IAAI,EAAE,SAAS,CAAC;IAChB,2GAA2G;IAC3G,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAkD1G;AAED,yFAAyF;AACzF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,EAAE,CAehF"}
@@ -0,0 +1,142 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { credsClaims, mintConnectionEvictorCreds, mintMembershipObserverCreds, newIdentity, rotateSystemAccount, writeSecretFileAtomic, } from "@cotal-ai/core";
4
+ import { assertSingleSpaceBroker, authDir, getSpaceAuth, putSpaceAuth } from "./auth-paths.js";
5
+ import { workspaceSecretStore } from "./secret-store-fs.js";
6
+ /**
7
+ * The class-3 ($SYS) renewal owner's half, the counterpart to `renewal.ts`, which owns the class-2
8
+ * standing renewal and deliberately EXCLUDES these two files.
9
+ *
10
+ * `membership-observer.creds` and `connection-evictor.creds` are `rotation-renewed`: they are signed
11
+ * by the system-account seed, which is never persisted (`putSpaceAuth` strips it), so no running
12
+ * process can re-sign them for their existing identity the way `remintDaemonCreds` does. Their only
13
+ * renewal is a system-account ROTATION: a fresh $SYS account under the SAME broker operator, fresh
14
+ * creds minted from its in-memory seed, and a broker that reloads the new operator/system JWTs.
15
+ *
16
+ * That rotation is NOT destructive. `rotateSystemAccount` re-signs the operator JWT with a new
17
+ * `system_account` using the operator's own (unchanged) seed, so the space's DATA account, its
18
+ * signing key, every agent credential minted from it, and the JetStream store all survive untouched.
19
+ * What dies is exactly what should: the retired system account and any copy of the old $SYS creds.
20
+ *
21
+ * The one thing this module cannot do is make the broker load the result. Rotation is only complete
22
+ * once the broker restarts on a `server.conf` rendered from the rotated record, which is why the
23
+ * operator surface is `cotal up --rotate-sys` (the one command that already renders that config and
24
+ * boots the broker + daemons from it) rather than a standalone verb that would leave the mesh in a
25
+ * half-rotated state.
26
+ *
27
+ * WORKSTATION ONLY, deliberately: unlike `renewal.ts`, nothing here takes a {@link SecretStore}. The
28
+ * $SYS pair lives on the raw FS with no store seam, and the multi-tenant guard below reads the FS
29
+ * account records, which an injected store could neither supply nor be enumerated for. See
30
+ * {@link rotateSystemCreds}.
31
+ */
32
+ /** The two $SYS credential files, by the same key↔filename convention `renewal.ts` uses. They stay on
33
+ * the raw FS, with no secret-store seam: renewing them means rewriting the broker config too, so the
34
+ * pair and the trust record move together or not at all, which is not something a store can hold half
35
+ * of. Named here so the rotation writer, the staleness check and `cotal clean`'s removal list cannot
36
+ * drift apart. */
37
+ export const SYSTEM_CREDS_FILES = ["membership-observer.creds", "connection-evictor.creds"];
38
+ /**
39
+ * Rotate the space's system account and re-mint both $SYS creds against it.
40
+ *
41
+ * `expectedSpace` is validated by `getSpaceAuth` against the store's signer, the same cross-space
42
+ * guard `remintDaemonCreds` runs: rotating with a foreign space's operator would re-issue an operator
43
+ * JWT this broker does not trust and strand every account under it.
44
+ *
45
+ * Ordering is the safety property, and it is a DIRECTIONAL one, not atomicity. All MINTING (the
46
+ * rotation and both creds) happens in memory before anything is persisted, so a mint failure changes
47
+ * nothing on disk. Then the trust record commits, where `putSpaceAuth`'s generation guard refuses a
48
+ * stale or non-successor write. Then the creds land. What this buys is a single failure direction: a
49
+ * cred file is never overwritten for a system account the record does not already carry, because the
50
+ * inverse order could clobber the last-good creds with creds for an authority the broker will never
51
+ * load, which is the availability loss `remintDaemonCreds` guards on its own class.
52
+ *
53
+ * It does NOT make the persistence atomic. `putSpaceAuth` is two puts and the creds are two more, so
54
+ * a crash leaves the record AHEAD of the creds. That state is detected rather than prevented (see
55
+ * {@link staleSystemCreds}) and is repaired by re-running the rotation.
56
+ *
57
+ * THROWS rather than degrading: a stripped signer (no operator seed) cannot rotate, and a caller that
58
+ * ignored the throw would boot a broker whose config still carries the retired system account.
59
+ */
60
+ export async function rotateSystemCreds(root, expectedSpace) {
61
+ // A rotation is BROKER-wide, not space-wide, however it is spelled: the system account lives in
62
+ // the shared broker record and the re-issued operator JWT names the successor for every space
63
+ // under it, while the two $SYS cred files are per-ROOT (one pair, its observer permissions pinned
64
+ // to ONE data account). So on a multi-tenant root, "rotate space A" would retire space B's system
65
+ // account and leave no cred that can observe B. Same guard, same reason, as `cotal down`,
66
+ // `cotal backup`, `cotal clean` and `cotal up --restore`.
67
+ //
68
+ // The guard reads the FS account records, and that is exactly why this function takes NO
69
+ // SecretStore. `SecretStore` cannot enumerate, so an injected multi-tenant store paired with an
70
+ // empty local root would sail past this check and retire the system account for every tenant in
71
+ // it: a guard that looks broker-wide while enforcing nothing. Taking the store away makes the
72
+ // mismatch impossible to express rather than merely refused at runtime, and it costs nothing real:
73
+ // the $SYS pair is FS-only anyway (a hosted composition has nowhere in the store to put it), so
74
+ // this operation was never store-composable to begin with. If store enumeration ever exists, this
75
+ // becomes a real hosted seam; until then it is a workstation operation and says so.
76
+ assertSingleSpaceBroker(authDir(root), "a system-account rotation (`cotal up --rotate-sys`)");
77
+ const s = workspaceSecretStore(root);
78
+ const auth = await getSpaceAuth(s, expectedSpace);
79
+ if (!auth)
80
+ throw new Error(`rotateSystemCreds: no trust record for space "${expectedSpace}" - there is no system account to rotate`);
81
+ if (!auth.operator.seed)
82
+ throw new Error("rotateSystemCreds: the broker operator seed is required to rotate the system account (this root holds a stripped signer) - run the rotation where the broker trust record lives");
83
+ // In-memory first: the rotation and BOTH mints must succeed before anything is persisted.
84
+ const rotated = await rotateSystemAccount(auth);
85
+ const observer = await mintMembershipObserverCreds(rotated, newIdentity());
86
+ const evictor = await mintConnectionEvictorCreds(rotated, newIdentity());
87
+ // Commit the trust record FIRST: the generation guard lives in this call, and it is what makes the
88
+ // successor real. Only then the creds that record authorizes.
89
+ //
90
+ // This is NOT one atomic act, and the code must not pretend otherwise: `putSpaceAuth` is itself two
91
+ // puts, and the two cred writes are two more. A crash anywhere in this sequence leaves the record
92
+ // AHEAD of the creds: both files stale (crash before either write) or one stale (crash between
93
+ // them). The window is not closed here; it is DETECTED, by {@link staleSystemCreds}, which every
94
+ // boot and every `doctor auth` runs against the persisted record. It cannot be repaired in place
95
+ // either: the successor's signing seed is gone the moment it is persisted, so the only repair is
96
+ // another rotation, which is idempotent and costs one generation. A durable rotation journal would
97
+ // close the window instead of reporting it; that is deliberately not built for a display-only feed
98
+ // plus an eviction rail whose recovery is one re-run.
99
+ //
100
+ // Each file is still written atomically, so no reader can ever see a half-written credential.
101
+ await putSpaceAuth(s, rotated);
102
+ writeSecretFileAtomic(join(root, ".cotal", SYSTEM_CREDS_FILES[0]), observer);
103
+ writeSecretFileAtomic(join(root, ".cotal", SYSTEM_CREDS_FILES[1]), evictor);
104
+ return { gen: rotated.gen ?? 0, auth: rotated, expiresAt: credsClaims(observer).exp };
105
+ }
106
+ /**
107
+ * The $SYS cred files whose issuer is not `sysPub`: the ONE staleness question, asked in one place
108
+ * so every surface answers it identically.
109
+ *
110
+ * This exists because expiry cannot see the failure. A rotation that committed the trust record and
111
+ * then died leaves credential files that parse, are nowhere near their expiry, and are refused by the
112
+ * broker, because the account that signed them no longer exists as far as the successor config is
113
+ * concerned. Comparing the two files to each other is not enough either: a crash BEFORE either write
114
+ * leaves them stale but mutually consistent. Only the persisted record settles it, which is why this
115
+ * takes `sysPub` and why the callers are the two surfaces that hold it: the boot path, which warns
116
+ * before it renders a config the files cannot serve, and `cotal doctor auth`, which must never call
117
+ * this state healthy. The delivery daemon is deliberately NOT a caller: it never loads the signer.
118
+ *
119
+ * Absent files are not stale (an unprovisioned space is a different, separately reported state), and
120
+ * an unreadable file is reported with no `iss` rather than throwing, because a diagnosis surface must not
121
+ * crash on the corruption it exists to describe.
122
+ */
123
+ export function staleSystemCreds(root, sysPub) {
124
+ const stale = [];
125
+ for (const file of SYSTEM_CREDS_FILES) {
126
+ const path = join(root, ".cotal", file);
127
+ if (!existsSync(path))
128
+ continue;
129
+ let iss;
130
+ try {
131
+ iss = credsClaims(readFileSync(path, "utf8")).iss;
132
+ }
133
+ catch {
134
+ stale.push({ file }); // unreadable: cannot be shown to match, so it is not assumed to
135
+ continue;
136
+ }
137
+ if (iss !== sysPub)
138
+ stale.push({ file, iss });
139
+ }
140
+ return stale;
141
+ }
142
+ //# sourceMappingURL=system-rotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-rotation.js","sourceRoot":"","sources":["../src/system-rotation.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/workspace",
3
3
  "description": "Cotal machine-local workstation layer: the ~/.cotal mesh registry, target resolution, preflight, on-disk auth-path I/O, and the command-copy renderer — operator concerns over a local checkout, kept separate from the wire protocol in @cotal-ai/core.",
4
- "version": "0.16.0",
4
+ "version": "0.18.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  }
19
19
  },
20
20
  "dependencies": {
21
- "@cotal-ai/core": "0.16.0"
21
+ "@cotal-ai/core": "0.18.0"
22
22
  },
23
23
  "files": [
24
24
  "dist"