@cotal-ai/workspace 0.11.6 → 0.13.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/renewal.js CHANGED
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { identityFromCreds, mintCreds, writeSecretFile, } from "@cotal-ai/core";
4
4
  import { authDir, loadSpaceAuth } from "./auth-paths.js";
5
+ import { workspaceSecretStore } from "./secret-store-fs.js";
5
6
  /**
6
7
  * D5 slice 5 class-2 standing renewal — the RENEWAL OWNER'S half, shared by the manager (the
7
8
  * resident owner in every mesh mode) and `cotal doctor auth --fix` (the operator repair). The
@@ -9,30 +10,46 @@ import { authDir, loadSpaceAuth } from "./auth-paths.js";
9
10
  * passive 75% source re-read as backstop). Machine-local file work lives here in the workspace
10
11
  * layer: implementations never import each other.
11
12
  */
13
+ /** The canonical secret-store key of the delivery daemon's scoped cred — by the FS convention the
14
+ * key IS the filename under `.cotal/`. The single source for every tier (the CLI writer, the
15
+ * delivery daemon reader, this renewal owner): a hand-copied drifted literal would silently split
16
+ * the kind across two store entries with no compile error. Lives in workspace because the
17
+ * key↔filename convention is the workspace layout's; implementations never import each other. */
18
+ export const DELIVERY_CREDS_KEY = "delivery.creds";
12
19
  /** The seed-less daemon creds files a renewal owner re-signs. The $SYS files
13
20
  * (membership-observer, connection-evictor) are deliberately ABSENT: they are rotation-renewed —
14
21
  * no persisted seed can re-sign them, by design. */
15
22
  export const REMINTABLE_DAEMON_CREDS = [
16
- { file: "delivery.creds", profile: "delivery" },
23
+ { file: DELIVERY_CREDS_KEY, profile: "delivery" },
17
24
  { file: "membership-rw.creds", profile: "membership-rw" },
18
25
  ];
