@cello-protocol/daemon 0.0.85 → 0.0.87

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.
@@ -11,7 +11,10 @@
11
11
  */
12
12
  import { homedir } from "node:os";
13
13
  import { join } from "node:path";
14
+ import { mkdirSync } from "node:fs";
15
+ import { LocalSidecarGatewayClient, spawnGatewaySidecar } from "@cello-protocol/gateway";
14
16
  import { startDaemon } from "../daemon.js";
17
+ import { resolveDbKey, dbKeyPathFor } from "../sqlcipher-db.js";
15
18
  import { createDirectoryEndpointResolver } from "../directory-bootstrap.js";
16
19
  import { buildManifestDeps } from "../manifest-deps.js";
17
20
  import { RandomizedPollScheduler } from "../manifest-poll-scheduler.js";
@@ -49,6 +52,82 @@ const celloDir = process.env.CELLO_DIR || join(homedir(), ".cello");
49
52
  const socketPath = join(celloDir, "daemon.sock");
50
53
  const lockFilePath = join(celloDir, "daemon.lock");
51
54
  const version = process.env.CELLO_VERSION || "0.0.1";
55
+ /**
56
+ * Bring up the security and governance layer (DOD-M9C-WIRE-1, policy D-2).
57
+ *
58
+ * THE DEFECT THIS EXISTS TO CLOSE: this function did not exist. `startDaemon` was called without a
59
+ * gateway, fell back to an always-allow stub, and every shipped daemon screened nothing while
60
+ * logging `security.gateway.connected`. The layer was fully built, unit-green and gate-green the
61
+ * whole time — the gate injected the client the product never did.
62
+ *
63
+ * Fail-closed, never passthrough (M9C-D12): if the sidecar cannot be spawned, the client is STILL
64
+ * the enforcing one. It fails closed on every call (`gateway_unavailable`, INV-6), the failure is
65
+ * announced, and the daemon stays up so the operator can run `cello config` / `cello policy log`
66
+ * against it and find out why. A dead agent with a cryptic startup error diagnoses nothing, and a
67
+ * silent downgrade to passthrough is the original bug wearing a hat.
68
+ */
69
+ async function startSecurityLayer() {
70
+ const socketPath = join(celloDir, "gateway.sock");
71
+ const client = new LocalSidecarGatewayClient({ socketPath, logger });
72
+ // The sidecar opens the encrypted store at startup, but the key file is only created when the
73
+ // daemon first opens its own database — which happens AFTER this. So resolve it here (M9C-D13):
74
+ // the same idempotent call the daemon makes, which on a fresh install is the one that generates
75
+ // the key. The bytes are deliberately unused; only the side effect matters, and the key never
76
+ // travels to the child — the child gets the PATH (M9C-D8).
77
+ const keyFilePath = dbKeyPathFor(join(celloDir, "sessions.db"));
78
+ let sidecar;
79
+ try {
80
+ mkdirSync(celloDir, { recursive: true, mode: 0o700 });
81
+ resolveDbKey(join(celloDir, "sessions.db"), keyFilePath);
82
+ sidecar = await spawnGatewaySidecar({
83
+ socketPath,
84
+ env: {
85
+ CELLO_GATEWAY_STORE_DB: join(celloDir, "gateway.db"),
86
+ CELLO_GATEWAY_STORE_KEY_FILE: keyFilePath,
87
+ },
88
+ });
89
+ logger.info("security.gateway.spawned", { pid: sidecar.pid ?? -1, socketPath });
90
+ sidecar.process.once("exit", (code, signal) => {
91
+ // No auto-restart (M9C-D14). Every subsequent screen fails closed with a real cause; this
92
+ // line is how the operator learns the screening process died rather than inferring it from
93
+ // a wall of blocked sends.
94
+ logger.error("security.gateway.exited", { code: code ?? -1, signal: signal ?? "none" });
95
+ });
96
+ }
97
+ catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ // The gateway's own errors carry a code and actionable guidance; the spawner appends the
100
+ // child's stderr tail. Keep BOTH, and hand them to the client so every later refusal names
101
+ // this cause instead of the generic "could not be reached, retry" (review F2) — which points
102
+ // at the transport when the fault was a key file, a lock, or a stale plaintext store, and
103
+ // suggests a retry that can never succeed.
104
+ const code = err?.code ?? "sidecar_spawn_failed";
105
+ const guidance = err?.guidance
106
+ ?? "The security layer could not start, so nothing can be sent or received: every message " +
107
+ "fails closed. Fix the cause below and restart the daemon.";
108
+ logger.error("security.gateway.spawn_failed", { reason: code, error: message, guidance });
109
+ client.setUnavailableCause(code, `${guidance} (cause: ${message})`);
110
+ }
111
+ return { client, sidecar };
112
+ }
113
+ /**
114
+ * Stop the screening sidecar. Idempotent, and safe to call when the spawn failed.
115
+ *
116
+ * An orphaned gateway holds the encrypted store's write lock against the NEXT daemon, and its
117
+ * stdio pipes keep this process's event loop alive — so a daemon stopped over IPC would never
118
+ * actually exit. Both are why this is wired into the daemon's own stop() rather than only into
119
+ * the signal handler.
120
+ */
121
+ let securityLayer;
122
+ async function stopSecurityLayer() {
123
+ if (!securityLayer)
124
+ return;
125
+ await securityLayer.client.close();
126
+ if (securityLayer.sidecar) {
127
+ await securityLayer.sidecar.stop();
128
+ logger.info("security.gateway.stopped", {});
129
+ }
130
+ }
52
131
  async function main() {
53
132
  // M7 Keystone (Part 1): give the daemon its door to the directory. The resolver
54
133
  // discovers the directory endpoint via GET ${CELLO_DIRECTORY_URL}/bootstrap (the
@@ -70,6 +149,25 @@ async function main() {
70
149
  // When a manifest is active, the daemon verifies the directory's step-6 identity proof against the
71
150
  // node pubkeys in it. Both dialers (keystone + per-agent) receive the verifier via startDaemon.
72
151
  const manifest = buildManifestDeps(logger);
152
+ // D-2: the security and governance layer runs ENFORCING in the shipped daemon. This is the line
153
+ // whose absence made every guard M9 built inert.
154
+ const security = await startSecurityLayer();
155
+ securityLayer = security;
156
+ /**
157
+ * Restart the sidecar so a stored config change takes effect (M9C-D17). The gateway reads its
158
+ * config only at boot, so without this a confirmed loosening is recorded and does nothing.
159
+ * The socket path is unchanged and `LocalSidecarGatewayClient` reconnects lazily, so the client
160
+ * needs no involvement. A failure PROPAGATES — the caller reports stored-but-not-applied rather
161
+ * than telling the operator a guard changed when it did not.
162
+ */
163
+ const restartSecurityGateway = async () => {
164
+ if (security.sidecar)
165
+ await security.sidecar.stop();
166
+ const restarted = await startSecurityLayer();
167
+ security.sidecar = restarted.sidecar;
168
+ if (!restarted.sidecar)
169
+ throw new Error("the screening process did not come back up");
170
+ };
73
171
  const handle = await startDaemon({
74
172
  celloDir,
75
173
  socketPath,
@@ -79,10 +177,16 @@ async function main() {
79
177
  logger,
80
178
  directoryEndpointResolver,
81
179
  ...manifest,
180
+ securityGateway: security.client,
181
+ restartSecurityGateway,
182
+ onShutdown: stopSecurityLayer,
82
183
  registryPubkey: REGISTRY_SIGNER_PUBKEY,
83
184
  registryPollScheduler: new RandomizedPollScheduler({ minMs: REGISTRY_POLL_MIN_MS, maxMs: REGISTRY_POLL_MAX_MS }),
84
185
  });
85
186
  const shutdown = async (signal) => {
187
+ // handle.stop() runs onShutdown (stopSecurityLayer) for us — the teardown lives there so that
188
+ // `cello logout`, which stops the daemon over IPC and never reaches this handler, tears the
189
+ // sidecar down too.
86
190
  await handle.stop(signal);
87
191
  process.exit(0);
88
192
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cello-daemon.js","sourceRoot":"","sources":["../../src/bin/cello-daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cello-daemon.js","sourceRoot":"","sources":["../../src/bin/cello-daemon.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAKrB,MAAM,YAAY,CAAC;AAMpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAiB7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAsB,MAAM,2BAA2B,CAAC;AAO5G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAkBnF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;IACrC;;OAEG;IACH,eAAe,IAAI,YAAY,CAAC;CACjC;AAYD,qBAAa,4BAA6B,YAAW,mBAAmB;IAChE,UAAU,CAAC,MAAM,EAAE,iBAAiB;CA8B3C;AAID,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7E"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAKrB,MAAM,YAAY,CAAC;AAMpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAO/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAiB7G,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAsB,MAAM,2BAA2B,CAAC;AAO5G,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAkBnF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;IACrC;;OAEG;IACH,eAAe,IAAI,YAAY,CAAC;CACjC;AAYD,qBAAa,4BAA6B,YAAW,mBAAmB;IAChE,UAAU,CAAC,MAAM,EAAE,iBAAiB;CA8B3C;AAID,eAAO,MAAM,sBAAsB,QAAsB,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7E"}
package/dist/daemon.js CHANGED
@@ -34,7 +34,7 @@ import { acquireSingletonLock } from "./singleton-lock.js";
34
34
  import { createIpcServer } from "./ipc-server.js";
35
35
  import { renderForSurface } from "./vocabulary.js";
36
36
  import { SessionNodeManager } from "./session-node-manager.js";
37
- import { PassthroughGatewayClient } from "@cello-protocol/gateway";
37
+ import { registerGatewayConfigHandlers } from "./gateway-config-handlers.js";
38
38
  import { RetryQueue } from "./retry-queue.js";
39
39
  import { NonceDedupStore } from "./nonce-dedup.js";
40
40
  import { ContentParkClient } from "./content-park-client.js";
@@ -183,16 +183,24 @@ async function startDaemonHoldingLock(config, singletonLock) {
183
183
  // the OS lock is what decides whether a daemon may run. This file never gates startup.
184
184
  await acquireLock(lockFilePath, { pid: process.pid, socketPath, version });
185
185
  // M9-CORE-001: one security-gateway client, shared by both seams — the outbound screen in
186
- // cello_send and the inbound screen inside SessionNodeManager. Absent config falls back to a
187
- // PassthroughGatewayClient (always-allow), so pre-M9 daemons behave exactly as before while
188
- // still returning a verdict (SI-001).
189
- const securityGateway = config.securityGateway ?? new PassthroughGatewayClient();
190
- // M9-CORE-001 observability: announce the gateway mode at startup. The sidecar socket connects
191
- // lazily on the first screen, so this records which adapter is wired (sidecar vs the always-allow
192
- // passthrough default), not a live socket handshake.
193
- logger.info("security.gateway.connected", {
194
- mode: config.securityGateway ? "sidecar" : "passthrough",
195
- });
186
+ // cello_send and the inbound screen inside SessionNodeManager. REQUIRED (INV-9): there is no
187
+ // fallback, because the fallback WAS the bug — an always-allow default that nothing in the
188
+ // product ever overrode.
189
+ // The type says required, but tests are excluded from typecheck and JS callers exist, so the
190
+ // absence has to be LOUD here rather than a TypeError three lines later that names the wrong
191
+ // subsystem. A test that genuinely does not screen says so by passing the passthrough client.
192
+ if (!config.securityGateway) {
193
+ throw new Error("startDaemon: securityGateway is required (INV-9). The daemon no longer defaults to " +
194
+ "always-allow screening, because that default shipped a security layer that never ran. " +
195
+ "Pass a LocalSidecarGatewayClient in production, or new PassthroughGatewayClient() if " +
196
+ "this caller deliberately does not screen.");
197
+ }
198
+ const securityGateway = config.securityGateway;
199
+ // Observability: announce the mode the CLIENT declares (M9C-D11), never a ternary over the
200
+ // config. The sidecar socket connects lazily on the first screen, so this reports which adapter
201
+ // is wired, not a live socket handshake — but it reports it from the object that will do the
202
+ // screening, so a wiring mistake shows up here instead of hiding behind a correct-looking line.
203
+ logger.info("security.gateway.connected", { mode: securityGateway.mode });
196
204
  const sessionNodeManager = new SessionNodeManager({
197
205
  factory: sessionNodeFactory ?? new ProductionSessionNodeFactory(),
198
206
  logger,
@@ -2255,6 +2263,16 @@ async function startDaemonHoldingLock(config, singletonLock) {
2255
2263
  return { ok: false, reason: "not_implemented", guidance: `'${tool}' is not yet implemented in the daemon. This feature will be available in a future milestone.` };
2256
2264
  });
2257
2265
  }
2266
+ // DOD-M9C-SURFACE-1: the security layer's control surface. Registered here, defined in its own
2267
+ // module — it needs the cello dir, a logger, and the connection's client type, and nothing else
2268
+ // about sessions or ceremonies.
2269
+ registerGatewayConfigHandlers({
2270
+ handlers,
2271
+ celloDir,
2272
+ logger,
2273
+ getClientType: (connectionId) => perConnectionState.get(connectionId)?.clientType,
2274
+ ...(config.restartSecurityGateway ? { restartSecurityGateway: config.restartSecurityGateway } : {}),
2275
+ });
2258
2276
  // The session READ surface (session-read-handlers.ts): sealed receipt, transcript, list, name.
2259
2277
  // All four read the PERSISTED store, so they survive a restart and a fresh connection.
2260
2278
  registerSessionReadHandlers({
@@ -2611,6 +2629,26 @@ async function startDaemonHoldingLock(config, singletonLock) {
2611
2629
  await ipcServer.stop();
2612
2630
  }
2613
2631
  finally {
2632
+ // DOD-M9C-WIRE-1: tear down whatever the composition root started alongside us — today the
2633
+ // screening sidecar. Two reasons it is HERE and not only in the bin's signal handler:
2634
+ // `cello logout` stops the daemon through the IPC `shutdown` verb, which reaches this
2635
+ // function and never touches the signal path; and a spawned child's stdio pipes keep this
2636
+ // process's event loop alive, so a daemon stopped that way would never actually exit.
2637
+ //
2638
+ // FIRST in the finally, BEFORE the singleton lock is released (review F4). The comment below
2639
+ // states the property the lock provides — "while we hold it, no successor daemon can start"
2640
+ // — and the sidecar teardown needs exactly that property: the `shutdown` verb acknowledges
2641
+ // immediately without awaiting this drain, so `cello logout && cello login` can race a new
2642
+ // daemon into existence. Releasing the lock first would let its gateway spawn while ours
2643
+ // still holds gateway.db's write lock, which the 3s busy_timeout usually hides — making the
2644
+ // failure intermittent rather than absent.
2645
+ if (config.onShutdown) {
2646
+ await config.onShutdown().catch((err) => {
2647
+ logger.error("daemon.shutdown.hook_failed", {
2648
+ error: err instanceof Error ? err.message : String(err),
2649
+ });
2650
+ });
2651
+ }
2614
2652
  // DOD-SINGLE-DAEMON-1: in a `finally`, because a throw anywhere above must not leave the lock
2615
2653
  // held. In the real binary the process exits and the kernel reclaims it — but an in-process
2616
2654
  // caller (vitest, an embedder) whose shutdown throws would otherwise find every subsequent