@cello-protocol/daemon 0.0.58 → 0.0.60

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.
@@ -242,8 +242,81 @@ export function openEncryptedDatabase(dbPath, dbKey, logger) {
242
242
  catch (err) {
243
243
  logger?.warn("persist.db.wal.unavailable", { error: err instanceof Error ? err.message : String(err) });
244
244
  }
245
+ // M10-D19: FOREIGN KEY enforcement. SQLite defaults this OFF — and with it off, a declared
246
+ // `FOREIGN KEY` is DECORATIVE: SQLite happily accepts an orphan row, so a schema that looks like it
247
+ // enforces a relationship enforces nothing. INV-AGENT-SCOPED depends on this being real (a received
248
+ // trust signal cannot exist except hung off one agent's contact row — `contact_trust_signals` FKs to
249
+ // `contacts(agent_id, pubkey)`), and ON DELETE CASCADE only fires when it is on.
250
+ //
251
+ // It is per-CONNECTION, not per-database, so it belongs here at open — not in a migration, which
252
+ // would set it for exactly one connection and leave every later one unguarded.
253
+ //
254
+ // Safe to enable on existing databases: the daemon declared ZERO foreign keys before this, so there
255
+ // is no pre-existing constraint that enforcement could retroactively violate.
256
+ inner.pragma("foreign_keys = ON");
257
+ const fkOn = inner.pragma("foreign_keys", { simple: true });
258
+ if (fkOn !== 1 && fkOn !== true) {
259
+ // Refuse to run with a silently-unenforced schema. If FKs cannot be turned on, every FK in the
260
+ // DB is a lie and INV-AGENT-SCOPED is unenforced — that is not a degraded mode we announce and
261
+ // continue through, it is a broken security property. ABSENT IS NOT FINE.
262
+ try {
263
+ inner.close();
264
+ }
265
+ catch { /* ignore */ }
266
+ throw new DbEncryptionError("db_open_failed", `PRAGMA foreign_keys did not take effect (reported ${String(fkOn)})`, "This SQLite build will not enforce foreign keys, so per-agent scoping of received trust signals cannot be guaranteed. The daemon will not run with an unenforced schema.");
267
+ }
245
268
  return new SqlcipherDatabase(inner);
246
269
  }
