@cello-protocol/daemon 0.0.10 → 0.0.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"db-identity-store.d.ts","sourceRoot":"","sources":["../src/db-identity-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,+BAA+B,CAAC;AAIvC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAqC7D;AAMD;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAe;;gBAId,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAM9C,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAOpC;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI;IAerF,2FAA2F;IAC3F,UAAU,IAAI,QAAQ,EAAE;CAWzB;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,6BAA6B;;gBAKjE,IAAI,EAAE;QAAE,EAAE,EAAE,cAAc,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAmBrE,mBAAmB,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,UAAU,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F,wBAAwB,CAAC,IAAI,EAAE;QACnC,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWX,oBAAoB,CAAC,IAAI,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,UAAU,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,UAAU,CAAC;QAC5B,mBAAmB,EAAE,UAAU,CAAC;QAChC,SAAS,EAAE,gBAAgB,GAAG,aAAa,CAAC;KAC7C,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BX,oBAAoB,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtG,qBAAqB,IAAI,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;IAYhE,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAUtD,uBAAuB,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAgB9D,iBAAiB,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;CAS/D"}
1
+ {"version":3,"file":"db-identity-store.d.ts","sourceRoot":"","sources":["../src/db-identity-store.ts"],"names":[],"mappings":"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"}
@@ -16,49 +16,126 @@
16
16
  * The K_local seed and FROST/ML-DSA secrets are stored as BLOB columns inside the encrypted DB; they
17
17
  * are NEVER written to a flat file and NEVER logged (SI-001). The row is the only home.
18
18
  */
19
+ import { randomUUID } from "node:crypto";
19
20
  const ML_DSA_ALGORITHM = "ML-DSA-44";
