@cello-protocol/daemon 0.0.181 → 0.0.182
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/agent-id-migration.d.ts +9 -0
- package/dist/agent-id-migration.d.ts.map +1 -1
- package/dist/agent-id-migration.js +73 -1
- package/dist/agent-id-migration.js.map +1 -1
- package/dist/agent-selection.d.ts +73 -0
- package/dist/agent-selection.d.ts.map +1 -0
- package/dist/agent-selection.js +80 -0
- package/dist/agent-selection.js.map +1 -0
- package/dist/assignment-verify.d.ts +62 -0
- package/dist/assignment-verify.d.ts.map +1 -0
- package/dist/assignment-verify.js +185 -0
- package/dist/assignment-verify.js.map +1 -0
- package/dist/backup-restore.d.ts +102 -0
- package/dist/backup-restore.d.ts.map +1 -0
- package/dist/backup-restore.js +340 -0
- package/dist/backup-restore.js.map +1 -0
- package/dist/bin/cello-daemon.js +91 -49
- package/dist/bin/cello-daemon.js.map +1 -1
- package/dist/close-commitment.d.ts +67 -0
- package/dist/close-commitment.d.ts.map +1 -0
- package/dist/close-commitment.js +78 -0
- package/dist/close-commitment.js.map +1 -0
- package/dist/close-session-handler.d.ts +20 -0
- package/dist/close-session-handler.d.ts.map +1 -1
- package/dist/close-session-handler.js +351 -58
- package/dist/close-session-handler.js.map +1 -1
- package/dist/consortium-bootstrap.d.ts +2 -0
- package/dist/consortium-bootstrap.d.ts.map +1 -1
- package/dist/consortium-bootstrap.js +65 -7
- package/dist/consortium-bootstrap.js.map +1 -1
- package/dist/content-park.d.ts.map +1 -1
- package/dist/content-park.js +141 -10
- package/dist/content-park.js.map +1 -1
- package/dist/daemon.d.ts +13 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +866 -122
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-session-suspects.d.ts.map +1 -1
- package/dist/delivery-session-suspects.js +5 -0
- package/dist/delivery-session-suspects.js.map +1 -1
- package/dist/directory-auth-posture.d.ts +87 -0
- package/dist/directory-auth-posture.d.ts.map +1 -0
- package/dist/directory-auth-posture.js +134 -0
- package/dist/directory-auth-posture.js.map +1 -0
- package/dist/directory-bootstrap.d.ts +66 -3
- package/dist/directory-bootstrap.d.ts.map +1 -1
- package/dist/directory-bootstrap.js +133 -7
- package/dist/directory-bootstrap.js.map +1 -1
- package/dist/document-delivery-transport.d.ts +22 -2
- package/dist/document-delivery-transport.d.ts.map +1 -1
- package/dist/document-delivery-transport.js +11 -3
- package/dist/document-delivery-transport.js.map +1 -1
- package/dist/inbound-sessions.d.ts +31 -1
- package/dist/inbound-sessions.d.ts.map +1 -1
- package/dist/inbound-sessions.js +276 -16
- package/dist/inbound-sessions.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc-server.d.ts +5 -1
- package/dist/ipc-server.d.ts.map +1 -1
- package/dist/ipc-server.js +19 -3
- package/dist/ipc-server.js.map +1 -1
- package/dist/manifest-validity.d.ts +151 -0
- package/dist/manifest-validity.d.ts.map +1 -0
- package/dist/manifest-validity.js +268 -0
- package/dist/manifest-validity.js.map +1 -0
- package/dist/notification-handlers.d.ts.map +1 -1
- package/dist/notification-handlers.js +15 -0
- package/dist/notification-handlers.js.map +1 -1
- package/dist/outbound-sessions.d.ts +31 -0
- package/dist/outbound-sessions.d.ts.map +1 -1
- package/dist/outbound-sessions.js +198 -17
- package/dist/outbound-sessions.js.map +1 -1
- package/dist/park-envelope.d.ts +72 -1
- package/dist/park-envelope.d.ts.map +1 -1
- package/dist/park-envelope.js +119 -4
- package/dist/park-envelope.js.map +1 -1
- package/dist/refusal-reasons.d.ts +72 -0
- package/dist/refusal-reasons.d.ts.map +1 -0
- package/dist/refusal-reasons.js +69 -0
- package/dist/refusal-reasons.js.map +1 -0
- package/dist/registration-manager.d.ts.map +1 -1
- package/dist/registration-manager.js +20 -1
- package/dist/registration-manager.js.map +1 -1
- package/dist/restart-seal-resolver.d.ts.map +1 -1
- package/dist/restart-seal-resolver.js +6 -0
- package/dist/restart-seal-resolver.js.map +1 -1
- package/dist/retry-queue.d.ts +23 -1
- package/dist/retry-queue.d.ts.map +1 -1
- package/dist/retry-queue.js +37 -9
- package/dist/retry-queue.js.map +1 -1
- package/dist/roster-freshness.d.ts +169 -0
- package/dist/roster-freshness.d.ts.map +1 -0
- package/dist/roster-freshness.js +249 -0
- package/dist/roster-freshness.js.map +1 -0
- package/dist/seal-certificate-pull.d.ts +17 -1
- package/dist/seal-certificate-pull.d.ts.map +1 -1
- package/dist/seal-certificate-pull.js +37 -3
- package/dist/seal-certificate-pull.js.map +1 -1
- package/dist/seal-coordinator.d.ts +17 -1
- package/dist/seal-coordinator.d.ts.map +1 -1
- package/dist/seal-coordinator.js +71 -0
- package/dist/seal-coordinator.js.map +1 -1
- package/dist/seal-failure-store.d.ts +104 -0
- package/dist/seal-failure-store.d.ts.map +1 -0
- package/dist/seal-failure-store.js +126 -0
- package/dist/seal-failure-store.js.map +1 -0
- package/dist/seal-flows.d.ts.map +1 -1
- package/dist/seal-flows.js +35 -6
- package/dist/seal-flows.js.map +1 -1
- package/dist/seal-upgrade.d.ts +20 -5
- package/dist/seal-upgrade.d.ts.map +1 -1
- package/dist/seal-upgrade.js +28 -5
- package/dist/seal-upgrade.js.map +1 -1
- package/dist/session-assignment-parser.d.ts +41 -5
- package/dist/session-assignment-parser.d.ts.map +1 -1
- package/dist/session-assignment-parser.js +77 -14
- package/dist/session-assignment-parser.js.map +1 -1
- package/dist/session-ceremony.d.ts +29 -0
- package/dist/session-ceremony.d.ts.map +1 -1
- package/dist/session-ceremony.js +71 -0
- package/dist/session-ceremony.js.map +1 -1
- package/dist/session-connection-gater.d.ts +46 -7
- package/dist/session-connection-gater.d.ts.map +1 -1
- package/dist/session-connection-gater.js +163 -12
- package/dist/session-connection-gater.js.map +1 -1
- package/dist/session-content-handlers.d.ts.map +1 -1
- package/dist/session-content-handlers.js +110 -4
- package/dist/session-content-handlers.js.map +1 -1
- package/dist/session-node-manager.d.ts +406 -36
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +3025 -245
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-read-handlers.d.ts +27 -0
- package/dist/session-read-handlers.d.ts.map +1 -1
- package/dist/session-read-handlers.js +112 -6
- package/dist/session-read-handlers.js.map +1 -1
- package/dist/session-relay-client.d.ts +61 -2
- package/dist/session-relay-client.d.ts.map +1 -1
- package/dist/session-relay-client.js +170 -11
- package/dist/session-relay-client.js.map +1 -1
- package/dist/session-salt-agreement.d.ts +284 -0
- package/dist/session-salt-agreement.d.ts.map +1 -0
- package/dist/session-salt-agreement.js +371 -0
- package/dist/session-salt-agreement.js.map +1 -0
- package/dist/signal-submission.d.ts.map +1 -1
- package/dist/signal-submission.js +10 -1
- package/dist/signal-submission.js.map +1 -1
- package/dist/signaling-connect.d.ts.map +1 -1
- package/dist/signaling-connect.js +42 -5
- package/dist/signaling-connect.js.map +1 -1
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +10 -0
- package/dist/vocabulary.js.map +1 -1
- package/dist/wire-content-hash.d.ts +56 -0
- package/dist/wire-content-hash.d.ts.map +1 -1
- package/dist/wire-content-hash.js +71 -0
- package/dist/wire-content-hash.js.map +1 -1
- package/package.json +5 -5
|
@@ -41,6 +41,15 @@
|
|
|
41
41
|
*/
|
|
42
42
|
import type { Logger } from "./types.js";
|
|
43
43
|
import { type DaemonDatabase } from "./sqlcipher-db.js";
|
|
44
|
+
/**
|
|
45
|
+
* The tables this migration rebuilds, derived from `REKEY_TARGETS` rather than written out again.
|
|
46
|
+
*
|
|
47
|
+
* Exported for the guards (`dod-m15-migration-guard-1`), which must cover exactly what the
|
|
48
|
+
* migration touches. A hand-typed copy over there would silently get SHORTER than this list the day
|
|
49
|
+
* an eighth table is added — so the one table nobody remembered to guard would be the one table
|
|
50
|
+
* nobody checks, which is the shape of every escape this migration has already had.
|
|
51
|
+
*/
|
|
52
|
+
export declare const REKEYED_TABLES: readonly string[];
|
|
44
53
|
export interface AgentIdMigrationResult {
|
|
45
54
|
readonly migrated: boolean;
|
|
46
55
|
/** Rows copied, per rebuilt table. Absent tables are omitted. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-id-migration.d.ts","sourceRoot":"","sources":["../src/agent-id-migration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-id-migration.d.ts","sourceRoot":"","sources":["../src/agent-id-migration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAuO5E;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAAsC,CAAC;AAEnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAyDD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,sBAAsB,CAyIxG"}
|
|
@@ -79,6 +79,21 @@ const REKEY_TARGETS = [
|
|
|
79
79
|
interrupted_by TEXT,
|
|
80
80
|
restart_seal_gave_up_at INTEGER,
|
|
81
81
|
restart_seal_gave_up_reason TEXT,
|
|
82
|
+
-- DOD-M15-DIVERGE-DURABLE-1. Omitting it here would delete the flag on the one boot a
|
|
83
|
+
-- legacy database upgrades, and a dropped divergence reads as HEALTHY — exactly the
|
|
84
|
+
-- failure the column was added to prevent, reintroduced by the migration that carries it.
|
|
85
|
+
diverged_at INTEGER,
|
|
86
|
+
-- Decisions Carried #8 — the session salt. Omitting it here drops the salt on the one boot
|
|
87
|
+
-- a legacy database upgrades, and a dropped salt is WORSE than no salt: every leaf hashed
|
|
88
|
+
-- under it becomes unverifiable, and the "does this session already have one?" lookup then
|
|
89
|
+
-- mints a fresh salt and splits the transcript silently. Same trap as diverged_at above.
|
|
90
|
+
content_salt BLOB,
|
|
91
|
+
-- DOD-M15-FREEZE-STATUS-1, carried for CELLO_Support's lane. Dropping these on upgrade would
|
|
92
|
+
-- un-freeze a session frozen because a party signed with a key that was not the
|
|
93
|
+
-- counterparty's — the failure that column exists to survive, reintroduced by the migration
|
|
94
|
+
-- that carries it.
|
|
95
|
+
frozen_at INTEGER,
|
|
96
|
+
frozen_reason TEXT,
|
|
82
97
|
PRIMARY KEY (agent_id, session_id)
|
|
83
98
|
)`,
|
|
84
99
|
indexSql: () => [],
|
|
@@ -126,6 +141,14 @@ const REKEY_TARGETS = [
|
|
|
126
141
|
direction TEXT NOT NULL,
|
|
127
142
|
blob BLOB NOT NULL,
|
|
128
143
|
created_at INTEGER NOT NULL,
|
|
144
|
+
-- DOD-M15-SEALWIRE-1 bullet 5. THE SECOND ENTRY. session-node-manager adds these by
|
|
145
|
+
-- inline ALTER before this rebuild runs, and the rebuild copies only the INTERSECTION --
|
|
146
|
+
-- so omitting them here DELETES a message's authorship proof on the one boot a legacy
|
|
147
|
+
-- database upgrades, then re-adds the columns empty. A transcript that silently stops
|
|
148
|
+
-- proving who wrote what is worse than one that never claimed to.
|
|
149
|
+
sender_pubkey TEXT,
|
|
150
|
+
sender_sig BLOB,
|
|
151
|
+
attribution TEXT NOT NULL DEFAULT 'local_session_state',
|
|
129
152
|
PRIMARY KEY (agent_id, session_id, sequence, direction)
|
|
130
153
|
)`,
|
|
131
154
|
indexSql: () => [],
|
|
@@ -170,6 +193,43 @@ const REKEY_TARGETS = [
|
|
|
170
193
|
// old semantics for them while giving each agent its own namespace.
|
|
171
194
|
table: "retry_queue",
|
|
172
195
|
backfill: "nullable",
|
|
196
|
+
/**
|
|
197
|
+
* ⚠️ THREE COLUMNS WERE MISSING FROM THE DDL BELOW. NOTHING WAS EVER LOST — AND WHAT KEPT IT
|
|
198
|
+
* THAT WAY IS THE ORDER OF TWO CALLS, NOT A CHECK.
|
|
199
|
+
*
|
|
200
|
+
* ⚠️ THIS COMMENT PREVIOUSLY CALLED IT *"a live data-loss bug"*, and that was WRONG. The other
|
|
201
|
+
* lane disproved it against git history and I verified it independently rather than take it:
|
|
202
|
+
* this re-key shipped 2026-07-10, `structure1_cbor` / `structure2_cbor` arrived in
|
|
203
|
+
* `retry-queue.ts` on 2026-08-05, and the re-key is one-shot. Every database that has ever run
|
|
204
|
+
* it did so a month before the columns existed, so there was never an ordering record present to
|
|
205
|
+
* drop. Recording the correction here because a comment stating a live bug that is not live is
|
|
206
|
+
* exactly the artifact this milestone keeps finding — one with the FORM of a verified finding
|
|
207
|
+
* and no verification behind it.
|
|
208
|
+
*
|
|
209
|
+
* **The real property, which is narrower and more fragile than the claim it replaces.** The
|
|
210
|
+
* rebuild copies the INTERSECTION of the two column lists, so omitting a column here does drop
|
|
211
|
+
* it — that mechanism is real and is what the header at the top of this file warns about. It is
|
|
212
|
+
* unreachable for this table only because `RetryQueue`'s constructor, which owns those
|
|
213
|
+
* `ALTER TABLE`s, runs AFTER `initialize()` — so at the moment this DDL executes, the columns do
|
|
214
|
+
* not exist to be copied. `daemon.ts` builds the queue roughly 1,400 lines after it calls
|
|
215
|
+
* `initialize()`, and nothing enforces that distance but a comment saying not to reorder.
|
|
216
|
+
*
|
|
217
|
+
* So the columns are listed here anyway. They cost nothing while the order holds, and they are
|
|
218
|
+
* the difference between a silent loss and no loss on the day someone moves the construction up.
|
|
219
|
+
*
|
|
220
|
+
* What that loss WOULD cost, if it ever became reachable: those two columns are the relay's
|
|
221
|
+
* signed ordering record, carried so a parked entry is self-ordering on recovery. Without it the
|
|
222
|
+
* recipient places the content at its ARRIVAL index rather than its witnessed sequence, the two
|
|
223
|
+
* trees part, and the session can no longer seal bilaterally.
|
|
224
|
+
*
|
|
225
|
+
* `content_hash_alg` is `DOD-M15-SEALWIRE-1` part B2b's own column, added here in the same edit
|
|
226
|
+
* as the two it would otherwise have joined. It is why the omission was found at all.
|
|
227
|
+
*
|
|
228
|
+
* The existing guard could not see any of it: `dod-agent-id-joinkey-migration` replays the inline
|
|
229
|
+
* ALTERs in the real boot order, but only the `sessions` ones — `retry_queue`'s live in a
|
|
230
|
+
* different file and run from a different constructor. `DOD-M15-MIGRATION-GUARD-1` is what now
|
|
231
|
+
* covers the cross-file case.
|
|
232
|
+
*/
|
|
173
233
|
createSql: (t) => `
|
|
174
234
|
CREATE TABLE ${t} (
|
|
175
235
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -181,7 +241,10 @@ const REKEY_TARGETS = [
|
|
|
181
241
|
attempts INTEGER NOT NULL DEFAULT 1,
|
|
182
242
|
position INTEGER NOT NULL,
|
|
183
243
|
awaiting_ack INTEGER NOT NULL DEFAULT 0,
|
|
184
|
-
content_hash_hex TEXT
|
|
244
|
+
content_hash_hex TEXT,
|
|
245
|
+
structure1_cbor BLOB,
|
|
246
|
+
structure2_cbor BLOB,
|
|
247
|
+
content_hash_alg TEXT
|
|
185
248
|
)`,
|
|
186
249
|
indexSql: (t) => [
|
|
187
250
|
`CREATE INDEX IF NOT EXISTS retry_queue_by_session_position ON ${t}(session_id, position ASC)`,
|
|
@@ -189,6 +252,15 @@ const REKEY_TARGETS = [
|
|
|
189
252
|
],
|
|
190
253
|
},
|
|
191
254
|
];
|
|
255
|
+
/**
|
|
256
|
+
* The tables this migration rebuilds, derived from `REKEY_TARGETS` rather than written out again.
|
|
257
|
+
*
|
|
258
|
+
* Exported for the guards (`dod-m15-migration-guard-1`), which must cover exactly what the
|
|
259
|
+
* migration touches. A hand-typed copy over there would silently get SHORTER than this list the day
|
|
260
|
+
* an eighth table is added — so the one table nobody remembered to guard would be the one table
|
|
261
|
+
* nobody checks, which is the shape of every escape this migration has already had.
|
|
262
|
+
*/
|
|
263
|
+
export const REKEYED_TABLES = REKEY_TARGETS.map((t) => t.table);
|
|
192
264
|
function columnsOf(db, table) {
|
|
193
265
|
return db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name);
|
|
194
266
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-id-migration.js","sourceRoot":"","sources":["../src/agent-id-migration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAGH,OAAO,EAAE,kBAAkB,EAAuB,MAAM,mBAAmB,CAAC;AAkB5E;;;;;;;;;;;;;GAaG;AACH,MAAM,aAAa,GAA2B;IAC5C;QACE,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,QAAQ;QAClB,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;qBACD,CAAC
|
|
1
|
+
{"version":3,"file":"agent-id-migration.js","sourceRoot":"","sources":["../src/agent-id-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent a call acts as, and HOW that was decided — `DOD-M15-SELECTION-1`,
|
|
3
|
+
* `DOD-M15-IPCVISIBLE-1`.
|
|
4
|
+
*
|
|
5
|
+
* ─── The defect this is extracted from ─────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* This logic was four lines inside `daemon.ts`, ending in:
|
|
8
|
+
*
|
|
9
|
+
* if (onlineAgents.size === 1) return [...onlineAgents][0];
|
|
10
|
+
*
|
|
11
|
+
* A connection that had selected nothing therefore acted as whichever agent happened to be the only
|
|
12
|
+
* one online. On a shared daemon that can be a DIFFERENT operator's agent, which is how it was
|
|
13
|
+
* found: after a restart a released agent was silently reinstated under someone else's name.
|
|
14
|
+
*
|
|
15
|
+
* ─── Why the fallback is worse than it looks even when it picks the "right" agent ──────────────
|
|
16
|
+
*
|
|
17
|
+
* It produces a HALF-ATTENDED state. Tools resolve a subject and work; the notification dispatcher
|
|
18
|
+
* routes doorbells by the connection's REGISTERED current agent, which this never sets. So the
|
|
19
|
+
* session sends and receives on request but never wakes — and an operator reads that as the
|
|
20
|
+
* protocol dropping messages, not as a selection that was never made.
|
|
21
|
+
*
|
|
22
|
+
* ─── AND WHY IT IS STILL HERE ──────────────────────────────────────────────────────────────────
|
|
23
|
+
*
|
|
24
|
+
* My first version switched the fallback OFF for MCP on that reasoning, and four tests said no. CC-3
|
|
25
|
+
* added it deliberately, to fix "the post-/mcp-reconnect papercut": a reconnected session with
|
|
26
|
+
* exactly one agent online used to hard-fail `no_current_agent`.
|
|
27
|
+
*
|
|
28
|
+
* `DOD-M15-SELECTION-1`'s answer is narrower than removal — *"if a fallback is wanted it is EXPLICIT
|
|
29
|
+
* in the response, not announced as an accomplished fact"* — and it sequences the work: *"diagnosis
|
|
30
|
+
* first… with the trigger field from DOD-M15-IPCVISIBLE-1 distinguishing replay from fallback in one
|
|
31
|
+
* run."* So this module makes the decision ATTRIBUTABLE, which is the precondition, and changing
|
|
32
|
+
* what the fallback DOES waits for the diagnosis that attribution makes possible.
|
|
33
|
+
*
|
|
34
|
+
* ─── Every path reports how it got there ───────────────────────────────────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* Two agent switches fired one second after a reconnect on 2026-08-09 and neither could be
|
|
37
|
+
* attributed, because an operator's explicit selection and the shim's reconnect replay arrive
|
|
38
|
+
* identically. `fallback` is the third value `DOD-M15-IPCVISIBLE-1` asks for, and the one that was
|
|
39
|
+
* invisible.
|
|
40
|
+
*/
|
|
41
|
+
/** How a call's acting agent was arrived at. Every branch reports one. */
|
|
42
|
+
export type SelectionTrigger =
|
|
43
|
+
/** The caller named an agent on this call. */
|
|
44
|
+
"explicit"
|
|
45
|
+
/** The connection had selected one earlier with `cello_use_agent`. */
|
|
46
|
+
| "selected"
|
|
47
|
+
/** Nothing was selected and one agent was online — a CLI convenience, never for a live session. */
|
|
48
|
+
| "fallback"
|
|
49
|
+
/**
|
|
50
|
+
* The connection's choice was TAKEN AWAY — the agent was shut down or removed underneath it.
|
|
51
|
+
*
|
|
52
|
+
* NOT a voluntary release. `cello_stop_using_agent` deliberately does not set this: the operator
|
|
53
|
+
* choosing to hold nothing stays eligible for the sole-online fallback, whereas a connection whose
|
|
54
|
+
* agent vanished must not have a replacement guessed for it.
|
|
55
|
+
*/
|
|
56
|
+
| "cleared"
|
|
57
|
+
/** No agent could be determined. */
|
|
58
|
+
| "none";
|
|
59
|
+
export interface SelectionConnState {
|
|
60
|
+
currentAgent: string | null;
|
|
61
|
+
/** Set by `cello_stop_using_agent`: this connection HAD an intent and gave it up. */
|
|
62
|
+
clearedAgent?: string;
|
|
63
|
+
/** `"mcp"` is a live session; anything else is treated as an ephemeral CLI invocation. */
|
|
64
|
+
clientType?: string;
|
|
65
|
+
}
|
|
66
|
+
export declare function resolveCurrentAgentFor(opts: {
|
|
67
|
+
connState: SelectionConnState | undefined;
|
|
68
|
+
onlineAgents: ReadonlySet<string>;
|
|
69
|
+
explicitAgent?: string;
|
|
70
|
+
/** Called exactly once with the outcome, so the decision is attributable in the log. */
|
|
71
|
+
onResolved?: (agent: string | null, trigger: SelectionTrigger) => void;
|
|
72
|
+
}): string | null;
|
|
73
|
+
//# sourceMappingURL=agent-selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-selection.d.ts","sourceRoot":"","sources":["../src/agent-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB;AAC1B,8CAA8C;AAC5C,UAAU;AACZ,sEAAsE;GACpE,UAAU;AACZ,mGAAmG;GACjG,UAAU;AACZ;;;;;;GAMG;GACD,SAAS;AACX,oCAAoC;GAClC,MAAM,CAAC;AAEX,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,SAAS,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC1C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACxE,GAAG,MAAM,GAAG,IAAI,CAsChB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent a call acts as, and HOW that was decided — `DOD-M15-SELECTION-1`,
|
|
3
|
+
* `DOD-M15-IPCVISIBLE-1`.
|
|
4
|
+
*
|
|
5
|
+
* ─── The defect this is extracted from ─────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* This logic was four lines inside `daemon.ts`, ending in:
|
|
8
|
+
*
|
|
9
|
+
* if (onlineAgents.size === 1) return [...onlineAgents][0];
|
|
10
|
+
*
|
|
11
|
+
* A connection that had selected nothing therefore acted as whichever agent happened to be the only
|
|
12
|
+
* one online. On a shared daemon that can be a DIFFERENT operator's agent, which is how it was
|
|
13
|
+
* found: after a restart a released agent was silently reinstated under someone else's name.
|
|
14
|
+
*
|
|
15
|
+
* ─── Why the fallback is worse than it looks even when it picks the "right" agent ──────────────
|
|
16
|
+
*
|
|
17
|
+
* It produces a HALF-ATTENDED state. Tools resolve a subject and work; the notification dispatcher
|
|
18
|
+
* routes doorbells by the connection's REGISTERED current agent, which this never sets. So the
|
|
19
|
+
* session sends and receives on request but never wakes — and an operator reads that as the
|
|
20
|
+
* protocol dropping messages, not as a selection that was never made.
|
|
21
|
+
*
|
|
22
|
+
* ─── AND WHY IT IS STILL HERE ──────────────────────────────────────────────────────────────────
|
|
23
|
+
*
|
|
24
|
+
* My first version switched the fallback OFF for MCP on that reasoning, and four tests said no. CC-3
|
|
25
|
+
* added it deliberately, to fix "the post-/mcp-reconnect papercut": a reconnected session with
|
|
26
|
+
* exactly one agent online used to hard-fail `no_current_agent`.
|
|
27
|
+
*
|
|
28
|
+
* `DOD-M15-SELECTION-1`'s answer is narrower than removal — *"if a fallback is wanted it is EXPLICIT
|
|
29
|
+
* in the response, not announced as an accomplished fact"* — and it sequences the work: *"diagnosis
|
|
30
|
+
* first… with the trigger field from DOD-M15-IPCVISIBLE-1 distinguishing replay from fallback in one
|
|
31
|
+
* run."* So this module makes the decision ATTRIBUTABLE, which is the precondition, and changing
|
|
32
|
+
* what the fallback DOES waits for the diagnosis that attribution makes possible.
|
|
33
|
+
*
|
|
34
|
+
* ─── Every path reports how it got there ───────────────────────────────────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* Two agent switches fired one second after a reconnect on 2026-08-09 and neither could be
|
|
37
|
+
* attributed, because an operator's explicit selection and the shim's reconnect replay arrive
|
|
38
|
+
* identically. `fallback` is the third value `DOD-M15-IPCVISIBLE-1` asks for, and the one that was
|
|
39
|
+
* invisible.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveCurrentAgentFor(opts) {
|
|
42
|
+
const report = (agent, trigger) => {
|
|
43
|
+
opts.onResolved?.(agent, trigger);
|
|
44
|
+
return agent;
|
|
45
|
+
};
|
|
46
|
+
if (opts.explicitAgent)
|
|
47
|
+
return report(opts.explicitAgent, "explicit");
|
|
48
|
+
if (opts.connState?.currentAgent)
|
|
49
|
+
return report(opts.connState.currentAgent, "selected");
|
|
50
|
+
/**
|
|
51
|
+
* AN INVOLUNTARY CLEAR BLOCKS THE FALLBACK. Pre-existing behaviour, preserved.
|
|
52
|
+
*
|
|
53
|
+
* `clearedAgent` means the connection's agent was shut down or removed underneath it — do not
|
|
54
|
+
* guess a replacement. A VOLUNTARY release (`cello_stop_using_agent`) deliberately does not set
|
|
55
|
+
* it, because the operator choosing to hold nothing is not the same as having the choice taken,
|
|
56
|
+
* and they stay eligible for the fallback.
|
|
57
|
+
*/
|
|
58
|
+
if (opts.connState?.clearedAgent)
|
|
59
|
+
return report(null, "cleared");
|
|
60
|
+
/**
|
|
61
|
+
* THE SOLE-ONLINE FALLBACK — behaviour UNCHANGED, visibility added.
|
|
62
|
+
*
|
|
63
|
+
* `DOD-M15-SELECTION-1` names this as the place a live MCP session gets bound to an identity it
|
|
64
|
+
* never asked for, and the instinct is to switch it off for MCP. That is wrong, and the tests say
|
|
65
|
+
* why: CC-3 introduced it deliberately to fix "the post-/mcp-reconnect papercut", where a
|
|
66
|
+
* reconnected session hard-failed `no_current_agent` with exactly one agent online.
|
|
67
|
+
*
|
|
68
|
+
* The line's own answer is narrower than removal: *"if a fallback is wanted it is EXPLICIT in the
|
|
69
|
+
* response, not announced as an accomplished fact."* And it sequences the work — *"diagnosis
|
|
70
|
+
* first… with the trigger field from DOD-M15-IPCVISIBLE-1 distinguishing replay from fallback in
|
|
71
|
+
* one run."* So this reports itself, and changing what it DOES waits for that diagnosis.
|
|
72
|
+
*
|
|
73
|
+
* Exactly one, never a guess between two: choosing between identities is a coin flip with the
|
|
74
|
+
* operator's name on it, not a convenience.
|
|
75
|
+
*/
|
|
76
|
+
if (opts.onlineAgents.size === 1)
|
|
77
|
+
return report([...opts.onlineAgents][0], "fallback");
|
|
78
|
+
return report(null, "none");
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=agent-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-selection.js","sourceRoot":"","sources":["../src/agent-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AA6BH,MAAM,UAAU,sBAAsB,CAAC,IAMtC;IACC,MAAM,MAAM,GAAG,CAAC,KAAoB,EAAE,OAAyB,EAAiB,EAAE;QAChF,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,IAAI,IAAI,CAAC,aAAa;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IACtE,IAAI,IAAI,CAAC,SAAS,EAAE,YAAY;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAEzF;;;;;;;OAOG;IACH,IAAI,IAAI,CAAC,SAAS,EAAE,YAAY;QAAE,OAAO,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAEjE;;;;;;;;;;;;;;;OAeG;IACH,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAE,EAAE,UAAU,CAAC,CAAC;IAExF,OAAO,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SessionAssignment } from "@cello-protocol/protocol-types";
|
|
2
|
+
import type { DbRegistrationPersistence } from "./db-identity-store.js";
|
|
3
|
+
import type { Logger } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* DOD-M15-ASSIGN-1 — verify a session assignment's signature before anything trusts it.
|
|
6
|
+
*
|
|
7
|
+
* REFUSES, never logs-and-continues. The whole finding here is a comment naming a verification the
|
|
8
|
+
* tree does not perform, so a check that ran and then let the session proceed anyway would be the
|
|
9
|
+
* same defect with more code. `DOD-M15-ASSIGN-1`'s receiver gate reads this assignment; gating on a
|
|
10
|
+
* document nobody verified relocates trust rather than closing it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function verifyAssignmentSignature(assignment: SessionAssignment, persistence: DbRegistrationPersistence, logger: Logger, agentName: string, correlationId: string): Promise<{
|
|
13
|
+
ok: true;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
reason: string;
|
|
17
|
+
guidance: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* DOD-M15-RESPONDER-VERIFY-1 — the RESPONDER's half, which until now did not exist.
|
|
21
|
+
*
|
|
22
|
+
* The initiator verifies its assignment against its own threshold group key (above). The responder
|
|
23
|
+
* did not verify at all: it logged `session.inbound.assignment.unverified` and proceeded, so every
|
|
24
|
+
* field it then acted on — the dialer it opens its receiver to, the `signer_pubkey` it persists as
|
|
25
|
+
* the seal trust anchor — was whatever the directory said.
|
|
26
|
+
*
|
|
27
|
+
* ─── Why this could not simply reuse the function above ────────────────────────────────────────
|
|
28
|
+
*
|
|
29
|
+
* That one compares `signer_pubkey` against the agent's OWN persisted `primaryPubkey`. The responder
|
|
30
|
+
* is not the signer — the assignment is signed by the INITIATOR's quorum — so it has no equivalent
|
|
31
|
+
* value, and verifying a frame's signature against a key from the same frame is circular: mint a
|
|
32
|
+
* key, sign with it, name it. That circularity is why the check was deferred rather than written.
|
|
33
|
+
*
|
|
34
|
+
* ─── What breaks the circle ────────────────────────────────────────────────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* The TOFU pin. For a counterparty this agent has completed a session with, `expectedSignerHex` is
|
|
37
|
+
* what THIS daemon recorded then — a value no directory can retroactively change. Verifying against
|
|
38
|
+
* that is a real check: a compromised directory can produce a signature, but not one that verifies
|
|
39
|
+
* under a key it does not hold.
|
|
40
|
+
*
|
|
41
|
+
* ─── Two modes, and the weaker one is honest about being weaker ────────────────────────────────
|
|
42
|
+
*
|
|
43
|
+
* PINNED (`expectedSignerHex` given): the signature must verify under the PINNED key. Non-circular.
|
|
44
|
+
* This is the mode that matters, and it covers every repeat counterparty.
|
|
45
|
+
*
|
|
46
|
+
* UNPINNED (first contact): there is nothing independent to verify against, so this checks INTERNAL
|
|
47
|
+
* CONSISTENCY only — the signature verifies over the TBS recomputed from the assignment's own
|
|
48
|
+
* contents, under the key the frame names. That does NOT authenticate the directory, and it is not
|
|
49
|
+
* claimed to. What it does catch is a TAMPERED or garbage assignment — one whose fields were altered
|
|
50
|
+
* after signing, or whose signature is noise — which today reaches the seal path unchallenged and
|
|
51
|
+
* gets PINNED, poisoning every later session with that counterparty. Refusing it here is what stops
|
|
52
|
+
* a bad first contact becoming a permanent one.
|
|
53
|
+
*/
|
|
54
|
+
export declare function verifyInboundAssignment(assignment: SessionAssignment, expectedSignerHex: string | null): {
|
|
55
|
+
ok: true;
|
|
56
|
+
mode: "pinned" | "internal";
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
reason: string;
|
|
60
|
+
detail: string;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=assignment-verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignment-verify.d.ts","sourceRoot":"","sources":["../src/assignment-verify.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,iBAAiB,EAC7B,WAAW,EAAE,yBAAyB,EACtC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0GzE;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,iBAAiB,EAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAsD3F"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* assignment-verify.ts — DOD-M15-ASSIGN-1.
|
|
3
|
+
*
|
|
4
|
+
* `session-assignment-parser.ts` shape-validates a session assignment. Its header said the
|
|
5
|
+
* signature "is verified downstream by the transport/session layer against the directory's pinned
|
|
6
|
+
* key" — and that site did not exist: `buildSessionEstablishmentTbs` was called to SIGN, and
|
|
7
|
+
* nowhere to verify. This module is the downstream the comment promised.
|
|
8
|
+
*
|
|
9
|
+
* SEPARATE FROM THE PARSER on purpose. Parsing answers "is this the right shape"; verifying answers
|
|
10
|
+
* "did the right party say it". Keeping them apart means the second cannot be quietly skipped by a
|
|
11
|
+
* caller that only wanted the first — which is how the gap existed.
|
|
12
|
+
*/
|
|
13
|
+
// `verify` (plain Ed25519) is deliberately NOT imported: the single-key branch that used it was
|
|
14
|
+
// removed with the downgrade refusal (review F1). Verifying `directory_signature` against the
|
|
15
|
+
// `directory_pubkey` riding beside it in the same unsigned frame checks a key against itself, and
|
|
16
|
+
// having that call available is how a future edit reintroduces the bypass.
|
|
17
|
+
import { verifyFrostSignature, CONTEXT_SESSION_ESTABLISHMENT } from "@cello-protocol/crypto";
|
|
18
|
+
import { buildSessionEstablishmentTbs, computeGenesisPrevRoot } from "@cello-protocol/protocol-types";
|
|
19
|
+
/**
|
|
20
|
+
* DOD-M15-ASSIGN-1 — verify a session assignment's signature before anything trusts it.
|
|
21
|
+
*
|
|
22
|
+
* REFUSES, never logs-and-continues. The whole finding here is a comment naming a verification the
|
|
23
|
+
* tree does not perform, so a check that ran and then let the session proceed anyway would be the
|
|
24
|
+
* same defect with more code. `DOD-M15-ASSIGN-1`'s receiver gate reads this assignment; gating on a
|
|
25
|
+
* document nobody verified relocates trust rather than closing it.
|
|
26
|
+
*/
|
|
27
|
+
export async function verifyAssignmentSignature(assignment, persistence, logger, agentName, correlationId) {
|
|
28
|
+
// RECOMPUTED, not taken from the frame — and that is a strengthening rather than a workaround.
|
|
29
|
+
// `genesis_prev_root` is not on the wire at all: the directory derives it from
|
|
30
|
+
// (pubA, pubB, session_id, timestamp) and so must we. A value we derive cannot be chosen for us,
|
|
31
|
+
// so the TBS we verify against is anchored to the assignment's own participants and id.
|
|
32
|
+
const genesisPrevRoot = computeGenesisPrevRoot(assignment.participant_a.pubkey, assignment.participant_b.pubkey, assignment.session_id, assignment.session_timestamp);
|
|
33
|
+
const tbs = buildSessionEstablishmentTbs(assignment.session_id, assignment.participant_a.pubkey, assignment.participant_b.pubkey, genesisPrevRoot, assignment.session_timestamp, assignment.initiator_session_peer_id, assignment.initiator_session_addrs, assignment.counterparty_session_peer_id, assignment.counterparty_session_addrs, assignment.transport_mode);
|
|
34
|
+
/**
|
|
35
|
+
* LOAD THE REGISTRATION BEFORE BRANCHING ON `signature_type` — review F1, and this ordering is
|
|
36
|
+
* the entire fix.
|
|
37
|
+
*
|
|
38
|
+
* `signature_type` rides in the frame and is covered by no signature. The parser reads any value
|
|
39
|
+
* that is not the string "frost" — including an ABSENT field — as "single". So while this load
|
|
40
|
+
* lived inside the frost branch, a hostile directory disabled every check below by omitting one
|
|
41
|
+
* field: it put its own freshly-minted key in `directory_pubkey`, signed a TBS naming an
|
|
42
|
+
* impostor as the counterparty, and the single-key branch verified that signature against that
|
|
43
|
+
* same key and returned ok. The anti-circularity comparison, the threshold verify and the
|
|
44
|
+
* fail-closed were all simply stepped over.
|
|
45
|
+
*
|
|
46
|
+
* Reading the registration first closes it: an agent that HAS a threshold registration knows its
|
|
47
|
+
* assignments are FROST-signed, so a non-FROST one is a downgrade attempt and is refused by
|
|
48
|
+
* name — never quietly routed to a weaker check.
|
|
49
|
+
*/
|
|
50
|
+
const reg = await persistence.loadRegistrationState();
|
|
51
|
+
if (!reg) {
|
|
52
|
+
// FAIL CLOSED. Without our own registration we cannot know whose quorum should have signed,
|
|
53
|
+
// so the signature is unverifiable — which is not the same as valid.
|
|
54
|
+
logger.warn("session.assignment.verify.no_identity", {
|
|
55
|
+
agentName, correlationId,
|
|
56
|
+
impact: "this agent has no persisted registration, so the signer of its own session assignment cannot be established; the session was refused rather than accepted unverified",
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
reason: "assignment_unverifiable_no_registration",
|
|
61
|
+
guidance: "This agent has no registration on record, so the directory's session assignment cannot be checked against the key that should have signed it. Re-register with cello register-agent, then try again.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (assignment.signature_type !== "frost") {
|
|
65
|
+
/**
|
|
66
|
+
* THE DOWNGRADE REFUSAL. Checked against the producer, not assumed: the directory constructs
|
|
67
|
+
* `signature_type: "frost"` unconditionally at a single site (`directory-node.ts`), with no
|
|
68
|
+
* branch that can emit anything else. There is no legitimate producer of a single-key session
|
|
69
|
+
* assignment for a registered agent, so this is not a compatibility path being closed — it is
|
|
70
|
+
* a shape that only an attacker or a broken directory can send.
|
|
71
|
+
*/
|
|
72
|
+
logger.error("session.assignment.signature_type_downgraded", {
|
|
73
|
+
agentName, correlationId,
|
|
74
|
+
offeredType: assignment.signature_type,
|
|
75
|
+
impact: "the assignment claimed a weaker signature type than this agent's registration can produce, which would have routed it to a check that verifies a key against itself; it was refused, and no session was established",
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
reason: "assignment_signature_type_downgraded",
|
|
80
|
+
guidance: "The directory returned a session assignment that does not carry a threshold signature, though this agent is registered with one. Nothing was established and no message was sent. Retry the session; if it repeats, the directory you reached is not producing valid assignments and cello status will show which one that is.",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
{
|
|
84
|
+
// THE ANTI-CIRCULARITY CHECK. `signer_pubkey` comes from the frame; `reg.primaryPubkey` comes
|
|
85
|
+
// from this machine's own registration. Comparing them is what makes verifying against
|
|
86
|
+
// `signer_pubkey` mean anything at all.
|
|
87
|
+
const expected = reg.primaryPubkey.toLowerCase();
|
|
88
|
+
const offered = Buffer.from(assignment.signer_pubkey).toString("hex").toLowerCase();
|
|
89
|
+
if (offered !== expected) {
|
|
90
|
+
logger.error("session.assignment.signer_mismatch", {
|
|
91
|
+
agentName, correlationId,
|
|
92
|
+
expectedPrefix: expected.slice(0, 16), offeredPrefix: offered.slice(0, 16),
|
|
93
|
+
impact: "the assignment was signed by a key that is not this agent's own threshold group key; it was refused, and no session was established",
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
reason: "assignment_signer_not_this_agent",
|
|
98
|
+
guidance: "The directory returned a session assignment signed by a key that is not this agent's own. Nothing was established and no message was sent. This is refused rather than reported as an error because the observation cannot distinguish a directory fault from an attempt to broker a session on your behalf; cause undetermined.",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (!verifyFrostSignature(assignment.directory_signature, tbs, CONTEXT_SESSION_ESTABLISHMENT, assignment.signer_pubkey)) {
|
|
102
|
+
logger.error("session.assignment.signature_invalid", {
|
|
103
|
+
agentName, correlationId, signatureType: "frost",
|
|
104
|
+
impact: "the assignment's threshold signature did not verify over its own contents; it was refused, and no session was established",
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
reason: "assignment_signature_invalid",
|
|
109
|
+
guidance: "The directory's session assignment did not verify against the key that should have signed it. Nothing was established. Retry the session; if it repeats, the directory you reached is not producing valid assignments and cello status will show which one that is.",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return { ok: true };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* DOD-M15-RESPONDER-VERIFY-1 — the RESPONDER's half, which until now did not exist.
|
|
117
|
+
*
|
|
118
|
+
* The initiator verifies its assignment against its own threshold group key (above). The responder
|
|
119
|
+
* did not verify at all: it logged `session.inbound.assignment.unverified` and proceeded, so every
|
|
120
|
+
* field it then acted on — the dialer it opens its receiver to, the `signer_pubkey` it persists as
|
|
121
|
+
* the seal trust anchor — was whatever the directory said.
|
|
122
|
+
*
|
|
123
|
+
* ─── Why this could not simply reuse the function above ────────────────────────────────────────
|
|
124
|
+
*
|
|
125
|
+
* That one compares `signer_pubkey` against the agent's OWN persisted `primaryPubkey`. The responder
|
|
126
|
+
* is not the signer — the assignment is signed by the INITIATOR's quorum — so it has no equivalent
|
|
127
|
+
* value, and verifying a frame's signature against a key from the same frame is circular: mint a
|
|
128
|
+
* key, sign with it, name it. That circularity is why the check was deferred rather than written.
|
|
129
|
+
*
|
|
130
|
+
* ─── What breaks the circle ────────────────────────────────────────────────────────────────────
|
|
131
|
+
*
|
|
132
|
+
* The TOFU pin. For a counterparty this agent has completed a session with, `expectedSignerHex` is
|
|
133
|
+
* what THIS daemon recorded then — a value no directory can retroactively change. Verifying against
|
|
134
|
+
* that is a real check: a compromised directory can produce a signature, but not one that verifies
|
|
135
|
+
* under a key it does not hold.
|
|
136
|
+
*
|
|
137
|
+
* ─── Two modes, and the weaker one is honest about being weaker ────────────────────────────────
|
|
138
|
+
*
|
|
139
|
+
* PINNED (`expectedSignerHex` given): the signature must verify under the PINNED key. Non-circular.
|
|
140
|
+
* This is the mode that matters, and it covers every repeat counterparty.
|
|
141
|
+
*
|
|
142
|
+
* UNPINNED (first contact): there is nothing independent to verify against, so this checks INTERNAL
|
|
143
|
+
* CONSISTENCY only — the signature verifies over the TBS recomputed from the assignment's own
|
|
144
|
+
* contents, under the key the frame names. That does NOT authenticate the directory, and it is not
|
|
145
|
+
* claimed to. What it does catch is a TAMPERED or garbage assignment — one whose fields were altered
|
|
146
|
+
* after signing, or whose signature is noise — which today reaches the seal path unchallenged and
|
|
147
|
+
* gets PINNED, poisoning every later session with that counterparty. Refusing it here is what stops
|
|
148
|
+
* a bad first contact becoming a permanent one.
|
|
149
|
+
*/
|
|
150
|
+
export function verifyInboundAssignment(assignment, expectedSignerHex) {
|
|
151
|
+
if (assignment.signature_type !== "frost") {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reason: "inbound_assignment_not_frost",
|
|
155
|
+
detail: `signature_type was ${String(assignment.signature_type)}; the directory produces frost for every session assignment`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const signer = assignment.signer_pubkey;
|
|
159
|
+
if (!signer || signer.length !== 32) {
|
|
160
|
+
return { ok: false, reason: "inbound_assignment_no_signer", detail: "signer_pubkey missing or not 32 bytes" };
|
|
161
|
+
}
|
|
162
|
+
// PINNED beats named. When we hold an independent value, the frame's own claim about who signed is
|
|
163
|
+
// not consulted at all — using it would reintroduce the circularity the pin exists to break.
|
|
164
|
+
const verifyAgainst = expectedSignerHex !== null ? Buffer.from(expectedSignerHex, "hex") : signer;
|
|
165
|
+
if (expectedSignerHex !== null && Buffer.from(signer).toString("hex").toLowerCase() !== expectedSignerHex.toLowerCase()) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
reason: "inbound_assignment_signer_not_pinned",
|
|
169
|
+
detail: "the assignment names a different signer than this agent recorded for this counterparty",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const genesisPrevRoot = computeGenesisPrevRoot(assignment.participant_a.pubkey, assignment.participant_b.pubkey, assignment.session_id, assignment.session_timestamp);
|
|
173
|
+
const tbs = buildSessionEstablishmentTbs(assignment.session_id, assignment.participant_a.pubkey, assignment.participant_b.pubkey, genesisPrevRoot, assignment.session_timestamp, assignment.initiator_session_peer_id, assignment.initiator_session_addrs, assignment.counterparty_session_peer_id, assignment.counterparty_session_addrs, assignment.transport_mode);
|
|
174
|
+
if (!verifyFrostSignature(assignment.directory_signature, tbs, CONTEXT_SESSION_ESTABLISHMENT, new Uint8Array(verifyAgainst))) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
reason: "inbound_assignment_signature_invalid",
|
|
178
|
+
detail: expectedSignerHex !== null
|
|
179
|
+
? "the signature did not verify under the key this agent recorded for this counterparty"
|
|
180
|
+
: "the signature did not verify over the assignment's own recomputed contents — the frame is tampered or malformed",
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return { ok: true, mode: expectedSignerHex !== null ? "pinned" : "internal" };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=assignment-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignment-verify.js","sourceRoot":"","sources":["../src/assignment-verify.ts"],"names":[],"mappings":"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"}
|