@cello-protocol/daemon 0.0.181 → 0.0.183
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/agent-settings-keys.d.ts.map +1 -1
- package/dist/agent-settings-keys.js +18 -0
- package/dist/agent-settings-keys.js.map +1 -1
- 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 +182 -9
- package/dist/consortium-bootstrap.js.map +1 -1
- package/dist/content-encryption-status.d.ts +97 -0
- package/dist/content-encryption-status.d.ts.map +1 -0
- package/dist/content-encryption-status.js +122 -0
- package/dist/content-encryption-status.js.map +1 -0
- package/dist/content-park-client.d.ts +10 -0
- package/dist/content-park-client.d.ts.map +1 -1
- package/dist/content-park-client.js +84 -3
- package/dist/content-park-client.js.map +1 -1
- package/dist/content-park.d.ts.map +1 -1
- package/dist/content-park.js +323 -21
- 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 +1168 -141
- 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/document-handlers.d.ts.map +1 -1
- package/dist/document-handlers.js +119 -0
- package/dist/document-handlers.js.map +1 -1
- package/dist/document-layer.d.ts +0 -2
- package/dist/document-layer.d.ts.map +1 -1
- package/dist/document-layer.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/initiate-session-handler.d.ts.map +1 -1
- package/dist/initiate-session-handler.js +34 -3
- package/dist/initiate-session-handler.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-deps.d.ts +0 -11
- package/dist/manifest-deps.d.ts.map +1 -1
- package/dist/manifest-deps.js +28 -2
- package/dist/manifest-deps.js.map +1 -1
- package/dist/manifest-validity.d.ts +153 -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 +217 -17
- package/dist/outbound-sessions.js.map +1 -1
- package/dist/park-envelope.d.ts +95 -1
- package/dist/park-envelope.d.ts.map +1 -1
- package/dist/park-envelope.js +195 -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/register-handler.d.ts.map +1 -1
- package/dist/register-handler.js +76 -0
- package/dist/register-handler.js.map +1 -1
- 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/relay-only.d.ts +133 -0
- package/dist/relay-only.d.ts.map +1 -0
- package/dist/relay-only.js +183 -0
- package/dist/relay-only.js.map +1 -0
- 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 +49 -2
- package/dist/retry-queue.d.ts.map +1 -1
- package/dist/retry-queue.js +55 -10
- 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 +35 -0
- package/dist/session-ceremony.d.ts.map +1 -1
- package/dist/session-ceremony.js +97 -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 +12 -0
- package/dist/session-content-handlers.d.ts.map +1 -1
- package/dist/session-content-handlers.js +149 -8
- package/dist/session-content-handlers.js.map +1 -1
- package/dist/session-node-manager.d.ts +653 -40
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +5938 -374
- 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 +127 -6
- package/dist/session-read-handlers.js.map +1 -1
- package/dist/session-relay-client.d.ts +177 -2
- package/dist/session-relay-client.d.ts.map +1 -1
- package/dist/session-relay-client.js +564 -16
- package/dist/session-relay-client.js.map +1 -1
- package/dist/session-salt-agreement.d.ts +338 -0
- package/dist/session-salt-agreement.d.ts.map +1 -0
- package/dist/session-salt-agreement.js +478 -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 +18 -1
- package/dist/signaling-connect.d.ts.map +1 -1
- package/dist/signaling-connect.js +206 -6
- package/dist/signaling-connect.js.map +1 -1
- package/dist/types.d.ts +75 -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 +68 -0
- package/dist/wire-content-hash.d.ts.map +1 -1
- package/dist/wire-content-hash.js +83 -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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-settings-keys.d.ts","sourceRoot":"","sources":["../src/agent-settings-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"agent-settings-keys.d.ts","sourceRoot":"","sources":["../src/agent-settings-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,gGAAgG;AAChG,eAAO,MAAM,mBAAmB,qDAAqE,CAAC;AACtG,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE;;4DAE4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQtE;AAED,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,WAAW,CAAC;AAEtD,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAEjF;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAEjE;AAED,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAgB/C;;0CAE0C;AAC1C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAqC7G;AAED,yGAAyG;AACzG,eAAO,MAAM,oBAAoB,OAAO,CAAC"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* refused before any reply). So the settable tiers are unknown / known / whitelisted / vip.
|
|
15
15
|
*/
|
|
16
16
|
import { TIER } from "./contacts-tier-migration.js";
|
|
17
|
+
import { RELAY_ONLY_KEY } from "./relay-only.js";
|
|
17
18
|
/** The tiers whose bounds and away texts are settable (BLOCKED is fixed, never overridable). */
|
|
18
19
|
export const SETTABLE_TIER_NAMES = Object.freeze(["unknown", "known", "whitelisted", "vip"]);
|
|
19
20
|
/** The tier NAME for a tier integer, or null for an unknown/unsettable value (e.g. BLOCKED=0).
|
|
@@ -42,6 +43,9 @@ const BOUND_FIELDS = Object.freeze(["max_sessions", "max_bytes"]);
|
|
|
42
43
|
/** Every valid setting key, precomputed. The handler validates a `set` against this exact set. */
|
|
43
44
|
const VALID_KEYS = new Set([
|
|
44
45
|
AWAY_DEFAULT_KEY,
|
|
46
|
+
// DOD-M15-RELAYONLY-1: relay-only routing. A BOOLEAN key, and the first one here — see
|
|
47
|
+
// `validateSettingValue`, where it needs its own branch or the away-text fallback swallows it.
|
|
48
|
+
RELAY_ONLY_KEY,
|
|
45
49
|
...SETTABLE_TIER_NAMES.flatMap((t) => [
|
|
46
50
|
...BOUND_FIELDS.map((f) => boundSettingKey(t, f)),
|
|
47
51
|
awayTierSettingKey(t),
|
|
@@ -79,6 +83,20 @@ export function validateSettingValue(key, value) {
|
|
|
79
83
|
}
|
|
80
84
|
return { ok: true };
|
|
81
85
|
}
|
|
86
|
+
// DOD-M15-RELAYONLY-1: a BOOLEAN key, and it must be caught BEFORE the away-text fallback below.
|
|
87
|
+
//
|
|
88
|
+
// ⚠️ THE FALLBACK IS THE HAZARD, not an edge case. Everything that is not a bound key falls
|
|
89
|
+
// through to away-text validation, which accepts any non-empty string under 2048 characters. So
|
|
90
|
+
// without this branch `transport.relay_only = "yes"` — or `"flase"` — would STORE successfully and
|
|
91
|
+
// then read as not-"true" forever: the operator sets the privacy control, is told it was saved,
|
|
92
|
+
// and has no protection. `isRelayOnly` deliberately treats anything but "true" as OFF, which is
|
|
93
|
+
// only safe because a bad value cannot get past here to be misread later.
|
|
94
|
+
if (key === RELAY_ONLY_KEY) {
|
|
95
|
+
if (value !== "true" && value !== "false") {
|
|
96
|
+
return { ok: false, reason: `${RELAY_ONLY_KEY} must be exactly "true" or "false"` };
|
|
97
|
+
}
|
|
98
|
+
return { ok: true };
|
|
99
|
+
}
|
|
82
100
|
// Away-text (review F2/F3): an EMPTY / whitespace-only away text is refused — to CLEAR, use null
|
|
83
101
|
// (cello_contact_set_away) or omit the key; storing "" would silently blank every away reply,
|
|
84
102
|
// including the minimal stranger disclosure. Bounded to a sane length (an answering-machine line,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-settings-keys.js","sourceRoot":"","sources":["../src/agent-settings-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-settings-keys.js","sourceRoot":"","sources":["../src/agent-settings-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,gGAAgG;AAChG,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,CAAU,CAAC,CAAC;AAGtG;;4DAE4D;AAC5D,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;QACpC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC;QAChC,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,aAAa,CAAC;QAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC;QAC5B,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,yCAAyC;IACjE,CAAC;AACH,CAAC;AAID,qDAAqD;AACrD,MAAM,UAAU,eAAe,CAAC,IAAsB,EAAE,KAAiB;IACvE,OAAO,UAAU,IAAI,IAAI,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACvD,OAAO,aAAa,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,gGAAgG;AAChG,MAAM,CAAC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAE/C,MAAM,YAAY,GAA0B,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AAEzF,kGAAkG;AAClG,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAS;IACtD,gBAAgB;IAChB,uFAAuF;IACvF,+FAA+F;IAC/F,cAAc;IACd,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACpC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,kBAAkB,CAAC,CAAC,CAAC;KACtB,CAAC;CACH,CAAC,CAAC;AAEH;;0CAE0C;AAC1C,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC;AACzB,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,KAAa;IAC7D,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,4FAA4F;QAC5F,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kFAAkF,EAAE,CAAC;QACnH,CAAC;QACD,+FAA+F;QAC/F,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,iGAAiG;IACjG,EAAE;IACF,4FAA4F;IAC5F,gGAAgG;IAChG,mGAAmG;IACnG,gGAAgG;IAChG,gGAAgG;IAChG,0EAA0E;IAC1E,IAAI,GAAG,KAAK,cAAc,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YAC1C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,oCAAoC,EAAE,CAAC;QACtF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,iGAAiG;IACjG,8FAA8F;IAC9F,kGAAkG;IAClG,mBAAmB;IACnB,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,yFAAyF,EAAE,CAAC;IAC1H,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QACxC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,oBAAoB,aAAa,EAAE,CAAC;IAChG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,yGAAyG;AACzG,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,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"}
|