21
+ /**
22
+ * The `agents` schema (CELLO-M7-REMOVE-001 DOD-REMOVE-1). The store is keyed by a STABLE `agent_id`,
23
+ * not by `agent_name` — guardrail #1: durable identity hangs off agent_id; the human name and the
24
+ * pubkey are mutable ATTRIBUTES. A partial unique index makes `agent_name` unique only among
25
+ * NON-retired rows, so removal (state='retired') frees the name for reuse while the retired identity
26
+ * survives for accountability (SI-002). `state`: 'created' | 'registered' | 'retired'.
27
+ */
28
+ const CREATE_AGENTS_SQL = `
29
+ CREATE TABLE IF NOT EXISTS agents (
30
+ agent_id TEXT PRIMARY KEY,
31
+ agent_name TEXT NOT NULL,
32
+ -- K_local Ed25519 identity (PERSIST-002: seed moves from agents/<name>/key into this BLOB).
33
+ k_local_seed BLOB NOT NULL,
34
+ k_local_pubkey TEXT NOT NULL,
35
+ -- lifecycle: 'created' (K_local exists, not yet registered) | 'registered' | 'retired' (REMOVE-001).
36
+ state TEXT NOT NULL DEFAULT 'created',
37
+ -- ML-DSA keypair (was ml-dsa-keypair.json).
38
+ ml_dsa_pubkey TEXT,
39
+ ml_dsa_secret BLOB,
40
+ ml_dsa_algorithm TEXT,
41
+ -- FROST signing share (was frost-share.json).
42
+ frost_epoch_id TEXT,
43
+ frost_primary_pubkey TEXT,
44
+ frost_identifier TEXT,
45
+ frost_signing_share BLOB,
46
+ frost_threshold INTEGER,
47
+ frost_participants INTEGER,
48
+ frost_commitments BLOB,
49
+ frost_verifying_shares BLOB,
50
+ frost_dkg_method TEXT,
51
+ -- registration record (was registration-state.json).
52
+ reg_agent_id TEXT,
53
+ reg_primary_pubkey TEXT,
54
+ reg_ml_dsa_pubkey TEXT,
55
+ reg_registered_at INTEGER,
56
+ reg_status TEXT,
57
+ -- agent↔user link captured at registration (was agent-user-link.json).
58
+ link_agent_id TEXT,
59
+ link_pre_auth_token TEXT,
60
+ link_linked_at INTEGER,
61
+ created_at INTEGER NOT NULL,
62
+ updated_at INTEGER NOT NULL
63
+ )
64
+ `;
65
+ // A name is unique only among NON-retired agents — the DB-level backstop for "free the name on removal".
66
+ const CREATE_ACTIVE_NAME_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS agents_active_name ON agents(agent_name) WHERE state != 'retired'";
67
+ // The columns of the PRE-REMOVE-001 `agents` table (agent_name PK, no agent_id), in order. Used to
68
+ // copy rows verbatim during the one-time re-key rebuild.
69
+ const PRE_REKEY_COLUMNS = [
70
+ "agent_name",
71
+ "k_local_seed",
72
+ "k_local_pubkey",
73
+ "state",
74
+ "ml_dsa_pubkey",
75
+ "ml_dsa_secret",
76
+ "ml_dsa_algorithm",
77
+ "frost_epoch_id",
78
+ "frost_primary_pubkey",
79
+ "frost_identifier",
80
+ "frost_signing_share",
81
+ "frost_threshold",
82
+ "frost_participants",
83
+ "frost_commitments",
84
+ "frost_verifying_shares",
85
+ "frost_dkg_method",
86
+ "reg_agent_id",
87
+ "reg_primary_pubkey",
88
+ "reg_ml_dsa_pubkey",
89
+ "reg_registered_at",
90
+ "reg_status",
91
+ "link_agent_id",
92
+ "link_pre_auth_token",
93
+ "link_linked_at",
94
+ "created_at",
95
+ "updated_at",
96
+ ];
97
+ /**
98
+ * One-time re-key of a PRE-REMOVE-001 `agents` table (PRIMARY KEY agent_name, no agent_id) to the new
99
+ * stable-agent_id shape. Rebuilds the table, backfilling a fresh agent_id per existing row. Runs at
100
+ * most once — `ensureIdentitySchema` guards it behind a column-presence check — and is wrapped in a
101
+ * transaction so a crash mid-rebuild leaves the original table intact. Works on node:sqlite (in-memory
102
+ * test handles) and SQLCipher alike.
103
+ */
104
+ function rebuildAgentsToAgentIdPk(db) {
105
+ const cols = PRE_REKEY_COLUMNS.join(", ");
106
+ db.exec("BEGIN");
107
+ try {
108
+ db.exec("ALTER TABLE agents RENAME TO agents_pre_rekey");
109
+ db.exec(CREATE_AGENTS_SQL);
110
+ db.exec(`INSERT INTO agents (agent_id, ${cols})
111
+ SELECT lower(hex(randomblob(16))), ${cols} FROM agents_pre_rekey`);
112
+ db.exec("DROP TABLE agents_pre_rekey");
113
+ db.exec("COMMIT");
114
+ }
115
+ catch (err) {
116
+ try {
117
+ db.exec("ROLLBACK");
118
+ }
119
+ catch { /* the failing statement may have already aborted the txn */ }
120
+ throw err;
121
+ }
122
+ }
20
123
  /**
21
124
  * Idempotent schema for the identity store. Called once at daemon init AND defensively by each
22
- * DbRegistrationPersistence constructor (CREATE TABLE IF NOT EXISTS is idempotent), so the store
23
- * works whether or not the composition root has run its own ensure.
125
+ * DbRegistrationPersistence constructor, so the store works whether or not the composition root has
126
+ * run its own ensure. Creates the table on a fresh DB, re-keys a pre-REMOVE-001 table to the
127
+ * stable-agent_id shape once, and (always) ensures the active-name partial unique index.
24
128
  */
