@cotal-ai/workspace 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/auth-paths.d.ts +180 -18
  2. package/dist/auth-paths.d.ts.map +1 -1
  3. package/dist/auth-paths.js +739 -31
  4. package/dist/auth-paths.js.map +1 -1
  5. package/dist/connect.js +3 -3
  6. package/dist/connect.js.map +1 -1
  7. package/dist/extension-mutation.d.ts +27 -0
  8. package/dist/extension-mutation.d.ts.map +1 -0
  9. package/dist/extension-mutation.js +229 -0
  10. package/dist/extension-mutation.js.map +1 -0
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/local-process.d.ts +2 -1
  16. package/dist/local-process.d.ts.map +1 -1
  17. package/dist/local-process.js +37 -3
  18. package/dist/local-process.js.map +1 -1
  19. package/dist/materialize.d.ts.map +1 -1
  20. package/dist/materialize.js +5 -5
  21. package/dist/materialize.js.map +1 -1
  22. package/dist/mesh-registry.d.ts +3 -1
  23. package/dist/mesh-registry.d.ts.map +1 -1
  24. package/dist/mesh-registry.js +59 -6
  25. package/dist/mesh-registry.js.map +1 -1
  26. package/dist/mesh-target.d.ts +1 -1
  27. package/dist/mesh-target.d.ts.map +1 -1
  28. package/dist/mesh-target.js +98 -30
  29. package/dist/mesh-target.js.map +1 -1
  30. package/dist/official-connectors.d.ts +12 -0
  31. package/dist/official-connectors.d.ts.map +1 -1
  32. package/dist/official-connectors.js +4 -0
  33. package/dist/official-connectors.js.map +1 -1
  34. package/dist/render.js +2 -0
  35. package/dist/render.js.map +1 -1
  36. package/dist/renewal.d.ts +43 -13
  37. package/dist/renewal.d.ts.map +1 -1
  38. package/dist/renewal.js +85 -21
  39. package/dist/renewal.js.map +1 -1
  40. package/dist/space.d.ts +4 -2
  41. package/dist/space.d.ts.map +1 -1
  42. package/dist/space.js +6 -4
  43. package/dist/space.js.map +1 -1
  44. package/package.json +2 -2
package/dist/renewal.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { identityFromCreds, mintCreds, writeSecretFile, } from "@cotal-ai/core";
4
- import { authDir, loadSpaceAuth } from "./auth-paths.js";
3
+ import { credsClaims, credsFingerprint, identityFromCreds, mintCreds, writeSecretFile, } from "@cotal-ai/core";
4
+ import { getSpaceAuth } from "./auth-paths.js";
5
5
  import { workspaceSecretStore } from "./secret-store-fs.js";
6
6
  /**
7
7
  * D5 slice 5 class-2 standing renewal — the RENEWAL OWNER'S half, shared by the manager (the
8
8
  * resident owner in every mesh mode) and `cotal doctor auth --fix` (the operator repair). The
9
- * daemon's half is the explicit `reloadCreds` adoption op on the delivery-admin rail (plus the
10
- * passive 75% source re-read as backstop). Machine-local file work lives here in the workspace
11
- * layer: implementations never import each other.
9
+ * daemon's half is the explicit `reloadCreds` adoption op on the delivery-admin rail, plus each
10
+ * daemon's own 75% renewal timer (the delivery endpoint re-reads its source; the membership feed
11
+ * preflight-proves each candidate). Machine-local file work lives here in the workspace layer:
12
+ * implementations never import each other.
12
13
  */
13
14
  /** The canonical secret-store key of the delivery daemon's scoped cred — by the FS convention the
14
15
  * key IS the filename under `.cotal/`. The single source for every tier (the CLI writer, the
@@ -16,31 +17,65 @@ import { workspaceSecretStore } from "./secret-store-fs.js";
16
17
  * the kind across two store entries with no compile error. Lives in workspace because the
17
18
  * key↔filename convention is the workspace layout's; implementations never import each other. */
18
19
  export const DELIVERY_CREDS_KEY = "delivery.creds";