19
26
  /** Re-sign the daemon creds files for their EXISTING nkeys (a renewal must never swap a daemon's
20
- * identity — the daemon side pins it). Structured per-file results, never throws: a failed remint
21
- * leaves the old cred running toward its loud expiry and the caller records/reports the failure. */
22
- export async function remintDaemonCreds(root) {
27
+ * identity — the daemon side pins it). Reads and writes through the secret-store seam; `store` is
28
+ * the renewal owner's injection point, SYMMETRIC with the daemon's `runDelivery(args, store?)`.
29
+ * 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) {
23
40
  const auth = loadSpaceAuth(authDir(root));
24
41
  if (!auth)
25
42
  return REMINTABLE_DAEMON_CREDS.map(({ file }) => ({ file, ok: false, skipped: "no-auth" }));
43
+ const s = store ?? workspaceSecretStore(root);
26
44
  const results = [];
27
45
  for (const { file, profile } of REMINTABLE_DAEMON_CREDS) {
28
- const path = join(root, ".cotal", file);
29
- if (!existsSync(path)) {
30
- results.push({ file, ok: false, skipped: "missing-file" });
31
- continue;
32
- }
33
46
  try {
34
- const identity = identityFromCreds(readFileSync(path, "utf8"));
35
- writeSecretFile(path, await mintCreds(auth, identity, profile));
47
+ const current = await s.get(file);
48
+ if (current === undefined) {
49
+ results.push({ file, ok: false, skipped: "missing-file" });
50
+ continue;
51
+ }
52
+ await s.put(file, await mintCreds(auth, identityFromCreds(current), profile));
36
53
  results.push({ file, ok: true });
37
54
  }
38
55
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"renewal.js","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,SAAS,EACT,eAAe,GAEhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEzD;;;;;;GAMG;AAEH;;qDAEqD;AACrD,MAAM,CAAC,MAAM,uBAAuB,GAAsD;IACxF,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE;IAC/C,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,eAAe,EAAE;CAC1D,CAAC;AAUF;;qGAEqG;AACrG,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,uBAAuB,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAkB,EAAE,CAAC,CAAC,CAAC;IAChH,MAAM,OAAO,GAAmB,EAAE,CAAC;IACnC,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,uBAAuB,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;YAC3D,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,iBAAiB,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/D,eAAe,CAAC,IAAI,EAAE,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAeD,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,MAAqB;IACpE,eAAe,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAkB,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC,CAAC,qEAAqE;IACzF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"renewal.js","sourceRoot":"","sources":["../src/renewal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type SecretStore } from "@cotal-ai/core";
2
+ /** THE local composition of the secret keyspace: a filesystem store rooted at the workspace's
3
+ * `.cotal/` dir, so every canonical key (`delivery.creds`, `auth/<space>/callout.json`, …)
4
+ * lands byte-for-byte on today's paths. Every local caller composes through here — a hand-rolled
5
+ * root that drifted from `.cotal` would silently split the keyspace in two. */
6
+ export declare function workspaceSecretStore(root: string): FsSecretStore;
7
+ /**
8
+ * The default filesystem {@link SecretStore}: the workstation adapter behind core's abstract seam.
9
+ *
10
+ * Keys are opaque logical strings the owning packages build to MIRROR today's exact `.cotal` layout
11
+ * per kind — `delivery.creds`, `auth/callout.json`, `<agent>.creds`, and so on — so a local
12
+ * `cotal up` writes byte-for-byte the same files it does today. That layout is heterogeneous (a few
13
+ * kinds are already space-segmented on disk, most are not), so each key builder reproduces its
14
+ * kind's current path verbatim. Crucially the key carries NO injected tenant / `<space>` prefix: a
15
+ * hosted KMS/Vault adapter adds per-tenant scope inside ITS OWN resolve from its injection context,
16
+ * which keeps one backend-agnostic key per kind and preserves local byte-for-byte.
17
+ *
18
+ * This adapter adds nothing of its own: it delegates to the `secret-fs` primitives for the
19
+ * 0600 / icacls / atomic-replace / fail-closed guarantees. Constructed with an EXPLICIT base root
20
+ * (no ambient `~/.cotal`), absolutized so keys are cwd-independent. Lives in `@cotal-ai/workspace`,
21
+ * not core, because on-disk layout is a workstation concern, not the wire protocol.
22
+ *
23
+ * Residual (documented): a symlink UNDER the root that points outside is not caught (no realpath) —
24
+ * acceptable for a single-tenant workstation; a multi-tenant FS deployment should realpath-check.
25
+ */
26
+ export declare class FsSecretStore implements SecretStore {
27
+ private readonly root;
28
+ constructor(root: string);
29
+ /** Resolve a logical key to an absolute path strictly UNDER `root`, fail-closed: reject empty,
30
+ * NUL, absolute keys, the root itself (`.`), and any key that normalizes outside the root
31
+ * (`..` traversal). Containment is checked via `path.relative`, not a `root + sep` prefix —
32
+ * the prefix form breaks at a filesystem-root base (`/` doubles the separator and rejects
33
+ * every key). A malformed key must never read or clobber a path outside the store's tree. */
34
+ private resolve;
35
+ get(key: string): Promise<string | undefined>;
36
+ put(key: string, value: string): Promise<void>;
37
+ delete(key: string): Promise<void>;
38
+ }
39
+ //# sourceMappingURL=secret-store-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store-fs.d.ts","sourceRoot":"","sources":["../src/secret-store-fs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEtF;;;gFAGgF;AAChF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAEhE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,IAAI,EAAE,MAAM;IAKxB;;;;kGAI8F;IAC9F,OAAO,CAAC,OAAO;IAYT,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAU7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQzC"}
@@ -0,0 +1,80 @@
1
+ import { readFileSync, rmSync } from "node:fs";
2
+ import { join, dirname, isAbsolute, normalize, relative, resolve, sep } from "node:path";
3
+ import { mkSecretDir, writeSecretFileAtomic } from "@cotal-ai/core";
4
+ /** THE local composition of the secret keyspace: a filesystem store rooted at the workspace's
5
+ * `.cotal/` dir, so every canonical key (`delivery.creds`, `auth/<space>/callout.json`, …)
6
+ * lands byte-for-byte on today's paths. Every local caller composes through here — a hand-rolled
7
+ * root that drifted from `.cotal` would silently split the keyspace in two. */
8
+ export function workspaceSecretStore(root) {
9
+ return new FsSecretStore(join(root, ".cotal"));
10
+ }
11
+ /**
12
+ * The default filesystem {@link SecretStore}: the workstation adapter behind core's abstract seam.
13
+ *
14
+ * Keys are opaque logical strings the owning packages build to MIRROR today's exact `.cotal` layout
15
+ * per kind — `delivery.creds`, `auth/callout.json`, `<agent>.creds`, and so on — so a local
16
+ * `cotal up` writes byte-for-byte the same files it does today. That layout is heterogeneous (a few
17
+ * kinds are already space-segmented on disk, most are not), so each key builder reproduces its
18
+ * kind's current path verbatim. Crucially the key carries NO injected tenant / `<space>` prefix: a
19
+ * hosted KMS/Vault adapter adds per-tenant scope inside ITS OWN resolve from its injection context,
20
+ * which keeps one backend-agnostic key per kind and preserves local byte-for-byte.
21
+ *
22
+ * This adapter adds nothing of its own: it delegates to the `secret-fs` primitives for the
23
+ * 0600 / icacls / atomic-replace / fail-closed guarantees. Constructed with an EXPLICIT base root
24
+ * (no ambient `~/.cotal`), absolutized so keys are cwd-independent. Lives in `@cotal-ai/workspace`,
25
+ * not core, because on-disk layout is a workstation concern, not the wire protocol.
26
+ *
27
+ * Residual (documented): a symlink UNDER the root that points outside is not caught (no realpath) —
28
+ * acceptable for a single-tenant workstation; a multi-tenant FS deployment should realpath-check.
29
+ */
30
+ export class FsSecretStore {
31
+ root;
32
+ constructor(root) {
33
+ if (!root)
34
+ throw new Error("FsSecretStore: root is required");
35
+ this.root = normalize(resolve(root)); // absolutize so keys are cwd-independent
36
+ }
37
+ /** Resolve a logical key to an absolute path strictly UNDER `root`, fail-closed: reject empty,
38
+ * NUL, absolute keys, the root itself (`.`), and any key that normalizes outside the root
39
+ * (`..` traversal). Containment is checked via `path.relative`, not a `root + sep` prefix —
40
+ * the prefix form breaks at a filesystem-root base (`/` doubles the separator and rejects
41
+ * every key). A malformed key must never read or clobber a path outside the store's tree. */
42
+ resolve(key) {
43
+ if (!key || key.includes("\0"))
44
+ throw new Error(`FsSecretStore: invalid key ${JSON.stringify(key)}`);
45
+ if (isAbsolute(key))
46
+ throw new Error(`FsSecretStore: key must be relative, got ${JSON.stringify(key)}`);
47
+ const abs = normalize(join(this.root, key));
48
+ const rel = relative(this.root, abs);
49
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel))
50
+ throw new Error(`FsSecretStore: key must name a file under the root: ${JSON.stringify(key)}`);
51
+ return abs;
52
+ }
53
+ async get(key) {
54
+ const p = this.resolve(key);
55
+ try {
56
+ return readFileSync(p, "utf8");
57
+ }
58
+ catch (e) {
59
+ if (e.code === "ENOENT")
60
+ return undefined; // absent → undefined, race-safe
61
+ throw e;
62
+ }
63
+ }
64
+ async put(key, value) {
65
+ const p = this.resolve(key);
66
+ mkSecretDir(dirname(p)); // harden the parent dir before the secret lands (0700 / private ACL)
67
+ writeSecretFileAtomic(p, value); // temp + rename: a concurrent get sees old or new, never torn
68
+ }
69
+ async delete(key) {
70
+ const p = this.resolve(key);
71
+ try {
72
+ rmSync(p);
73
+ }
74
+ catch (e) {
75
+ if (e.code !== "ENOENT")
76
+ throw e; // already-absent is success (idempotent)
77
+ }
78
+ }
79
+ }
80
+ //# sourceMappingURL=secret-store-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-store-fs.js","sourceRoot":"","sources":["../src/secret-store-fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAoB,MAAM,gBAAgB,CAAC;AAEtF;;;gFAGgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,aAAa;IACP,IAAI,CAAS;IAE9B,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAC9D,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,yCAAyC;IACjF,CAAC;IAED;;;;kGAI8F;IACtF,OAAO,CAAC,GAAW;QACzB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvE,IAAI,UAAU,CAAC,GAAG,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrF,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;YACvE,MAAM,IAAI,KAAK,CAAC,uDAAuD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChG,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAK,CAA2B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC,CAAC,gCAAgC;YACtG,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,qEAAqE;QAC9F,qBAAqB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,8DAA8D;IACjG,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,CAAC,CAAC,CAAC,CAAC;QACZ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAK,CAA2B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,CAAC,CAAC,CAAC,yCAAyC;QACxG,CAAC;IACH,CAAC;CACF"}
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.11.6",
4
+ "version": "0.13.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.11.6"
21
+ "@cotal-ai/core": "0.13.0"
22
22
  },
23
23
  "files": [
24
24
  "dist"
@@ -29,6 +29,6 @@
29
29
  "scripts": {
30
30
  "typecheck": "tsc -p tsconfig.json --noEmit",
31
31
  "build": "tsc -p tsconfig.json",
32
- "test": "tsx smoke/preflight.smoke.ts"
32
+ "test": "tsx smoke/preflight.smoke.ts && tsx smoke/maintenance.smoke.ts"
33
33
  }
34
34
  }