25
129
  export function ensureIdentitySchema(db) {
26
- db.exec(`
27
- CREATE TABLE IF NOT EXISTS agents (
28
- agent_name TEXT PRIMARY KEY,
29
- -- K_local Ed25519 identity (PERSIST-002: seed moves from agents/<name>/key into this BLOB).
30
- k_local_seed BLOB NOT NULL,
31
- k_local_pubkey TEXT NOT NULL,
32
- -- lifecycle: 'created' (K_local exists, not yet registered) | 'registered'.
33
- state TEXT NOT NULL DEFAULT 'created',
34
- -- ML-DSA keypair (was ml-dsa-keypair.json).
35
- ml_dsa_pubkey TEXT,
36
- ml_dsa_secret BLOB,
37
- ml_dsa_algorithm TEXT,
38
- -- FROST signing share (was frost-share.json).
39
- frost_epoch_id TEXT,
40
- frost_primary_pubkey TEXT,
41
- frost_identifier TEXT,
42
- frost_signing_share BLOB,
43
- frost_threshold INTEGER,
44
- frost_participants INTEGER,
45
- frost_commitments BLOB,
46
- frost_verifying_shares BLOB,
47
- frost_dkg_method TEXT,
48
- -- registration record (was registration-state.json).
49
- reg_agent_id TEXT,
50
- reg_primary_pubkey TEXT,
51
- reg_ml_dsa_pubkey TEXT,
52
- reg_registered_at INTEGER,
53
- reg_status TEXT,
54
- -- agent↔user link captured at registration (was agent-user-link.json).
55
- link_agent_id TEXT,
56
- link_pre_auth_token TEXT,
57
- link_linked_at INTEGER,
58
- created_at INTEGER NOT NULL,
59
- updated_at INTEGER NOT NULL
60
- )
61
- `);
130
+ const cols = db.prepare("PRAGMA table_info(agents)").all();
131
+ if (cols.length === 0) {
132
+ db.exec(CREATE_AGENTS_SQL);
133
+ }
134
+ else if (!cols.some((c) => c.name === "agent_id")) {
135
+ // A pre-REMOVE-001 table exists (agent_name PK). Re-key it once to the stable-agent_id shape.
136
+ rebuildAgentsToAgentIdPk(db);
137
+ }
138
+ db.exec(CREATE_ACTIVE_NAME_INDEX_SQL);
62
139
  }
63
140
  const toBuf = (b) => Buffer.from(b);
64
141
  const toBytes = (v) => v instanceof Uint8Array ? new Uint8Array(v) : Buffer.isBuffer(v) ? new Uint8Array(v) : new Uint8Array(0);
@@ -70,34 +147,85 @@ export class DbIdentityStore {
70
147
  this.#logger = logger;
71
148
  ensureIdentitySchema(db);
72
149
  }