20
+ /** The membership feed's data-account rw cred key — the same key↔filename discipline as
21
+ * {@link DELIVERY_CREDS_KEY}. Named (not a bare literal) so the renewal owner can map a remint
22
+ * result back to the daemon's `membership` component without a hand-copied string. */
23
+ export const MEMBERSHIP_RW_CREDS_KEY = "membership-rw.creds";
19
24
  /** The seed-less daemon creds files a renewal owner re-signs. The $SYS files
20
25
  * (membership-observer, connection-evictor) are deliberately ABSENT: they are rotation-renewed —
21
26
  * no persisted seed can re-sign them, by design. */
22
27
  export const REMINTABLE_DAEMON_CREDS = [
23
28
  { file: DELIVERY_CREDS_KEY, profile: "delivery" },
24
- { file: "membership-rw.creds", profile: "membership-rw" },
29
+ { file: MEMBERSHIP_RW_CREDS_KEY, profile: "membership-rw" },
25
30
  ];
26
31
  /** Re-sign the daemon creds files for their EXISTING nkeys (a renewal must never swap a daemon's
27
32
  * identity — the daemon side pins it). Reads and writes through the secret-store seam; `store` is
28
33
  * the renewal owner's injection point, SYMMETRIC with the daemon's `runDelivery(args, store?)`.
29
34
  * The manager — the D5 standing-renewal owner, and a hosted-path caller (manager.ts calls this
30
- * unconditionally) — must pass the SAME store it gives the daemon, or a hosted composition
31
- * re-signs into one store while the daemon reads another and rides to expiry. Threading the store
32
- * through the manager's entry is its own slice; until it lands, hosted end-to-end renewal on an
33
- * injected store is NOT yet wired, and no store means the local workspace FS composition (keys =
34
- * the filenames under `.cotal/`). The store's ATOMIC put is load-bearing here, because the daemons
35
- * re-read these files LIVE (the delivery endpoint's 75% source backstop, the membership rw
36
- * reconnect getter) and the plain `writeSecretFile` this replaced could tear such a concurrent
37
- * re-read. Structured per-file results, never throws: a failed remint leaves the old cred running
38
- * toward its loud expiry and the caller records/reports the failure. */
39
- export async function remintDaemonCreds(root, store) {
40
- const auth = loadSpaceAuth(authDir(root));
35
+ * unconditionally) — passes the SAME store it gives the daemon (its `ManagerOptions.secretStore`),
36
+ * so a hosted composition re-signs into the store the daemon renews from, never a divergent one; no
37
+ * store means the local workspace FS composition (keys = the filenames under `.cotal/`).
38
+ *
39
+ * `expectedSpace` (the caller's known space the manager's `this.space`, doctor's resolved space) is
40
+ * a REQUIRED positional and is validated against the store's signer: the daemon creds are SPACE-SCOPED,
41
+ * so a store whose signer is for a DIFFERENT space (a swap after start, a misconfigured hosted mount)
42
+ * must NOT re-sign that would overwrite each last-good cred with one the space's broker rejects,
43
+ * breaking the daemon on a value that looks freshly renewed. It is required (not optional) so the
44
+ * unsafe no-space call cannot compile — the same claim-exceeds-enforcement trap the cross-space fix
45
+ * would otherwise leave for the next caller. A signer that fails validation fails EVERY file
46
+ * (`ok:false`), leaving the standing creds intact toward their loud expiry rather than clobbering them.
47
+ * The store's ATOMIC put is load-bearing here, because the daemons re-read these values LIVE (the
48
+ * delivery endpoint's 75% source refresh, the membership feed's 75% renewal fetch).
49
+ *
50
+ * The last-good cred is NEVER overwritten with an UNPROVEN one — a bundle's JWT chain is self-bound,
51
+ * not broker-bound (two `createSpaceAuth(space)` calls yield same-named, DIFFERENT-account chains), so
52
+ * a same-label alternate signer could mint a broker-dead cred and CLOBBER the last-good (availability
53
+ * loss). Proof before overwrite is one of: (1) `opts.preflight` — a disposable "does the broker accept
54
+ * this cred" probe the caller owns (the manager passes a {@link probeConnect} over `this.servers`),
55
+ * which gates EVERY candidate when supplied; or (2) AUTHORITY CONTINUITY — the candidate is signed by
56
+ * the SAME account signing key (`iss`) as the current (last-good, already broker-accepted) cred, the
57
+ * same authority the broker trusts, which the OFFLINE local repair (`doctor auth --fix`) relies on. A
58
+ * same-label alternate account breaks continuity, so with no preflight it is refused, not clobbered.
59
+ *
60
+ * Structured per-file results, never throws: a failed remint leaves the old cred running toward its
61
+ * loud expiry and the caller records/reports the failure. */
62
+ export async function remintDaemonCreds(root, expectedSpace, store, opts) {
63
+ const s = store ?? workspaceSecretStore(root);
64
+ // Read + FULLY VALIDATE the SIGNER through the SAME store the daemon creds live in (symmetric with
65
+ // the daemon's `runDelivery(args, store)`) — reading it from the FS while writing the daemon creds
66
+ // to an injected store would split authority (signer ← disk, cred ← KMS): the class 3b closed for
67
+ // daemon creds, here for the signer. `getSpaceAuth(s, expectedSpace)` cross-checks the trust chain
68
+ // against the caller's space; a wrong-space or malformed signer THROWS (never re-signs).
69
+ let auth;
70
+ try {
71
+ auth = await getSpaceAuth(s, expectedSpace);
72
+ }
73
+ catch (e) {
74
+ // Wrong-space / malformed signer: fail every file, DO NOT overwrite the last-good creds.
75
+ return REMINTABLE_DAEMON_CREDS.map(({ file }) => ({ file, ok: false, error: e.message }));
76
+ }
41
77
  if (!auth)
42
78
  return REMINTABLE_DAEMON_CREDS.map(({ file }) => ({ file, ok: false, skipped: "no-auth" }));
43
- const s = store ?? workspaceSecretStore(root);
44
79
  const results = [];
45
80
  for (const { file, profile } of REMINTABLE_DAEMON_CREDS) {
46
81
  try {
@@ -49,8 +84,33 @@ export async function remintDaemonCreds(root, store) {
49
84
  results.push({ file, ok: false, skipped: "missing-file" });
50
85
  continue;
51
86
  }
52
- await s.put(file, await mintCreds(auth, identityFromCreds(current), profile));
53
- results.push({ file, ok: true });
87
+ const next = await mintCreds(auth, identityFromCreds(current), profile);
88
+ // NEVER overwrite the last-good cred with an UNPROVEN one. A bundle's JWT chain is SELF-bound,
89
+ // not broker-bound (two `createSpaceAuth(space)` calls yield same-named, DIFFERENT-account chains),
90
+ // so `expectedSpace` + full-chain validity is not enough. Proof is one of:
91
+ // (1) a broker PREFLIGHT — the manager's live "does the broker accept this cred" probe; OR
92
+ // (2) AUTHORITY CONTINUITY — the candidate is signed by the SAME account signing key (`iss`) as
93
+ // the current (last-good, already broker-accepted) cred, i.e. the same authority the broker
94
+ // already trusts. This is what the OFFLINE local repair (`doctor auth --fix`, no live broker)
95
+ // relies on. A same-label ALTERNATE account (full OR stripped) breaks continuity, so with no
96
+ // preflight it is REFUSED — the last-good is preserved rather than clobbered by a broker-dead cred.
97
+ let proven;
98
+ let why;
99
+ if (opts?.preflight) {
100
+ proven = await opts.preflight(next);
101
+ why = "the broker refused the re-signed cred (wrong-account or unreachable signer)";
102
+ }
103
+ else {
104
+ const iss = credsClaims(next).iss;
105
+ proven = iss !== undefined && iss === credsClaims(current).iss;
106
+ why = "the re-signed cred's signer is not the last-good cred's authority, and no broker preflight was supplied";
107
+ }
108
+ if (!proven) {
109
+ results.push({ file, ok: false, error: `${why} - last-good cred preserved` });
110
+ continue;
111
+ }
112
+ await s.put(file, next);
113
+ results.push({ file, ok: true, fingerprint: credsFingerprint(next) });
54
114
  }
55
115
  catch (e) {
56
116
  results.push({ file, ok: false, error: e.message });
@@ -62,7 +122,11 @@ export function renewalRecordPath(root) {
62
122
  return join(root, ".cotal", "renewal.json");
63
123
  }
64
124
  export function writeRenewalRecord(root, record) {
65
- writeSecretFile(renewalRecordPath(root), JSON.stringify(record, null, 2));
125
+ // REDACT the ephemeral generation token HERE, at the single persistence boundary, so no writer
126
+ // (the manager, `doctor auth --fix`, or any future caller) can leak the stable secret-derived
127
+ // fingerprint to `.cotal/renewal.json`. `JSON.stringify` then omits the `undefined` field.
128
+ const redacted = { ...record, results: record.results.map((r) => ({ ...r, fingerprint: undefined })) };
129
+ writeSecretFile(renewalRecordPath(root), JSON.stringify(redacted, null, 2));
66
130
  }
67
131
  export function readRenewalRecord(root) {
68
132
  const p = renewalRecordPath(root);
@@ -1 +1 @@
1
- {"version":3,"file":"renewal.js","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"renewal.js","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"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"}
package/dist/space.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** The space this folder operates on: its `.cotal/auth` space if set up, else the default.
2
- * A folder has exactly one space (its auth) commands resolve it through here so they always
3
- * match the folder's mesh instead of assuming the global default. */
2
+ * Commands resolve it through here so they always match the folder's mesh instead of assuming the
3
+ * global default. A root that has grown to hold SEVERAL space accounts makes this question
4
+ * ambiguous, and {@link soleSpaceOf} fails loud there rather than picking one - such a caller has
5
+ * to name its space (`--space`). */
4
6
  export declare function resolveSpace(cwd: string): string;
5
7
  //# sourceMappingURL=space.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"space.d.ts","sourceRoot":"","sources":["../src/space.ts"],"names":[],"mappings":"AAGA;;sEAEsE;AACtE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
1
+ {"version":3,"file":"space.d.ts","sourceRoot":"","sources":["../src/space.ts"],"names":[],"mappings":"AAGA;;;;qCAIqC;AACrC,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
package/dist/space.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { DEFAULT_SPACE } from "@cotal-ai/core";
2
- import { authDir, findCotalRoot, loadSpaceAuth } from "./auth-paths.js";
2
+ import { authDir, findCotalRoot, soleSpaceOf } from "./auth-paths.js";
3
3
  /** The space this folder operates on: its `.cotal/auth` space if set up, else the default.
4
- * A folder has exactly one space (its auth) commands resolve it through here so they always
5
- * match the folder's mesh instead of assuming the global default. */
4
+ * Commands resolve it through here so they always match the folder's mesh instead of assuming the
5
+ * global default. A root that has grown to hold SEVERAL space accounts makes this question
6
+ * ambiguous, and {@link soleSpaceOf} fails loud there rather than picking one - such a caller has
7
+ * to name its space (`--space`). */
6
8
  export function resolveSpace(cwd) {
7
- return loadSpaceAuth(authDir(findCotalRoot(cwd)))?.space ?? DEFAULT_SPACE;
9
+ return soleSpaceOf(authDir(findCotalRoot(cwd))) ?? DEFAULT_SPACE;
8
10
  }
9
11
  //# sourceMappingURL=space.js.map
package/dist/space.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"space.js","sourceRoot":"","sources":["../src/space.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAExE;;sEAEsE;AACtE,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,aAAa,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"space.js","sourceRoot":"","sources":["../src/space.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEtE;;;;qCAIqC;AACrC,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC;AACnE,CAAC"}
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.13.1",
4
+ "version": "0.14.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.13.1"
21
+ "@cotal-ai/core": "0.14.0"
22
22
  },
23
23
  "files": [
24
24
  "dist"