270
+ /**
271
+ * Run a table REBUILD (the create-copy-drop-rename recipe) with foreign keys safely disabled.
272
+ *
273
+ * THE TRAP THIS EXISTS TO CLOSE. With `PRAGMA foreign_keys = ON` (M10-D19), SQLite treats
274
+ * `DROP TABLE parent` as an implicit `DELETE FROM parent` — which **fires `ON DELETE CASCADE` and
275
+ * silently empties every child table**. No error. No log. The rebuild's own row-count guards count
276
+ * the table being rebuilt, not its children, so they pass. Measured:
277
+ *
278
+ * children before rebuild: 1
279
+ * PRAGMA foreign_keys = OFF (inside BEGIN) -> still reports 1 <-- A SILENT NO-OP
280
+ * DROP TABLE contacts -> children: 0 <-- cascade fired
281
+ *
282
+ * And the obvious mitigation does not work: **`PRAGMA foreign_keys` is a no-op inside a
283
+ * transaction.** SQLite ignores it and says nothing. Since every rebuild in this codebase runs
284
+ * inside one `BEGIN…COMMIT` (agent-id-migration.ts rebuilds seven tables, `contacts` among them),
285
+ * a rebuild that "disabled" FKs in the usual place would still cascade.
286
+ *
287
+ * So the pragma must be toggled OUTSIDE the transaction, which is what this helper enforces — and it
288
+ * VERIFIES the toggle took effect rather than assuming it, because the failure is silent by nature.
289
+ * On the way out it re-enables FKs and runs `PRAGMA foreign_key_check`: a rebuild that left a
290
+ * dangling reference (e.g. `ALTER TABLE parent RENAME` rewrites children's FK clauses to point at
291
+ * the renamed table) is a loud failure here rather than a mystery on some later insert.
292
+ */
293
+ export function withForeignKeysOff(db, logger, fn) {
294
+ db.exec("PRAGMA foreign_keys = OFF");
295
+ // Verify. If we are inside a transaction the pragma was silently ignored, and proceeding would
296
+ // cascade-delete children on the first DROP. Refuse — do not rebuild with FKs live.
297
+ const off = db.prepare("PRAGMA foreign_keys").get();
298
+ if (off?.foreign_keys !== 0) {
299
+ throw new Error("table rebuild refused: PRAGMA foreign_keys = OFF did not take effect (still " +
300
+ `${String(off?.foreign_keys)}). PRAGMA foreign_keys is a NO-OP inside a transaction — this ` +
301
+ "helper must be called OUTSIDE any BEGIN. Rebuilding with FKs live makes DROP TABLE <parent> " +
302
+ "cascade-delete every child row, silently.");
303
+ }
304
+ try {
305
+ return fn();
306
+ }
307
+ finally {
308
+ db.exec("PRAGMA foreign_keys = ON");
309
+ const violations = db.prepare("PRAGMA foreign_key_check").all();
310
+ if (violations.length > 0) {
311
+ // The rebuild left dangling references. Loud — an FK that points at a table that no longer
312
+ // exists (or a renamed one) fails on some unrelated insert much later, naming the wrong
313
+ // subsystem entirely.
314
+ logger?.error("persist.db.rebuild.fk_violation", { violations: violations.length });
315
+ throw new Error(`table rebuild left ${violations.length} foreign-key violation(s) — PRAGMA foreign_key_check ` +
316
+ "is non-empty. The rebuilt table's children now reference a parent that is missing or renamed.");
317
+ }
318
+ }
319
+ }
247
320
  /**
248
321
  * Convenience used by tests and tooling: resolve the key file beside `dbPath` and open. Throws if
249
322
  * the key file is absent (it does NOT generate one) — callers inspecting an existing DB always have
@@ -1 +1 @@
1
- {"version":3,"file":"sqlcipher-db.js","sourceRoot":"","sources":["../src/sqlcipher-db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqlcipher-db.js","sourceRoot":"","sources":["../src/sqlcipher-db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * M10 / DOD-STORE-CLIENT-1 — the daemon's two trust-signal tables.
3
+ *
4
+ * TWO tables, never one with a role flag (M10-D4). They answer different questions and obey
5
+ * different scoping rules, and a single table with a `role` column is how one set of rules ends up
6
+ * applied to the other's rows.
7
+ *
8
+ * `wallet_trust_signals` — signals ABOUT this daemon's agents, held so they can be PRESENTED.
9
+ * `contact_trust_signals` — signals OTHER agents PRESENTED TO one of my agents.
10
+ *
11
+ * THE WALLET CARRIES NO AGENT ASSOCIATION (M10-D14). PK = `signal_hash`; one row per signal per
12
+ * daemon, and nothing more. It is the envelope's own HASHED `subject_kind`/`subject` that decides
13
+ * who may present it, evaluated at presentation time — not a local attribution decided at delivery
14
+ * time. That is the whole point: an account-subject signal (phone, email) serves every agent under
15
+ * the account, so **adding an agent to an existing daemon is ZERO signal work** — no assignment
16
+ * sweep, no re-attribution at renewal, no copies to keep in step at expiry. A per-agent column
17
+ * would quietly reintroduce every one of those chores.
18
+ *
19
+ * THE RECEIVED STORE IS PER-AGENT, AND THE DATABASE ENFORCES IT (INV-AGENT-SCOPED). Consent is
20
+ * genuinely per-agent here: a signal Bob showed to my agent `alice` must be invisible to my agent
21
+ * `bob` on the same daemon. `agent_id` is NOT NULL and the row hangs off a contact row by composite
22
+ * FK, so an unscoped row cannot be written at all — as opposed to being merely discouraged by a
23
+ * query convention that every future caller must remember. (This requires `PRAGMA foreign_keys = ON`,
24
+ * set at open in sqlcipher-db.ts — M10-D19. Without it the FK is decorative.) This is where the M8
25
+ * scaffold's `agent_id = null` defect dies.
26
+ *
27
+ * EVIDENCE, NOT AN INPUT (M10-D4 / INV-STATELESS-RECIPIENT). Received rows are re-checkable evidence
28
+ * of what was presented and when we verified it. They are NEVER an input to policy evaluation, which
29
+ * consumes only the currently-presented set, and they are never trusted for freshness (that is
30
+ * re-checked on use). This module therefore exposes no "is this contact trusted?" read — the absence
31
+ * is deliberate and structural. Do not add one.
32
+ *
33
+ * OPAQUE THROUGHOUT (INV-ZERO-BUMP / INV-TYPE-CARRY). `type` is a TEXT column with no CHECK, no
34
+ * enum, and no index predicated on a type value; `payload` is a BLOB that is never parsed and whose
35
+ * fields are never hoisted into columns. A type string this code has never seen stores, reads, and
36
+ * presents exactly like a known one. Adding a signal type must require no change to this file.
37
+ */
38
+ import type { DaemonDatabase } from "./sqlcipher-db.js";
39
+ import type { Logger } from "./types.js";
40
+ /** Status lives OUTSIDE the hash — it is mutable after minting, which is exactly why it is not in
41
+ * the preimage. If it were hashed, revoking a signal would change its hash and the directory could
42
+ * never find it again. */
43
+ export type SignalStatus = "active" | "revoked" | "superseded";
44
+ /** The envelope, as this store takes and returns it. Mirrors `TrustSignalEnvelope` in
45
+ * protocol-types, plus the two non-hashed local columns. */
46
+ export interface WalletSignalInput {
47
+ signalHash: string;
48
+ subjectKind: "account" | "agent";
49
+ subject: string;
50
+ issuerKind: "portal" | "agent";
51
+ issuerPubkey: string;
52
+ /** OPAQUE. Never gated on, never enumerated. */
53
+ type: string;
54
+ schemaVersion: number;
55
+ /** OPAQUE bytes. Never parsed. */
56
+ payload: Uint8Array;
57
+ issuedAt: number;
58
+ expiresAt: number | null;
59
+ supersedesHash: string | null;
60
+ status: SignalStatus | string;
61
+ }
62
+ export interface WalletSignalRow extends WalletSignalInput {
63
+ receivedAt: number;
64
+ }
65
+ /**
66
+ * A signal a contact PRESENTED to one of my agents, as it is stored after verification.
67
+ *
68
+ * ⚠️ NOTE WHAT IS ABSENT: there is no `status` field, and this type deliberately does NOT extend
69
+ * `WalletSignalInput`.
70
+ *
71
+ * `status` is outside the hash preimage — that is what makes it mutable, and it also means it is
72
+ * **NOT AUTHENTICATED BY THE SIGNAL HASH**. A presenter can claim any status they like. If this type
73
+ * simply extended the wallet's, the peer's claimed `status` would ride in the same struct as the
74
+ * envelope fields and the natural call site would pass it straight through to the database.
75
+ *
76
+ * The attack that permits: Bob presents signal H; we verify it, find it REVOKED at the directory,
77
+ * and store that as evidence. Next session Bob re-presents the same hash claiming `status: active`.
78
+ * An upsert on the peer's value overwrites the one durable record that says we caught it — the party
79
+ * a revocation indicts gets to erase the indictment. Evidence an adversary can rewrite is not
80
+ * evidence.
81
+ *
82
+ * So the stored status is OUR verification verdict (`verdict`), produced by the verification seam
83
+ * (DOD-VERIFY-1) from what the DIRECTORY said — never a field the peer controls. Removing `status`
84
+ * from the input type makes that structural: there is no pass-through to forget to block.
85
+ */
86
+ export interface ReceivedSignalInput extends Omit<WalletSignalInput, "status"> {
87
+ agentId: string;
88
+ contactPubkey: string;
89
+ verifiedAt: number;
90
+ /** OUR verdict from re-checking the directory — never the presenter's claim. */
91
+ verdict: SignalStatus;
92
+ }
93
+ export interface ReceivedSignalRow extends ReceivedSignalInput {
94
+ receivedAt: number;
95
+ }
96
+ /**
97
+ * Idempotent schema. Must run AFTER `contacts` exists — the composite FK's parent. SQLite resolves
98
+ * an FK's parent at DML time, not at DDL time, so creating this first would not fail here; it would
99
+ * fail later, on the first insert, which is a far worse place to find out.
100
+ */
101
+ export declare function ensureTrustSignalSchema(db: DaemonDatabase, _logger: Logger): void;
102
+ export declare class TrustSignalStore {
103
+ #private;
104
+ /**
105
+ * Does NOT create the schema. `SessionNodeManager.initialize()` owns it, and must — the received
106
+ * table's FK parent is `contacts`, which that init creates. A store that created its own schema
107
+ * would happily build `contact_trust_signals` on any handle lacking `contacts`, producing a
108
+ * dangling FK that then fails on the first INSERT with `no such table: main.contacts` — an error
109
+ * that names the wrong subsystem, at the wrong time, far from the cause.
110
+ */
111
+ constructor(db: DaemonDatabase, logger: Logger);
112
+ /**
113
+ * Store a signal this daemon holds about one of its agents.
114
+ *
115
+ * `INSERT OR IGNORE`, and that is a correctness property, not a convenience: the row is keyed by
116
+ * its own content hash, so a second delivery of the same hash is by definition the same signal.
117
+ * If the bytes really differed, the hash would differ. A differing payload under an identical hash
118
+ * is therefore a liar, and it must not overwrite the truth. This is also what makes duplicate
119
+ * delivery a no-op and wallet rows safely mergeable across a user's daemons (spec §14.11).
120
+ */
121
+ putWalletSignal(s: WalletSignalInput): void;
122
+ getWalletSignal(signalHash: string): WalletSignalRow | null;
123
+ /**
124
+ * Status is the ONE mutable field — it lives outside the hash precisely so this is possible.
125
+ *
126
+ * REVOCATION IS TERMINAL. A revoked signal can never go back to `active`. Without that guard this
127
+ * is a blind UPDATE, and a stale or replayed directory read reporting `active` for a hash we
128
+ * already revoked would resurrect it — and `listPresentable` would start offering it again. The
129
+ * directory is the authority on revocation, but a LATE answer from it is not a NEW answer; the
130
+ * monotonic rule makes out-of-order responses harmless instead of dangerous.
131
+ *
132
+ * `superseded` may still be overtaken by `revoked` (a superseded signal can also be revoked), but
133
+ * never the reverse, and never back to `active`.
134
+ */
135
+ setWalletStatus(signalHash: string, status: SignalStatus): void;
136
+ /**
137
+ * The signals `agentId` may present, resolved from the ENVELOPE, not from a stored attribution:
138
+ * an `account`-subject row is presentable by every agent under `accountId`; an `agent`-subject row
139
+ * only by its own subject (M10-D5/M10-D14).
140
+ *
141
+ * Excludes expired and non-active rows. Selective disclosure (all / some / none) is the CALLER's
142
+ * choice on top of this — DOD-PRESENT-1; this returns what is *eligible*, never what to send.
143
+ */
144
+ listPresentable(opts: {
145
+ agentId: string;
146
+ accountId: string;
147
+ nowSec?: number;
148
+ }): WalletSignalRow[];
149
+ /**
150
+ * Store a signal a contact PRESENTED to one of my agents, after it was verified.
151
+ *
152
+ * The FK refuses a row for a contact that does not exist — an unscoped received signal is not
153
+ * merely discouraged, it is unwritable. `verified_at` is RE-STAMPED on re-presentation: it records
154
+ * when WE last re-verified, and a stale value that looked fresh would be worse than no value at
155
+ * all (freshness is re-checked on use — M10-D4).
156
+ */
157
+ putReceivedSignal(s: ReceivedSignalInput): void;
158
+ /** Evidence only. Never an input to policy — see the header. */
159
+ listReceived(opts: {
160
+ agentId: string;
161
+ contactPubkey: string;
162
+ }): ReceivedSignalRow[];
163
+ }
164
+ //# sourceMappingURL=trust-signal-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-signal-store.d.ts","sourceRoot":"","sources":["../src/trust-signal-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;2BAE2B;AAC3B,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAE/D;6DAC6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,SAAS,GAAG,OAAO,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,UAAU,EAAE,MAAM,CAAC;CACpB;AAmDD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMjF;AAiED,qBAAa,gBAAgB;;IAI3B;;;;;;OAMG;gBACS,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAK9C;;;;;;;;OAQG;IACH,eAAe,CAAC,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAmB3C,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAO3D;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IA0B/D;;;;;;;OAOG;IACH,eAAe,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe,EAAE;IAqBjG;;;;;;;OAOG;IACH,iBAAiB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI;IA8B/C,gEAAgE;IAChE,YAAY,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,iBAAiB,EAAE;CAiBpF"}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * M10 / DOD-STORE-CLIENT-1 — the daemon's two trust-signal tables.
3
+ *
4
+ * TWO tables, never one with a role flag (M10-D4). They answer different questions and obey
5
+ * different scoping rules, and a single table with a `role` column is how one set of rules ends up
6
+ * applied to the other's rows.
7
+ *
8
+ * `wallet_trust_signals` — signals ABOUT this daemon's agents, held so they can be PRESENTED.
9
+ * `contact_trust_signals` — signals OTHER agents PRESENTED TO one of my agents.
10
+ *
11
+ * THE WALLET CARRIES NO AGENT ASSOCIATION (M10-D14). PK = `signal_hash`; one row per signal per
12
+ * daemon, and nothing more. It is the envelope's own HASHED `subject_kind`/`subject` that decides
13
+ * who may present it, evaluated at presentation time — not a local attribution decided at delivery
14
+ * time. That is the whole point: an account-subject signal (phone, email) serves every agent under
15
+ * the account, so **adding an agent to an existing daemon is ZERO signal work** — no assignment
16
+ * sweep, no re-attribution at renewal, no copies to keep in step at expiry. A per-agent column
17
+ * would quietly reintroduce every one of those chores.
18
+ *
19
+ * THE RECEIVED STORE IS PER-AGENT, AND THE DATABASE ENFORCES IT (INV-AGENT-SCOPED). Consent is
20
+ * genuinely per-agent here: a signal Bob showed to my agent `alice` must be invisible to my agent
21
+ * `bob` on the same daemon. `agent_id` is NOT NULL and the row hangs off a contact row by composite
22
+ * FK, so an unscoped row cannot be written at all — as opposed to being merely discouraged by a
23
+ * query convention that every future caller must remember. (This requires `PRAGMA foreign_keys = ON`,
24
+ * set at open in sqlcipher-db.ts — M10-D19. Without it the FK is decorative.) This is where the M8
25
+ * scaffold's `agent_id = null` defect dies.
26
+ *
27
+ * EVIDENCE, NOT AN INPUT (M10-D4 / INV-STATELESS-RECIPIENT). Received rows are re-checkable evidence
28
+ * of what was presented and when we verified it. They are NEVER an input to policy evaluation, which
29
+ * consumes only the currently-presented set, and they are never trusted for freshness (that is
30
+ * re-checked on use). This module therefore exposes no "is this contact trusted?" read — the absence
31
+ * is deliberate and structural. Do not add one.
32
+ *
33
+ * OPAQUE THROUGHOUT (INV-ZERO-BUMP / INV-TYPE-CARRY). `type` is a TEXT column with no CHECK, no
34
+ * enum, and no index predicated on a type value; `payload` is a BLOB that is never parsed and whose
35
+ * fields are never hoisted into columns. A type string this code has never seen stores, reads, and
36
+ * presents exactly like a known one. Adding a signal type must require no change to this file.
37
+ */
38
+ /**
39
+ * ⚠️ TWO TIME UNITS LIVE IN THESE TABLES, AND MIXING THEM SILENTLY PRESENTS EXPIRED SIGNALS.
40
+ *
41
+ * issued_at / expires_at — epoch **SECONDS**. These are ENVELOPE fields: they are HASHED, so the
42
+ * protocol fixed the unit and we do not get to choose it.
43
+ * received_at / verified_at — epoch **MILLISECONDS** (`Date.now()`). Local bookkeeping only, never
44
+ * hashed, and matching the house convention of every other daemon table
45
+ * (`contacts.added_at`, `sessions.updated_at`).
46
+ *
47
+ * A factor-of-1000 error between them does not throw. Compared against a 1970 timestamp, every
48
+ * expiry is still in the future — so the failure mode is an expired signal being cheerfully
49
+ * presented. Anything that compares against `expires_at` must be in SECONDS; see `listPresentable`.
50
+ */
51
+ const ENVELOPE_COLUMNS = `
52
+ signal_hash TEXT NOT NULL,
53
+ subject_kind TEXT NOT NULL,
54
+ subject TEXT NOT NULL,
55
+ issuer_kind TEXT NOT NULL,
56
+ issuer_pubkey TEXT NOT NULL,
57
+ type TEXT NOT NULL,
58
+ schema_version INTEGER NOT NULL,
59
+ payload BLOB NOT NULL,
60
+ issued_at INTEGER NOT NULL,
61
+ expires_at INTEGER,
62
+ supersedes_hash TEXT,
63
+ status TEXT NOT NULL`;
64
+ /** Content-addressed: the row IS its hash (spec §14.11 — this is what makes wallet rows portable
65
+ * between a user's daemons, and duplicate delivery a no-op). */
66
+ const CREATE_WALLET_SQL = `
67
+ CREATE TABLE IF NOT EXISTS wallet_trust_signals (
68
+ ${ENVELOPE_COLUMNS},
69
+ received_at INTEGER NOT NULL,
70
+ PRIMARY KEY (signal_hash)
71
+ );
72
+ `;
73
+ const CREATE_RECEIVED_SQL = `
74
+ CREATE TABLE IF NOT EXISTS contact_trust_signals (
75
+ agent_id TEXT NOT NULL,
76
+ contact_pubkey TEXT NOT NULL,
77
+ ${ENVELOPE_COLUMNS},
78
+ verified_at INTEGER NOT NULL,
79
+ received_at INTEGER NOT NULL,
80
+ PRIMARY KEY (agent_id, contact_pubkey, signal_hash),
81
+ FOREIGN KEY (agent_id, contact_pubkey) REFERENCES contacts(agent_id, pubkey) ON DELETE CASCADE
82
+ );
83
+ `;
84
+ /**
85
+ * Idempotent schema. Must run AFTER `contacts` exists — the composite FK's parent. SQLite resolves
86
+ * an FK's parent at DML time, not at DDL time, so creating this first would not fail here; it would
87
+ * fail later, on the first insert, which is a far worse place to find out.
88
+ */
89
+ export function ensureTrustSignalSchema(db, _logger) {
90
+ db.exec(CREATE_WALLET_SQL);
91
+ db.exec(CREATE_RECEIVED_SQL);
92
+ // Presentation reads by subject; nothing reads by `type`, and nothing may (INV-ZERO-BUMP — an
93
+ // index predicated on a type VALUE is a per-type construct in the schema).
94
+ db.exec("CREATE INDEX IF NOT EXISTS idx_wallet_signals_subject ON wallet_trust_signals (subject_kind, subject)");
95
+ }
96
+ const toBuf = (b) => Buffer.from(b);
97
+ /**
98
+ * The payload column is `BLOB NOT NULL`, so an unrecognized shape coming back from the driver means
99
+ * something is wrong — NOT that the payload is empty.
100
+ *
101
+ * Returning `new Uint8Array(0)` here (the previous behavior) is the silent-fallback pattern: a signal
102
+ * whose payload failed to materialise would flow on to presentation and verification looking like a
103
+ * perfectly valid signal that simply has no content. Its hash would then not match, and the operator
104
+ * would be sent to hunt a canonicalization bug that does not exist. ABSENT IS NOT FINE — refuse, and
105
+ * name the actual cause.
106
+ */
107
+ const toBytes = (v) => {
108
+ if (v instanceof Uint8Array)
109
+ return new Uint8Array(v);
110
+ if (Buffer.isBuffer(v))
111
+ return new Uint8Array(v);
112
+ throw new Error(`signal_payload_not_bytes: the payload column returned ${v === null ? "null" : typeof v}` +
113
+ `${v && typeof v === "object" ? ` (${v.constructor?.name})` : ""}, but the column is BLOB NOT NULL. ` +
114
+ "This is a storage-layer fault, not an empty payload — an empty payload would be a zero-length BLOB.");
115
+ };
116
+ function toWalletRow(r) {
117
+ return {
118
+ signalHash: r.signal_hash,
119
+ subjectKind: r.subject_kind,
120
+ subject: r.subject,
121
+ issuerKind: r.issuer_kind,
122
+ issuerPubkey: r.issuer_pubkey,
123
+ type: r.type,
124
+ schemaVersion: r.schema_version,
125
+ payload: toBytes(r.payload),
126
+ issuedAt: r.issued_at,
127
+ expiresAt: r.expires_at,
128
+ supersedesHash: r.supersedes_hash,
129
+ status: r.status,
130
+ receivedAt: r.received_at,
131
+ };
132
+ }
133
+ export class TrustSignalStore {
134
+ #db;
135
+ #logger;
136
+ /**
137
+ * Does NOT create the schema. `SessionNodeManager.initialize()` owns it, and must — the received
138
+ * table's FK parent is `contacts`, which that init creates. A store that created its own schema
139
+ * would happily build `contact_trust_signals` on any handle lacking `contacts`, producing a
140
+ * dangling FK that then fails on the first INSERT with `no such table: main.contacts` — an error
141
+ * that names the wrong subsystem, at the wrong time, far from the cause.
142
+ */
143
+ constructor(db, logger) {
144
+ this.#db = db;
145
+ this.#logger = logger;
146
+ }
147
+ /**
148
+ * Store a signal this daemon holds about one of its agents.
149
+ *
150
+ * `INSERT OR IGNORE`, and that is a correctness property, not a convenience: the row is keyed by
151
+ * its own content hash, so a second delivery of the same hash is by definition the same signal.
152
+ * If the bytes really differed, the hash would differ. A differing payload under an identical hash
153
+ * is therefore a liar, and it must not overwrite the truth. This is also what makes duplicate
154
+ * delivery a no-op and wallet rows safely mergeable across a user's daemons (spec §14.11).
155
+ */
156
+ putWalletSignal(s) {
157
+ const res = this.#db
158
+ .prepare(`INSERT OR IGNORE INTO wallet_trust_signals
159
+ (signal_hash, subject_kind, subject, issuer_kind, issuer_pubkey, type, schema_version,
160
+ payload, issued_at, expires_at, supersedes_hash, status, received_at)
161
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
162
+ .run(s.signalHash, s.subjectKind, s.subject, s.issuerKind, s.issuerPubkey, s.type, s.schemaVersion, toBuf(s.payload), s.issuedAt, s.expiresAt, s.supersedesHash, s.status, Date.now());
163
+ if (res.changes > 0) {
164
+ this.#logger.info("signal.wallet.stored", {
165
+ signalHash: s.signalHash, type: s.type, subjectKind: s.subjectKind, issuerKind: s.issuerKind,
166
+ });
167
+ }
168
+ }
169
+ getWalletSignal(signalHash) {
170
+ const row = this.#db
171
+ .prepare("SELECT * FROM wallet_trust_signals WHERE signal_hash = ?")
172
+ .get(signalHash);
173
+ return row ? toWalletRow(row) : null;
174
+ }
175
+ /**
176
+ * Status is the ONE mutable field — it lives outside the hash precisely so this is possible.
177
+ *
178
+ * REVOCATION IS TERMINAL. A revoked signal can never go back to `active`. Without that guard this
179
+ * is a blind UPDATE, and a stale or replayed directory read reporting `active` for a hash we
180
+ * already revoked would resurrect it — and `listPresentable` would start offering it again. The
181
+ * directory is the authority on revocation, but a LATE answer from it is not a NEW answer; the
182
+ * monotonic rule makes out-of-order responses harmless instead of dangerous.
183
+ *
184
+ * `superseded` may still be overtaken by `revoked` (a superseded signal can also be revoked), but
185
+ * never the reverse, and never back to `active`.
186
+ */
187
+ setWalletStatus(signalHash, status) {
188
+ const res = this.#db
189
+ .prepare(`UPDATE wallet_trust_signals SET status = ?
190
+ WHERE signal_hash = ?
191
+ AND status != 'revoked'
192
+ AND NOT (status = 'superseded' AND ? = 'active')`)
193
+ .run(status, signalHash, status);
194
+ if (res.changes > 0) {
195
+ this.#logger.info("signal.wallet.status.changed", { signalHash, status });
196
+ return;
197
+ }
198
+ // No change is not necessarily an error (the row may already hold this status), but a REFUSED
199
+ // downgrade is worth surfacing — it means something tried to resurrect a dead signal.
200
+ const current = this.#db
201
+ .prepare("SELECT status FROM wallet_trust_signals WHERE signal_hash = ?")
202
+ .get(signalHash);
203
+ if (current && current.status !== status) {
204
+ this.#logger.warn("signal.wallet.status.change_refused", {
205
+ signalHash, current: current.status, attempted: status,
206
+ reason: "revocation is terminal — a revoked or superseded signal is never returned to active",
207
+ });
208
+ }
209
+ }
210
+ /**
211
+ * The signals `agentId` may present, resolved from the ENVELOPE, not from a stored attribution:
212
+ * an `account`-subject row is presentable by every agent under `accountId`; an `agent`-subject row
213
+ * only by its own subject (M10-D5/M10-D14).
214
+ *
215
+ * Excludes expired and non-active rows. Selective disclosure (all / some / none) is the CALLER's
216
+ * choice on top of this — DOD-PRESENT-1; this returns what is *eligible*, never what to send.
217
+ */
218
+ listPresentable(opts) {
219
+ // `nowSec` is epoch SECONDS, and it is named for its unit deliberately. The envelope's
220
+ // `issued_at`/`expires_at` are seconds (they are HASHED, and the protocol fixed the unit);
221
+ // `Date.now()` is milliseconds. An unnamed `now` here invites a caller to pass one where the
222
+ // other is meant, and a factor-of-1000 error in an expiry check does not throw — it silently
223
+ // presents an EXPIRED signal (compared against a 1970 timestamp, every expiry is still in the
224
+ // future). The first version of this method took `now` and its own test passed seconds into a
225
+ // parameter that was divided by 1000; it only went green because the fixture's expiry was 1.
226
+ const nowSec = opts.nowSec ?? Math.floor(Date.now() / 1000);
227
+ const rows = this.#db
228
+ .prepare(`SELECT * FROM wallet_trust_signals
229
+ WHERE status = 'active'
230
+ AND (expires_at IS NULL OR expires_at > ?)
231
+ AND ( (subject_kind = 'account' AND subject = ?)
232
+ OR (subject_kind = 'agent' AND subject = ?) )`)
233
+ .all(nowSec, opts.accountId, opts.agentId);
234
+ return rows.map(toWalletRow);
235
+ }
236
+ /**
237
+ * Store a signal a contact PRESENTED to one of my agents, after it was verified.
238
+ *
239
+ * The FK refuses a row for a contact that does not exist — an unscoped received signal is not
240
+ * merely discouraged, it is unwritable. `verified_at` is RE-STAMPED on re-presentation: it records
241
+ * when WE last re-verified, and a stale value that looked fresh would be worse than no value at
242
+ * all (freshness is re-checked on use — M10-D4).
243
+ */
244
+ putReceivedSignal(s) {
245
+ this.#db
246
+ .prepare(`INSERT INTO contact_trust_signals
247
+ (agent_id, contact_pubkey, signal_hash, subject_kind, subject, issuer_kind, issuer_pubkey,
248
+ type, schema_version, payload, issued_at, expires_at, supersedes_hash, status,
249
+ verified_at, received_at)
250
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
251
+ ON CONFLICT (agent_id, contact_pubkey, signal_hash)
252
+ DO UPDATE SET
253
+ verified_at = excluded.verified_at,
254
+ -- OUR verdict may only ever get WORSE, never better. A re-presentation cannot launder a
255
+ -- signal we already caught as revoked/superseded back to active: the party a revocation
256
+ -- indicts does not get to erase the indictment. (The envelope fields are not updated at
257
+ -- all — they are hashed, so an identical signal_hash means identical envelope bytes; a
258
+ -- differing value under the same hash is a liar and must not win.)
259
+ status = CASE WHEN contact_trust_signals.status = 'active' THEN excluded.status
260
+ ELSE contact_trust_signals.status END`)
261
+ .run(s.agentId, s.contactPubkey, s.signalHash, s.subjectKind, s.subject, s.issuerKind, s.issuerPubkey, s.type, s.schemaVersion, toBuf(s.payload), s.issuedAt, s.expiresAt, s.supersedesHash, s.verdict, s.verifiedAt, Date.now());
262
+ this.#logger.info("signal.received.stored", {
263
+ agentId: s.agentId, signalHash: s.signalHash, type: s.type, issuerKind: s.issuerKind,
264
+ verdict: s.verdict,
265
+ });
266
+ }
267
+ /** Evidence only. Never an input to policy — see the header. */
268
+ listReceived(opts) {
269
+ const rows = this.#db
270
+ .prepare(`SELECT * FROM contact_trust_signals WHERE agent_id = ? AND contact_pubkey = ?`)
271
+ .all(opts.agentId, opts.contactPubkey);
272
+ return rows.map((r) => {
273
+ const { status, ...envelope } = toWalletRow(r);
274
+ return {
275
+ ...envelope,
276
+ agentId: opts.agentId,
277
+ contactPubkey: opts.contactPubkey,
278
+ verifiedAt: r.verified_at,
279
+ verdict: status,
280
+ };
281
+ });
282
+ }
283
+ }
284
+ //# sourceMappingURL=trust-signal-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-signal-store.js","sourceRoot":"","sources":["../src/trust-signal-store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cello-protocol/daemon",
3
- "version": "0.0.58",
3
+ "version": "0.0.60",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -30,10 +30,10 @@
30
30
  "@signalapp/sqlcipher": "^3.3.5",
31
31
  "cbor-x": "^1.6.0",
32
32
  "it-length-prefixed": "^10.0.1",
33
- "@cello-protocol/crypto": "0.0.21",
34
- "@cello-protocol/gateway": "0.0.3",
35
- "@cello-protocol/protocol-types": "0.0.22",
36
- "@cello-protocol/transport": "0.0.22"
33
+ "@cello-protocol/crypto": "0.0.22",
34
+ "@cello-protocol/gateway": "0.0.4",
35
+ "@cello-protocol/protocol-types": "0.0.23",
36
+ "@cello-protocol/transport": "0.0.23"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^25.6.2",