73
- /** True if an agent row with this name already exists. */
74
- hasAgent(agentName) {
75
- const row = this.#db.prepare("SELECT 1 AS one FROM agents WHERE agent_name = ?").get(agentName);
150
+ /** True if an ACTIVE (non-retired) agent row with this name exists — the create-collision check. */
151
+ hasActiveAgent(agentName) {
152
+ const row = this.#db
153
+ .prepare("SELECT 1 AS one FROM agents WHERE agent_name = ? AND state != 'retired'")
154
+ .get(agentName);
76
155
  return row !== undefined;
77
156
  }
78
157
  /**
79
- * Create a new agent row holding a fresh K_local seed (AC-004). Explicit only callers generate
80
- * the seed (crypto) and pass it in. Throws if the name already exists (no silent overwrite of an
81
- * identity).
158
+ * Create a new agent row holding a fresh K_local seed (AC-004), keyed by a freshly-minted stable
159
+ * agent_id (REMOVE-001 DOD-REMOVE-1; guardrail #1). Explicit only callers generate the seed
160
+ * (crypto) and pass it in. Throws if an ACTIVE agent already holds the name (no silent overwrite); a
161
+ * RETIRED row with the same name does NOT block — the name has been freed. Returns the new agent_id.
82
162
  */
83
163
  createAgent(agentName, kLocalSeed, kLocalPubkeyHex) {
84
- if (this.hasAgent(agentName)) {
164
+ if (this.hasActiveAgent(agentName)) {
85
165
  throw new Error(`agent '${agentName}' already exists`);
86
166
  }
167
+ const agentId = randomUUID();
87
168
  const now = Date.now();
88
169
  this.#db
89
- .prepare(`INSERT INTO agents (agent_name, k_local_seed, k_local_pubkey, state, created_at, updated_at)
90
- VALUES (?, ?, ?, 'created', ?, ?)`)
91
- .run(agentName, toBuf(kLocalSeed), kLocalPubkeyHex, now, now);
92
- // SI-001: the seed is NEVER logged — only the agent name and PUBLIC key.
93
- this.#logger.info("persist.identity.created", { agentName, agentPubkey: kLocalPubkeyHex });
170
+ .prepare(`INSERT INTO agents (agent_id, agent_name, k_local_seed, k_local_pubkey, state, created_at, updated_at)
171
+ VALUES (?, ?, ?, ?, 'created', ?, ?)`)
172
+ .run(agentId, agentName, toBuf(kLocalSeed), kLocalPubkeyHex, now, now);
173
+ // SI-001: the seed is NEVER logged — only the agent name, the agent_id, and the PUBLIC key.
174
+ this.#logger.info("persist.identity.created", { agentName, agentId, agentPubkey: kLocalPubkeyHex });
175
+ return agentId;
176
+ }
177
+ /**
178
+ * Retire (REMOVE-001 DOD-REMOVE-1): flip the ACTIVE row for `agentName` to state='retired' WITHOUT
179
+ * deleting the row, its keys, or its history (SI-002 — accountability survives). One-way. The name
180
+ * is freed (the partial unique index excludes retired rows). Returns the retired agent_id, or null if
181
+ * there was no active agent with that name (fail-loud at the caller).
182
+ */
183
+ retireAgent(agentName) {
184
+ const active = this.#db
185
+ .prepare("SELECT agent_id FROM agents WHERE agent_name = ? AND state != 'retired'")
186
+ .get(agentName);
187
+ if (!active)
188
+ return null;
189
+ const res = this.#db
190
+ .prepare("UPDATE agents SET state = 'retired', updated_at = ? WHERE agent_id = ?")
191
+ .run(Date.now(), active.agent_id);
192
+ if (Number(res.changes) === 0)
193
+ return null;
194
+ return active.agent_id;
94
195
  }
95
- /** Enumerate all agents (name + seed + pubkey + state) for the daemon's startup loader. */
196
+ /**
197
+ * Read the row to act on for a removal / directory-revocation re-push (REMOVE-001 DOD-REMOVE-2): the
198
+ * ACTIVE row for the name if one exists (a fresh removal), else the MOST-RECENTLY-retired row (a
199
+ * re-push of an already-retired agent whose directory revocation did not land — DB-001). Includes the
200
+ * K_local seed (to re-sign the revocation) and the DIRECTORY-known reg_agent_id (what the directory is
201
+ * asked to revoke; null if the agent was never registered). Returns null if no row with this name
202
+ * exists at all.
203
+ */
204
+ getAgentForRevocation(agentName) {
205
+ const r = this.#db
206
+ .prepare(`SELECT agent_id, reg_agent_id, k_local_seed, state FROM agents WHERE agent_name = ?
207
+ ORDER BY (state != 'retired') DESC, updated_at DESC LIMIT 1`)
208
+ .get(agentName);
209
+ if (!r)
210
+ return null;
211
+ return {
212
+ localAgentId: r.agent_id,
213
+ regAgentId: r.reg_agent_id ?? null,
214
+ kLocalSeed: toBytes(r.k_local_seed),
215
+ state: r.state,
216
+ };
217
+ }
218
+ /**
219
+ * Enumerate ACTIVE agents (agent_id + name + seed + pubkey + state) for the daemon's startup loader.
220
+ * Retired rows are EXCLUDED — they must never be resurrected into the runtime — but remain in the DB
221
+ * and are readable directly for accountability (SI-002).
222
+ */
96
223
  listAgents() {
97
224
  const rows = this.#db
98
- .prepare("SELECT agent_name, k_local_seed, k_local_pubkey, state FROM agents ORDER BY agent_name ASC")
225
+ .prepare("SELECT agent_id, agent_name, k_local_seed, k_local_pubkey, state FROM agents WHERE state != 'retired' ORDER BY agent_name ASC")
99
226
  .all();
100
227
  return rows.map((r) => ({
228
+ agentId: r.agent_id,
101
229
  agentName: r.agent_name,
102
230
  kLocalSeed: toBytes(r.k_local_seed),
103
231
  kLocalPubkey: r.k_local_pubkey,
@@ -123,8 +251,11 @@ export class DbRegistrationPersistence {
123
251
  }
124
252
  #updateRow(setClause, params) {
125
253
  const now = Date.now();
254
+ // REMOVE-001: target the ACTIVE row for this name. After name reuse a retired row and a new active
255
+ // row can share a name; the partial unique index guarantees at most one active row, so this is
256
+ // unambiguous. A retired identity's registration material is never mutated.
126
257
  const res = this.#db
127
- .prepare(`UPDATE agents SET ${setClause}, updated_at = ? WHERE agent_name = ?`)
258
+ .prepare(`UPDATE agents SET ${setClause}, updated_at = ? WHERE agent_name = ? AND state != 'retired'`)
128
259
  .run(...params, now, this.#agentName);
129
260
  if (Number(res.changes) === 0) {
130
261
  // The agent row must exist (created by the agent-creation path before registration). A missing
@@ -181,7 +312,8 @@ export class DbRegistrationPersistence {
181
312
  }
182
313
  // ─── Load (restart rehydration) ──────────────────────────────────────────────
183
314
  #row() {
184
- return this.#db.prepare("SELECT * FROM agents WHERE agent_name = ?").get(this.#agentName);
315
+ // REMOVE-001: load the ACTIVE row for this name (never a retired tombstone).
316
+ return this.#db.prepare("SELECT * FROM agents WHERE agent_name = ? AND state != 'retired'").get(this.#agentName);
185
317
  }
186
318
  async loadRegistrationState() {
187
319
  const r = this.#row();
@@ -1 +1 @@
1
- {"version":3,"file":"db-identity-store.js","sourceRoot":"","sources":["../src/db-identity-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"db-identity-store.js","sourceRoot":"","sources":["../src/db-identity-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * CELLO-M7-CONN-001 (DOD-CONN-3) — HTTP consortium-manifest poll.
3
+ *
4
+ * The manifest poll is the ONE daemon-level directory operation. With the keystone
5
+ * deleted there is no shared authenticated signaling stream to carry it, so it moves
6
+ * to unauthenticated HTTP: GET ${directoryUrl}/manifest. The manifest is PUBLIC,
7
+ * self-authenticating data — threshold-signed by officer root keys that the daemon has
8
+ * pinned LOCALLY (CELLO_CONSORTIUM_ROOT_KEYS). The directory is ONLY a transport, never
9
+ * a trust anchor: a rogue/compromised directory cannot make us adopt a forged, expired,
10
+ * not-yet-valid, or rolled-back manifest. The verify-before-adopt policy here is the
11
+ * SAME TUF policy the deleted keystone signaling path enforced (SignalingManager
12
+ * .handleManifestPollResponse), preserved unchanged across the transport move.
13
+ *
14
+ * Runs daemon-level — even with ZERO agents (the property the keystone, which borrowed
15
+ * the primary agent's identity to authenticate, could never provide).
16
+ *
17
+ * Crypto reference: RFC 8032 (Ed25519 threshold verification via verifyManifest).
18
+ */
19
+ import type { IManifestProvider, IManifestVersionStore, IManifestPollScheduler } from "@cello-protocol/transport";
20
+ /** Minimal structural logger — events use the `domain.noun.verb` taxonomy. */
21
+ export interface ManifestPollLogger {
22
+ info(event: string, ctx?: Record<string, unknown>): void;
23
+ warn(event: string, ctx?: Record<string, unknown>): void;
24
+ error(event: string, ctx?: Record<string, unknown>): void;
25
+ }
26
+ export interface ManifestAdoptDeps {
27
+ manifestProvider: IManifestProvider;
28
+ manifestVersionStore: IManifestVersionStore;
29
+ /** Locally-pinned officer root keys (CELLO_CONSORTIUM_ROOT_KEYS). */
30
+ rootKeys: readonly string[];
31
+ threshold: number;
32
+ logger: ManifestPollLogger;
33
+ }
34
+ export type ManifestPollFailureReason = "manifest_http_unreachable" | "manifest_malformed" | "manifest_threshold_invalid" | "manifest_signature_invalid" | "manifest_not_yet_valid" | "manifest_expired" | "manifest_version_rollback" | "manifest_store_error";
35
+ export type ManifestPollOutcome = {
36
+ ok: true;
37
+ adopted: boolean;
38
+ oldVersion: number | null;
39
+ newVersion: number;
40
+ } | {
41
+ ok: false;
42
+ reason: ManifestPollFailureReason;
43
+ };
44
+ /**
45
+ * Fetch the consortium manifest over HTTP, verify it (TUF), and adopt it if it is a
46
+ * newer, validly-signed, unexpired manifest. Never throws — a poll failure returns a
47
+ * distinct reason and leaves the cached manifest + trusted version untouched (DB-001).
48
+ */
49
+ export declare function pollManifestOverHttp(opts: {
50
+ directoryUrl: string;
51
+ fetchFn?: typeof fetch;
52
+ now?: Date;
53
+ correlationId?: string;
54
+ } & ManifestAdoptDeps): Promise<ManifestPollOutcome>;
55
+ /**
56
+ * Wire the daemon-level HTTP manifest poll onto a scheduler. Self-rearming: each tick
57
+ * polls then schedules the next. Returns a stop function. The poll runs regardless of
58
+ * how many agents exist (including zero) — it has no agent identity and opens no
59
+ * signaling connection.
60
+ */
61
+ export declare function startHttpManifestPoll(opts: {
62
+ scheduler: IManifestPollScheduler;
63
+ directoryUrl: string;
64
+ fetchFn?: typeof fetch;
65
+ mintCorrelationId?: () => string;
66
+ } & ManifestAdoptDeps): () => void;
67
+ //# sourceMappingURL=http-manifest-poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-manifest-poll.d.ts","sourceRoot":"","sources":["../src/http-manifest-poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAElH,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,oBAAoB,EAAE,qBAAqB,CAAC;IAC5C,qEAAqE;IACrE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,MAAM,yBAAyB,GACjC,2BAA2B,GAC3B,oBAAoB,GACpB,4BAA4B,GAC5B,4BAA4B,GAC5B,wBAAwB,GACxB,kBAAkB,GAClB,2BAA2B,GAC3B,sBAAsB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAA;CAAE,CAAC;AAIrD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,iBAAiB,GACpB,OAAO,CAAC,mBAAmB,CAAC,CA+F9B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IACJ,SAAS,EAAE,sBAAsB,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAC;CAClC,GAAG,iBAAiB,GACpB,MAAM,IAAI,CA6BZ"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * CELLO-M7-CONN-001 (DOD-CONN-3) — HTTP consortium-manifest poll.
3
+ *
4
+ * The manifest poll is the ONE daemon-level directory operation. With the keystone
5
+ * deleted there is no shared authenticated signaling stream to carry it, so it moves
6
+ * to unauthenticated HTTP: GET ${directoryUrl}/manifest. The manifest is PUBLIC,
7
+ * self-authenticating data — threshold-signed by officer root keys that the daemon has
8
+ * pinned LOCALLY (CELLO_CONSORTIUM_ROOT_KEYS). The directory is ONLY a transport, never
9
+ * a trust anchor: a rogue/compromised directory cannot make us adopt a forged, expired,
10
+ * not-yet-valid, or rolled-back manifest. The verify-before-adopt policy here is the
11
+ * SAME TUF policy the deleted keystone signaling path enforced (SignalingManager
12
+ * .handleManifestPollResponse), preserved unchanged across the transport move.
13
+ *
14
+ * Runs daemon-level — even with ZERO agents (the property the keystone, which borrowed
15
+ * the primary agent's identity to authenticate, could never provide).
16
+ *
17
+ * Crypto reference: RFC 8032 (Ed25519 threshold verification via verifyManifest).
18
+ */
19
+ import { verifyManifest } from "@cello-protocol/crypto";
20
+ const FETCH_TIMEOUT_MS = 10_000;
21
+ /**
22
+ * Fetch the consortium manifest over HTTP, verify it (TUF), and adopt it if it is a
23
+ * newer, validly-signed, unexpired manifest. Never throws — a poll failure returns a
24
+ * distinct reason and leaves the cached manifest + trusted version untouched (DB-001).
25
+ */
26
+ export async function pollManifestOverHttp(opts) {
27
+ const { directoryUrl, fetchFn = fetch, now = new Date(), correlationId, manifestProvider, manifestVersionStore, rootKeys, threshold, logger, } = opts;
28
+ const manifestUrl = `${directoryUrl.replace(/\/$/, "")}/manifest`;
29
+ // 1. Fetch. Any network error / non-200 / timeout → unreachable. DB-001: the caller
30
+ // keeps the cached manifest; a poll failure never disturbs agent connections.
31
+ let raw;
32
+ try {
33
+ const ac = new AbortController();
34
+ const timer = setTimeout(() => ac.abort(), FETCH_TIMEOUT_MS);
35
+ let resp;
36
+ try {
37
+ resp = await fetchFn(manifestUrl, { signal: ac.signal });
38
+ }
39
+ finally {
40
+ clearTimeout(timer);
41
+ }
42
+ if (!resp.ok) {
43
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_http_unreachable", status: resp.status, directoryUrl, correlationId });
44
+ return { ok: false, reason: "manifest_http_unreachable" };
45
+ }
46
+ raw = await resp.json();
47
+ }
48
+ catch (err) {
49
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_http_unreachable", directoryUrl, correlationId, error: err instanceof Error ? err.message : String(err) });
50
+ return { ok: false, reason: "manifest_http_unreachable" };
51
+ }
52
+ // 2. Structural check before verify — a junk body must fail loudly, not be cached.
53
+ const manifest = raw;
54
+ if (typeof manifest?.version !== "number" ||
55
+ !Array.isArray(manifest?.nodes) ||
56
+ !Array.isArray(manifest?.signatures)) {
57
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_malformed", directoryUrl, correlationId });
58
+ return { ok: false, reason: "manifest_malformed" };
59
+ }
60
+ // 3. Defense-in-depth: never adopt against a 0/missing threshold (verifyManifest would
61
+ // accept an unsigned manifest at threshold 0). The composition root rejects
62
+ // threshold < 1 before wiring, but guard the policy directly too.
63
+ if (threshold < 1) {
64
+ logger.error("manifest.http.poll.failed", { reason: "manifest_threshold_invalid", threshold, correlationId });
65
+ return { ok: false, reason: "manifest_threshold_invalid" };
66
+ }
67
+ // 4. Threshold signature (RFC 8032) against locally-pinned root keys.
68
+ const verifyResult = verifyManifest(manifest, rootKeys, threshold);
69
+ if (!verifyResult.ok) {
70
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_signature_invalid", manifestVersion: manifest.version, detail: verifyResult.reason, correlationId });
71
+ return { ok: false, reason: "manifest_signature_invalid" };
72
+ }
73
+ // 5. Validity window.
74
+ if (now < new Date(manifest.not_before)) {
75
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_not_yet_valid", manifestVersion: manifest.version, notBefore: manifest.not_before, correlationId });
76
+ return { ok: false, reason: "manifest_not_yet_valid" };
77
+ }
78
+ if (new Date(manifest.expires) <= now) {
79
+ logger.error("manifest.http.poll.failed", { reason: "manifest_expired", manifestVersion: manifest.version, expiresAt: manifest.expires, correlationId });
80
+ return { ok: false, reason: "manifest_expired" };
81
+ }
82
+ // 6-7. Anti-rollback + adopt. The version store / provider are I/O (the version store is
83
+ // DB-backed in production), so they can THROW — wrap them so a store error becomes a distinct,
84
+ // LOGGED failure (manifest_store_error) instead of an invisible throw that silently disables
85
+ // manifest adoption forever (fallback-finder MED).
86
+ try {
87
+ const lastSeen = await manifestVersionStore.getLastSeenVersion();
88
+ if (lastSeen !== null && manifest.version < lastSeen) {
89
+ logger.warn("manifest.http.poll.failed", { reason: "manifest_version_rollback", manifestVersion: manifest.version, lastSeenVersion: lastSeen, correlationId });
90
+ return { ok: false, reason: "manifest_version_rollback" };
91
+ }
92
+ if (lastSeen !== null && manifest.version === lastSeen) {
93
+ // Already current — nothing to adopt.
94
+ return { ok: true, adopted: false, oldVersion: lastSeen, newVersion: manifest.version };
95
+ }
96
+ const oldVersion = manifestProvider.getCurrentManifest()?.version ?? null;
97
+ manifestProvider.updateManifest(manifest);
98
+ await manifestVersionStore.persistVersion(manifest.version);
99
+ logger.info("manifest.http.poll.success", { oldVersion, newVersion: manifest.version, directoryUrl, correlationId });
100
+ return { ok: true, adopted: true, oldVersion, newVersion: manifest.version };
101
+ }
102
+ catch (err) {
103
+ logger.error("manifest.http.poll.failed", { reason: "manifest_store_error", manifestVersion: manifest.version, directoryUrl, correlationId, error: err instanceof Error ? err.message : String(err) });
104
+ return { ok: false, reason: "manifest_store_error" };
105
+ }
106
+ }
107
+ /**
108
+ * Wire the daemon-level HTTP manifest poll onto a scheduler. Self-rearming: each tick
109
+ * polls then schedules the next. Returns a stop function. The poll runs regardless of
110
+ * how many agents exist (including zero) — it has no agent identity and opens no
111
+ * signaling connection.
112
+ */
113
+ export function startHttpManifestPoll(opts) {
114
+ const { scheduler, directoryUrl, fetchFn, mintCorrelationId, manifestProvider, manifestVersionStore, rootKeys, threshold, logger } = opts;
115
+ let stopped = false;
116
+ const tick = async () => {
117
+ if (stopped)
118
+ return;
119
+ await pollManifestOverHttp({
120
+ directoryUrl,
121
+ fetchFn,
122
+ correlationId: mintCorrelationId?.(),
123
+ manifestProvider,
124
+ manifestVersionStore,
125
+ rootKeys,
126
+ threshold,
127
+ logger,
128
+ }).catch((err) => {
129
+ // pollManifestOverHttp is designed never to throw (all failures return a reason). If it ever
130
+ // does, LOG it (never swallow silently — that would hide a permanently-disabled poll) and let
131
+ // the loop self-heal by re-arming below.
132
+ logger.error("manifest.http.poll.failed", { reason: "manifest_poll_unexpected_error", directoryUrl, error: err instanceof Error ? err.message : String(err) });
133
+ });
134
+ if (!stopped)
135
+ scheduler.scheduleNext(tick);
136
+ };
137
+ scheduler.scheduleNext(tick);
138
+ return () => {
139
+ stopped = true;
140
+ scheduler.cancel();
141
+ };
142
+ }
143
+ //# sourceMappingURL=http-manifest-poll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-manifest-poll.js","sourceRoot":"","sources":["../src/http-manifest-poll.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@
23
23
  */
24
24
  import { existsSync, readFileSync, readdirSync, statSync, renameSync, unlinkSync, } from "node:fs";
25
25
  import { join, dirname } from "node:path";
26
- import { createDecipheriv } from "node:crypto";
26
+ import { createDecipheriv, randomUUID } from "node:crypto";
27
27
  import { DatabaseSync } from "node:sqlite";
28
28
  import { InMemoryKeyProvider, decodeKeyFileSeed } from "@cello-protocol/crypto";
29
29
  import { openEncryptedDatabase, isPlaintextSqliteFile, resolveDbKey, dbKeyPathFor, } from "./sqlcipher-db.js";
@@ -357,10 +357,15 @@ function importFlatIdentity(celloDir, encDb, logger) {
357
357
  const agents = discoverFlatAgents(celloDir);
358
358
  let count = 0;
359
359
  for (const a of agents) {
360
- // Skip an agent that already exists in the encrypted DB (e.g. a prior in-place run imported it but
361
- // a cleanup unlink failed) — never overwrite live DB identity with stale flat-file values
362
- // (fallback-finder / code-review LOW). deleteFlatIdentity will remove the redundant files.
363
- const exists = encDb.prepare("SELECT 1 AS one FROM agents WHERE agent_name = ?").get(a.name);
360
+ // Skip an agent whose name is held by an ACTIVE row in the encrypted DB (e.g. a prior in-place run
361
+ // imported it but a cleanup unlink failed) — never overwrite live DB identity with stale flat-file
362
+ // values (fallback-finder / code-review LOW). REMOVE-001: a RETIRED tombstone does NOT occupy the
363
+ // name (it was freed for reuse), so qualify active-only matching hasActiveAgent everywhere else —
364
+ // otherwise a genuinely different flat identity restored under a freed name would be silently
365
+ // skipped and then deleted. deleteFlatIdentity will remove the redundant files.
366
+ const exists = encDb
367
+ .prepare("SELECT 1 AS one FROM agents WHERE agent_name = ? AND state != 'retired'")
368
+ .get(a.name);
364
369
  if (exists)
365
370
  continue;
366
371
  // K_local seed from the 37-byte CELLO key file. A corrupt key SKIPS the WHOLE agent — its key AND
@@ -386,10 +391,11 @@ function importFlatIdentity(celloDir, encDb, logger) {
386
391
  const link = readJson(join(a.dir, REG_FILES.link));
387
392
  const state = reg ? "registered" : "created";
388
393
  const now = Date.now();
394
+ // REMOVE-001: the store is keyed by a stable agent_id — mint one for each imported flat agent.
389
395
  encDb
390
- .prepare(`INSERT OR IGNORE INTO agents (agent_name, k_local_seed, k_local_pubkey, state, created_at, updated_at)
391
- VALUES (?, ?, ?, ?, ?, ?)`)
392
- .run(a.name, Buffer.from(seed), pubkeyHex, state, now, now);
396
+ .prepare(`INSERT OR IGNORE INTO agents (agent_id, agent_name, k_local_seed, k_local_pubkey, state, created_at, updated_at)
397
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
398
+ .run(randomUUID(), a.name, Buffer.from(seed), pubkeyHex, state, now, now);
393
399
  if (mlDsa) {
394
400
  encDb
395
401
  .prepare("UPDATE agents SET ml_dsa_pubkey = ?, ml_dsa_secret = ?, ml_dsa_algorithm = ? WHERE agent_name = ?")