@cello-protocol/protocol-types 0.0.21 → 0.0.23

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.
Files changed (57) hide show
  1. package/dist/cbor.d.ts +25 -0
  2. package/dist/cbor.d.ts.map +1 -0
  3. package/dist/cbor.js +51 -0
  4. package/dist/cbor.js.map +1 -0
  5. package/dist/connection-package.d.ts +4 -0
  6. package/dist/connection-package.d.ts.map +1 -1
  7. package/dist/connection-package.js +5 -6
  8. package/dist/connection-package.js.map +1 -1
  9. package/dist/index.d.ts +5 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/limits.d.ts +10 -0
  14. package/dist/limits.d.ts.map +1 -0
  15. package/dist/limits.js +10 -0
  16. package/dist/limits.js.map +1 -0
  17. package/dist/manifest.d.ts +2 -4
  18. package/dist/manifest.d.ts.map +1 -1
  19. package/dist/manifest.js +2 -4
  20. package/dist/manifest.js.map +1 -1
  21. package/dist/primary-transfer.d.ts +6 -8
  22. package/dist/primary-transfer.d.ts.map +1 -1
  23. package/dist/primary-transfer.js +18 -13
  24. package/dist/primary-transfer.js.map +1 -1
  25. package/dist/revocation.d.ts.map +1 -1
  26. package/dist/revocation.js +2 -5
  27. package/dist/revocation.js.map +1 -1
  28. package/dist/session-liveness.js +4 -4
  29. package/dist/session-liveness.js.map +1 -1
  30. package/dist/session.d.ts +26 -61
  31. package/dist/session.d.ts.map +1 -1
  32. package/dist/session.js +22 -54
  33. package/dist/session.js.map +1 -1
  34. package/dist/structure1.d.ts +16 -0
  35. package/dist/structure1.d.ts.map +1 -0
  36. package/dist/structure1.js +33 -0
  37. package/dist/structure1.js.map +1 -0
  38. package/dist/structure2.d.ts.map +1 -1
  39. package/dist/structure2.js +7 -8
  40. package/dist/structure2.js.map +1 -1
  41. package/dist/trust-signal.d.ts +94 -0
  42. package/dist/trust-signal.d.ts.map +1 -0
  43. package/dist/trust-signal.js +252 -0
  44. package/dist/trust-signal.js.map +1 -0
  45. package/package.json +6 -3
  46. package/test/vectors/connection-package-canonical.json +10 -0
  47. package/test/vectors/envelope-v1-canonical.json +16 -0
  48. package/test/vectors/genesis-prev-root.json +53 -0
  49. package/test/vectors/scan-result-unscanned.json +5 -0
  50. package/test/vectors/structure1-canonical.json +13 -0
  51. package/test/vectors/structure2-canonical.json +12 -0
  52. package/test/vectors/tbs-v0-canonical.json +8 -0
  53. package/test/vectors/trust-signal-envelope-canonical.json +136 -0
  54. package/dist/envelope.d.ts +0 -170
  55. package/dist/envelope.d.ts.map +0 -1
  56. package/dist/envelope.js +0 -776
  57. package/dist/envelope.js.map +0 -1
@@ -0,0 +1,252 @@
1
+ /**
2
+ * The canonical trust-signal envelope (M10 / DOD-CBOR-1): serialize, hash, verify.
3
+ *
4
+ * Four parties independently re-derive this hash — the portal at mint, the directory at submission
5
+ * (INV-CHOKEPOINT's re-hash before store), the directory again at presentation (dumb check 1), and
6
+ * the holder/recipient daemons on receipt and at verification. They must agree byte-for-byte,
7
+ * forever, across three repos and two languages' worth of tooling. A single divergent byte turns a
8
+ * valid signal into a false `hash_mismatch` — unpresentable, and censorship-shaped, because it can
9
+ * differ per node or per client version.
10
+ *
11
+ * THE PREIMAGE IS AN ARRAY, NOT A MAP, AND THAT IS LOAD-BEARING (M10-D15).
12
+ * The shared encoder (`cbor.ts`) is not RFC 8949 §4.2 deterministic for MAPS: map keys follow
13
+ * INSERTION ORDER, and map headers are not minimal-length (a 2-entry map emits `b9 0002`, not `a2`).
14
+ * Arrays, text strings, byte strings, integers and null are all minimal and order-fixed. Encoding
15
+ * the preimage as a fixed-order array therefore makes determinism STRUCTURAL — a property of the
16
+ * shape, not of the encoder's configuration — and costs no encoder change and no migration of the
17
+ * blobs already on disk. Every signed TBS in CELLO is an array for this same reason
18
+ * (`buildAgentRevocationTbs`, `buildPrimaryTransferTbs`, `buildSealTbs`, `buildParkContentTbs`).
19
+ *
20
+ * Consequences, all enforced below:
21
+ * - FIELD ORDER IS THE WIRE FORMAT. Reordering the array is a breaking hash change, not a
22
+ * refactor. Retrofitting a canonical form later breaks every hash already minted (spec §5).
23
+ * - THE FIELD SET IS CLOSED (spec §4). An unknown field is REJECTED, never ignored: silently
24
+ * dropping it would let two parties who disagree about the field set still agree on the hash,
25
+ * making the extra field unauthenticated data riding inside a "verified" signal.
26
+ * - `status` / `class` / `verified_at` ARE NOT IN THE PREIMAGE. They are mutable after minting —
27
+ * that is the entire point of excluding them. If `status` were hashed, revoking a signal would
28
+ * change its hash and the directory could never find it again.
29
+ * - OPTIONAL FIELDS ARE AN EXPLICIT `null` IN A FIXED SLOT, NEVER OMITTED (M10-D17). In an array,
30
+ * omitting a field shifts every later field and changes the arity, so an absent `expires_at`
31
+ * becomes confusable with a misaligned `supersedes_hash`.
32
+ * - NO FLOATING POINT. Timestamps are integer epoch SECONDS. A float would not survive
33
+ * byte-agreement across languages.
34
+ * - `payload` IS OPAQUE BYTES, never decoded or re-encoded. A payload parsed and re-serialized
35
+ * would change bytes under a different encoder version. This is also what INV-ZERO-BUMP rests
36
+ * on: a type this code has never seen must hash and flow through untouched.
37
+ */
38
+ import { hash as sha256 } from "@cello-protocol/crypto";
39
+ import { encodeCbor } from "./cbor.js";
40
+ /** Domain separation, bound as element 0 of the preimage array — the house convention
41
+ * (`AGENT_REVOCATION_DOMAIN`, `PRIMARY_TRANSFER_DOMAIN`). A trust-signal hash can therefore never
42
+ * collide with, or be replayed as, any other hashed/signed CELLO structure. */
43
+ export const TRUST_SIGNAL_DOMAIN = "CELLO-TSIG-v1";
44
+ /** The preimage field order. THIS ARRAY IS THE WIRE FORMAT — reordering it breaks every hash ever
45
+ * minted. It also doubles as the closed-set definition used to reject unknown fields. */
46
+ const PREIMAGE_FIELDS = [
47
+ "subject_kind",
48
+ "subject",
49
+ "issuer_kind",
50
+ "issuer_pubkey",
51
+ "type",
52
+ "schema_version",
53
+ "payload",
54
+ "issued_at",
55
+ "expires_at",
56
+ "supersedes_hash",
57
+ ];
58
+ /** Sanity bound on epoch-SECOND timestamps. A millisecond timestamp (~1.7e12) is the classic
59
+ * cross-implementation skew: it is a valid integer, so nothing would complain, and it would mint a
60
+ * signal that expires roughly thirty thousand years from now. Refuse it at the door. */
61
+ const MAX_EPOCH_SECONDS = 100_000_000_000; // ~year 5138
62
+ /** Above this, a JS `number` is encoded by cbor-x as an IEEE **float64** (major type 7), not as a
63
+ * uint64 — see `asCborInteger`. Anything hashed must cross this boundary as a BigInt. */
64
+ const CBOR_UINT32_MAX = 0xffff_ffff;
65
+ /** SHA-256. Named separately from SUPERSEDES_HASH_BYTES even though both are 32 today: they are
66
+ * independent sizes, and coupling them means a future change to one silently breaks the other. */
67
+ const SIGNAL_HASH_BYTES = 32;
68
+ const SUPERSEDES_HASH_BYTES = 32;
69
+ /**
70
+ * Integers above 2³² MUST cross into CBOR as BigInt, or they are hashed as a FLOAT.
71
+ *
72
+ * Measured with this package's encoder:
73
+ *
74
+ * encode(1768000000) -> 1a 69618a00 uint32 (fine)
75
+ * encode(4920000000) -> fb 41f25413e0000000 FLOAT64 (WRONG)
76
+ * encode(BigInt(4920000000)) -> 1b 0000000125413e00 uint64 (what RFC 8949 requires)
77
+ *
78
+ * cbor-x emits any JS `number` above 0xffffffff as major type 7 float64. A conforming CBOR
79
+ * implementation in Rust, Go, or Python emits a uint64 — so the two disagree on the preimage, and
80
+ * therefore on the hash, permanently and unfixably (spec §5: retrofitting the canonical form breaks
81
+ * every hash already minted).
82
+ *
83
+ * This is NOT hypothetical and NOT a 2106 problem: an `expires_at` a hundred years out is
84
+ * 1.768e9 + 3.15e9 = 4.92e9, well past 2³², and reachable by the first long-dated signal the portal
85
+ * mints. `buildSealTbs` (session.ts) and `buildAgentRevocationTbs` (revocation.ts) both carry this
86
+ * coercion. The trust-signal envelope shipped without it and a reviewer caught it.
87
+ */
88
+ function asCborInteger(value) {
89
+ return value > CBOR_UINT32_MAX ? BigInt(value) : value;
90
+ }
91
+ /** A non-negative integer safe to hash — rejected if it is a float, NaN, Infinity, or negative, and
92
+ * coerced to BigInt past 2³² so it never encodes as a float64. */
93
+ function requireHashableInteger(name, value, max, hint) {
94
+ if (typeof value !== "number" || !Number.isFinite(value)) {
95
+ throw new Error(`trust-signal envelope: ${name} must be a finite integer, got ${String(value)}`);
96
+ }
97
+ if (!Number.isInteger(value)) {
98
+ throw new Error(`trust-signal envelope: ${name} must be an integer — no floating point is hashable across implementations, got ${value}`);
99
+ }
100
+ if (value < 0) {
101
+ throw new Error(`trust-signal envelope: ${name} must not be negative, got ${value}`);
102
+ }
103
+ if (value > Number.MAX_SAFE_INTEGER) {
104
+ // Past 2^53 a JS number cannot even represent consecutive integers, so the value the caller
105
+ // meant and the value we hash may already differ. Refuse rather than hash an approximation.
106
+ throw new Error(`trust-signal envelope: ${name} exceeds Number.MAX_SAFE_INTEGER (${value}) — it cannot be hashed exactly`);
107
+ }
108
+ if (value >= max) {
109
+ throw new Error(`trust-signal envelope: ${name} is out of range (${value}) — ${hint}`);
110
+ }
111
+ return asCborInteger(value);
112
+ }
113
+ const requireEpochSeconds = (name, value) => requireHashableInteger(name, value, MAX_EPOCH_SECONDS, "this looks like a millisecond timestamp; the contract is epoch SECONDS");
114
+ /**
115
+ * Every hashed STRING must be NFC-normalized, and we REFUSE a string that is not — we never
116
+ * normalize it silently.
117
+ *
118
+ * Spec §5's list of cross-language hash-breakers names Unicode normalization explicitly. The same
119
+ * logical string differs on the wire: "é" is `62 c3a9` in NFC and `63 65cc81` in NFD. CBOR does not
120
+ * normalize, and RFC 8949's deterministic profile does not require NFC, so nothing downstream saves
121
+ * us.
122
+ *
123
+ * Refusing rather than normalizing is deliberate: silently normalizing would make two DIFFERENT
124
+ * inputs hash to the SAME signal — a collision we manufactured ourselves, which is strictly worse
125
+ * than refusing the odd one at the door.
126
+ */
127
+ function requireNfcString(name, value) {
128
+ if (typeof value !== "string" || value.length === 0) {
129
+ throw new Error(`trust-signal envelope: ${name} must be a non-empty string, got ${String(value)}`);
130
+ }
131
+ if (value.normalize("NFC") !== value) {
132
+ throw new Error(`trust-signal envelope: ${name} must be Unicode NFC-normalized — the same logical string hashes ` +
133
+ `differently in NFD ("é" is c3a9 in NFC, 65cc81 in NFD), so a non-NFC value would produce a ` +
134
+ `signal no other implementation can reproduce. Normalize it before minting.`);
135
+ }
136
+ return value;
137
+ }
138
+ /**
139
+ * Validate against the CLOSED preimage set and return the fixed-order array to encode.
140
+ *
141
+ * Every rejection here is deliberate and loud. An envelope that cannot be canonicalized must never
142
+ * be hashed "best effort" — a best-effort hash is a hash two parties can disagree about.
143
+ */
144
+ function toPreimage(envelope) {
145
+ if (envelope === null || typeof envelope !== "object") {
146
+ throw new Error(`trust-signal envelope: expected an object, got ${String(envelope)}`);
147
+ }
148
+ // Closed set, both directions: no unknown fields, no missing fields.
149
+ const known = new Set(PREIMAGE_FIELDS);
150
+ for (const key of Object.keys(envelope)) {
151
+ if (!known.has(key)) {
152
+ throw new Error(`trust-signal envelope: unknown envelope field "${key}". The preimage is a CLOSED set (spec §4) — ` +
153
+ `an unhashed field riding inside a verified signal is unauthenticated data. ` +
154
+ `Note status/class/verified_at are deliberately NOT hashed (they are mutable after minting) ` +
155
+ `and do not belong on this object.`);
156
+ }
157
+ }
158
+ for (const field of PREIMAGE_FIELDS) {
159
+ if (!(field in envelope)) {
160
+ throw new Error(`trust-signal envelope: missing mandatory field "${field}" (the preimage is a closed set — spec §4)`);
161
+ }
162
+ }
163
+ const { subject_kind, subject, issuer_kind, issuer_pubkey, type, schema_version, payload, expires_at, supersedes_hash } = envelope;
164
+ if (subject_kind !== "account" && subject_kind !== "agent") {
165
+ throw new Error(`trust-signal envelope: subject_kind must be "account" or "agent", got ${String(subject_kind)}`);
166
+ }
167
+ if (issuer_kind !== "portal" && issuer_kind !== "agent") {
168
+ throw new Error(`trust-signal envelope: issuer_kind must be "portal" or "agent", got ${String(issuer_kind)}`);
169
+ }
170
+ requireNfcString("subject", subject);
171
+ requireNfcString("type", type);
172
+ // issuer_pubkey is hex, and hex has a CASE. "AABB" and "aabb" are the same key and DIFFERENT bytes,
173
+ // so a portal that stores it uppercase and a directory that lowercases it on read would produce
174
+ // two different signal_hashes for one signal — a false hash_mismatch, per-node and per-version.
175
+ // Lowercase-only removes the ambiguity at zero cost. (Length/validity is NOT checked here: this
176
+ // component's job is byte-agreement, and the key's validity is the directory's to judge when it
177
+ // checks it against the authorized-issuer set.)
178
+ requireNfcString("issuer_pubkey", issuer_pubkey);
179
+ if (!/^[0-9a-f]+$/.test(issuer_pubkey) || issuer_pubkey.length % 2 !== 0) {
180
+ throw new Error(`trust-signal envelope: issuer_pubkey must be LOWERCASE hex with an even length, got "${issuer_pubkey}". ` +
181
+ `Hex has a case, and "AABB" and "aabb" are the same key but different preimage bytes — which would ` +
182
+ `mint two different hashes for one signal.`);
183
+ }
184
+ const schemaVersion = requireHashableInteger("schema_version", schema_version, CBOR_UINT32_MAX + 1, "a schema version is a small counter, not an identifier");
185
+ // The payload must be RAW BYTES. An object here would be encoded as a CBOR map — the one
186
+ // non-deterministic shape in the encoder — and the hash would silently depend on key insertion
187
+ // order. Callers serialize their own payload and hand us the bytes.
188
+ if (!(payload instanceof Uint8Array)) {
189
+ throw new Error(`trust-signal envelope: payload must be raw bytes (Uint8Array), got ${payload === null ? "null" : typeof payload}. ` +
190
+ `An object payload would be map-encoded, and CBOR maps are not deterministic under this encoder — ` +
191
+ `serialize the payload yourself and pass the bytes.`);
192
+ }
193
+ const issuedAt = requireEpochSeconds("issued_at", envelope.issued_at);
194
+ const expiresAt = expires_at === null ? null : requireEpochSeconds("expires_at", expires_at);
195
+ if (supersedes_hash !== null) {
196
+ if (!(supersedes_hash instanceof Uint8Array)) {
197
+ throw new Error(`trust-signal envelope: supersedes_hash must be 32 raw bytes or null, got ${typeof supersedes_hash}`);
198
+ }
199
+ if (supersedes_hash.length !== SUPERSEDES_HASH_BYTES) {
200
+ throw new Error(`trust-signal envelope: supersedes_hash must be exactly ${SUPERSEDES_HASH_BYTES} bytes, got ${supersedes_hash.length}`);
201
+ }
202
+ }
203
+ // FIXED ORDER. Element 0 is the domain tag; the nullable slots keep their position so arity is
204
+ // always 11 and no field can be mistaken for another (M10-D17).
205
+ return [
206
+ TRUST_SIGNAL_DOMAIN,
207
+ subject_kind,
208
+ subject,
209
+ issuer_kind,
210
+ issuer_pubkey,
211
+ type,
212
+ schemaVersion,
213
+ payload,
214
+ issuedAt,
215
+ expiresAt,
216
+ supersedes_hash,
217
+ ];
218
+ }
219
+ /** The canonical preimage bytes. Deterministic across every party that runs this code. */
220
+ export function encodeTrustSignalEnvelope(envelope) {
221
+ return encodeCbor(toPreimage(envelope));
222
+ }
223
+ /** The signal's identity: SHA-256 over the canonical preimage. This is the `signal_hash` that the
224
+ * directory stores, the holder presents, and the recipient re-derives. */
225
+ export function hashTrustSignalEnvelope(envelope) {
226
+ return sha256(encodeTrustSignalEnvelope(envelope));
227
+ }
228
+ /**
229
+ * Does this envelope actually hash to `expected`? Length is checked first, so a truncated hash can
230
+ * never pass by matching a prefix. The comparison is constant-time in the length of the input: a
231
+ * signal hash is public, but the habit is cheap and the alternative invites a timing oracle the day
232
+ * one of these stops being public.
233
+ *
234
+ * ⚠️ THIS RETURNS `false` FOR A WRONG HASH BUT **THROWS** FOR A MALFORMED ENVELOPE, AND THAT
235
+ * ASYMMETRY IS DELIBERATE. A wrong hash is a normal, expected answer ("no, this doesn't match"). A
236
+ * malformed envelope is not an answer at all — it is an envelope that cannot be canonicalized, so
237
+ * there is no hash to compare and any boolean would be a lie.
238
+ *
239
+ * The directory calls this on hostile input. DO NOT wrap it in `try { … } catch { return false }`:
240
+ * that converts "this envelope is unhashable garbage" into "this envelope simply didn't match",
241
+ * which is the silent swallow that hides the attack. Let it throw, and log the cause.
242
+ */
243
+ export function verifyTrustSignalHash(envelope, expected) {
244
+ if (!(expected instanceof Uint8Array) || expected.length !== SIGNAL_HASH_BYTES)
245
+ return false;
246
+ const actual = hashTrustSignalEnvelope(envelope);
247
+ let diff = 0;
248
+ for (let i = 0; i < actual.length; i++)
249
+ diff |= actual[i] ^ expected[i];
250
+ return diff === 0;
251
+ }
252
+ //# sourceMappingURL=trust-signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-signal.js","sourceRoot":"","sources":["../src/trust-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;gFAEgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAoCnD;0FAC0F;AAC1F,MAAM,eAAe,GAAG;IACtB,cAAc;IACd,SAAS;IACT,aAAa;IACb,eAAe;IACf,MAAM;IACN,gBAAgB;IAChB,SAAS;IACT,WAAW;IACX,YAAY;IACZ,iBAAiB;CACT,CAAC;AAEX;;yFAEyF;AACzF,MAAM,iBAAiB,GAAG,eAAe,CAAC,CAAC,aAAa;AAExD;0FAC0F;AAC1F,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC;mGACmG;AACnG,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED;mEACmE;AACnE,SAAS,sBAAsB,CAAC,IAAY,EAAE,KAAc,EAAE,GAAW,EAAE,IAAY;IACrF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,kCAAkC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,mFAAmF,KAAK,EAAE,CAAC,CAAC;IAC5I,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,8BAA8B,KAAK,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QACpC,4FAA4F;QAC5F,4FAA4F;QAC5F,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,qCAAqC,KAAK,iCAAiC,CAAC,CAAC;IAC7H,CAAC;IACD,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,qBAAqB,KAAK,OAAO,IAAI,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,mBAAmB,GAAG,CAAC,IAAY,EAAE,KAAc,EAAmB,EAAE,CAC5E,sBAAsB,CACpB,IAAI,EACJ,KAAK,EACL,iBAAiB,EACjB,wEAAwE,CACzE,CAAC;AAEJ;;;;;;;;;;;;GAYG;AACH,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAc;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,oCAAoC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,0BAA0B,IAAI,mEAAmE;YACjG,6FAA6F;YAC7F,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,QAA6B;IAC/C,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,kDAAkD,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,qEAAqE;IACrE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,eAAe,CAAC,CAAC;IAC/C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,kDAAkD,GAAG,8CAA8C;gBACnG,6EAA6E;gBAC7E,6FAA6F;gBAC7F,mCAAmC,CACpC,CAAC;QACJ,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,mDAAmD,KAAK,4CAA4C,CAAC,CAAC;QACxH,CAAC;IACH,CAAC;IAED,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,QAAQ,CAAC;IAEnI,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,OAAO,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,yEAAyE,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACnH,CAAC;IACD,IAAI,WAAW,KAAK,QAAQ,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,uEAAuE,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;IACD,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/B,oGAAoG;IACpG,gGAAgG;IAChG,gGAAgG;IAChG,gGAAgG;IAChG,gGAAgG;IAChG,gDAAgD;IAChD,gBAAgB,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;IACjD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CACb,wFAAwF,aAAa,KAAK;YAC1G,oGAAoG;YACpG,2CAA2C,CAC5C,CAAC;IACJ,CAAC;IACD,MAAM,aAAa,GAAG,sBAAsB,CAC1C,gBAAgB,EAChB,cAAc,EACd,eAAe,GAAG,CAAC,EACnB,wDAAwD,CACzD,CAAC;IACF,yFAAyF;IACzF,+FAA+F;IAC/F,oEAAoE;IACpE,IAAI,CAAC,CAAC,OAAO,YAAY,UAAU,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,sEAAsE,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,OAAO,IAAI;YACpH,mGAAmG;YACnG,oDAAoD,CACrD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAE7F,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,eAAe,YAAY,UAAU,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,4EAA4E,OAAO,eAAe,EAAE,CAAC,CAAC;QACxH,CAAC;QACD,IAAI,eAAe,CAAC,MAAM,KAAK,qBAAqB,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CAAC,0DAA0D,qBAAqB,eAAe,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1I,CAAC;IACH,CAAC;IAED,+FAA+F;IAC/F,gEAAgE;IAChE,OAAO;QACL,mBAAmB;QACnB,YAAY;QACZ,OAAO;QACP,WAAW;QACX,aAAa;QACb,IAAI;QACJ,aAAa;QACb,OAAO;QACP,QAAQ;QACR,SAAS;QACT,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,yBAAyB,CAAC,QAA6B;IACrE,OAAO,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;2EAC2E;AAC3E,MAAM,UAAU,uBAAuB,CAAC,QAA6B;IACnE,OAAO,MAAM,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA6B,EAAE,QAAoB;IACvF,IAAI,CAAC,CAAC,QAAQ,YAAY,UAAU,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,iBAAiB;QAAE,OAAO,KAAK,CAAC;IAC7F,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cello-protocol/protocol-types",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,15 +15,18 @@
15
15
  ".": {
16
16
  "import": "./dist/index.js",
17
17
  "types": "./dist/index.d.ts"
18
- }
18
+ },
19
+ "./test/vectors/*": "./test/vectors/*",
20
+ "./package.json": "./package.json"
19
21
  },
20
22
  "files": [
21
23
  "dist/",
24
+ "test/vectors/",
22
25
  "package.json"
23
26
  ],
24
27
  "dependencies": {
25
28
  "cbor-x": "^1.6.4",
26
- "@cello-protocol/crypto": "0.0.19"
29
+ "@cello-protocol/crypto": "0.0.22"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@claude-flow/testing": "3.0.0-alpha.6",
@@ -0,0 +1,10 @@
1
+ {
2
+ "description": "CELLO-CONNREQ-001: connection package canonical CBOR fixture",
3
+ "seed_hex": "deadbeefcafe0102030405060708090a0b0c0d0e0f101112131415161718191a",
4
+ "pseudonym_label": "ac009-agent",
5
+ "klocal_pubkey_derive": "ac009-klocal",
6
+ "primary_pubkey_derive": "ac009-primary",
7
+ "created_at": 1746057600000,
8
+ "expected_cbor_hex": "b900037170736575646f6e796d5f62696e64696e67b900066f70736575646f6e796d5f6c6162656c6b61633030392d6167656e746e6b5f6c6f63616c5f7075626b657958204c4dc1cdb4434b1cf0efc479f57f71c3e095ca28835ebf0fdd295fe48b6247916e7072696d6172795f7075626b657958409a8f5157d02c2588c6468da9e74b8414fe3719cd254de95689a045e01afa3bf69a8f5157d02c2588c6468da9e74b8414fe3719cd254de95689a045e01afa3bf66d6d6c5f6473615f7075626b65795905202e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad2e329a65af7790d0d9374d4893a5d5dcbab74f3c5b9f4cfe5788d57fb58273ad6a637265617465645f61741b0000019689249c00706d6c5f6473615f7369676e617475726559097494de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d60830a5d0f5bf7021ab000caf8a6994de5f68123bf5f8eaaf206a1daf32fa13d608306c656e646f7273656d656e7473806c6174746573746174696f6e7380",
9
+ "_encoding": "Plain CBOR (RFC 8949). Byte fields are CBOR byte strings; objects are CBOR maps. NOT cbor-x's record extension (tag 57343) \u2014 that is a cbor-x-private format no other CBOR reader can parse."
10
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "description": "CELLO-MSG-003 AC-007 canonical v1 envelope fixture. Cross-implementation regression guard. RFC 8949 §4.2.1.",
3
+ "_note": "full_envelope_cbor_hex excludes sender_signature (keypair-dependent). Tests verify tbs_cbor_hex and content_hash_hex; signature is tested separately with a generated keypair.",
4
+ "inputs": {
5
+ "protocol_version": 1,
6
+ "content_hex": "68656c6c6f",
7
+ "sender_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
8
+ "session_id_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
9
+ "last_seen_seq": 0,
10
+ "timestamp": 1700000000000
11
+ },
12
+ "expected": {
13
+ "content_hash_hex": "8a2a5c9b768827de5a9552c38a044c66959c68f6d2f21b5260af54d2f87db827",
14
+ "tbs_cbor_hex": "860158208a2a5c9b768827de5a9552c38a044c66959c68f6d2f21b5260af54d2f87db8275820aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa50bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb001b0000018bcfe56800"
15
+ }
16
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "description": "CELLO-SESSION-002 SI-004: genesis prev_root fixture. SHA-256(min(A,B) || max(A,B) || session_id || session_timestamp_be8) where pubkeys are sorted bytewise-lexicographically and timestamp is 8-byte big-endian unsigned integer (milliseconds since Unix epoch).",
3
+ "fixtures": [
4
+ {
5
+ "_note": "A < B (0xaa < 0xbb) — no reordering needed",
6
+ "inputs": {
7
+ "pubkey_a_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
8
+ "pubkey_b_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
9
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
10
+ "session_timestamp_ms": 1700000000000
11
+ },
12
+ "intermediate": {
13
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
14
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
15
+ "timestamp_be8_hex": "0000018bcfe56800",
16
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000018bcfe56800"
17
+ },
18
+ "expected_genesis_prev_root_hex": "e2f3e8e2eff39907ea59df96aa4f63dbdae3ca8cebe35ed5e5240b1543161932"
19
+ },
20
+ {
21
+ "_note": "B < A (0xaa < 0xbb) — args supplied in reversed order; result must be identical to fixture 1 with same effective min/max",
22
+ "inputs": {
23
+ "pubkey_a_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
24
+ "pubkey_b_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
25
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
26
+ "session_timestamp_ms": 1700000000000
27
+ },
28
+ "intermediate": {
29
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
30
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
31
+ "timestamp_be8_hex": "0000018bcfe56800",
32
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000018bcfe56800"
33
+ },
34
+ "expected_genesis_prev_root_hex": "e2f3e8e2eff39907ea59df96aa4f63dbdae3ca8cebe35ed5e5240b1543161932"
35
+ },
36
+ {
37
+ "_note": "Different pubkeys and timestamp — cross-check for distinct inputs",
38
+ "inputs": {
39
+ "pubkey_a_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
40
+ "pubkey_b_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
41
+ "session_id_hex": "11111111111111111111111111111111",
42
+ "session_timestamp_ms": 1746000000000
43
+ },
44
+ "intermediate": {
45
+ "min_pubkey_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
46
+ "max_pubkey_hex": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
47
+ "timestamp_be8_hex": "0000019685b5b400",
48
+ "preimage_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb111111111111111111111111111111110000019685b5b400"
49
+ },
50
+ "expected_genesis_prev_root_hex": "b78b94839803ec7f1654518c0fbb796f1ba2992f7f40d611cca7948b92ff2848"
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-007: M1 scan_result sentinel — canonical CBOR map. RFC 8949 §4.2.1.",
3
+ "_note": "CBOR map with 3 keys sorted by byte-length (RFC 8949 §4.2.1): score(5) < verdict(7) < model_hash(10). score=null, verdict='unscanned' (9 bytes), model_hash=<32 zero bytes>.",
4
+ "expected_cbor_hex": "a36573636f7265f6677665726469637469756e7363616e6e65646a6d6f64656c5f6861736858200000000000000000000000000000000000000000000000000000000000000000"
5
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-001: Structure 1 canonical CBOR fixture. RFC 8949 §4.2.1.",
3
+ "_note": "Canonical CBOR of the 6-element positional TBS array for protocol_version=1.",
4
+ "inputs": {
5
+ "protocol_version": 1,
6
+ "content_hash_hex": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
7
+ "sender_pubkey_hex": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
8
+ "session_id_hex": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
9
+ "last_seen_seq": 3,
10
+ "timestamp": 1700000000000
11
+ },
12
+ "expected_cbor_hex": "86015820cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc5820dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd50eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee031b0000018bcfe56800"
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "description": "CELLO-MERKLE-002 AC-002: Structure 2 canonical CBOR fixture. RFC 8949 §4.2.1.",
3
+ "_note": "Canonical CBOR of the 6-element positional relay-built leaf: [sequence_number, sender_pubkey, content_hash, sender_signature, scan_result, prev_root]. scan_result is the M1 sentinel: {score:null, verdict:'unscanned', model_hash:<32 zero bytes>}.",
4
+ "inputs": {
5
+ "sequence_number": 1,
6
+ "sender_pubkey_hex": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
7
+ "content_hash_hex": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
8
+ "sender_signature_hex": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
9
+ "prev_root_hex": "0000000000000000000000000000000000000000000000000000000000000000"
10
+ },
11
+ "expected_cbor_hex": "86015820dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd5820cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc5840aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa36573636f7265f6677665726469637469756e7363616e6e65646a6d6f64656c5f686173685820000000000000000000000000000000000000000000000000000000000000000058200000000000000000000000000000000000000000000000000000000000000000"
12
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "_comment": "CELLO-MSG-001 TBS v0 canonical CBOR fixture. Cross-implementation regression guard. RFC 8949 \u00a74.2.1.",
3
+ "protocol_version": 0,
4
+ "content_hash_hex": "0000000000000000000000000000000000000000000000000000000000000000",
5
+ "sender_pubkey_hex": "abababababababababababababababababababababababababababababababab",
6
+ "timestamp": 1746057600000,
7
+ "expected_cbor_hex": "8400582000000000000000000000000000000000000000000000000000000000000000005820abababababababababababababababababababababababababababababababab1b0000019689249c00"
8
+ }
@@ -0,0 +1,136 @@
1
+ {
2
+ "_comment": [
3
+ "DOD-CBOR-1 / DOD-INV-CANONICAL — FROZEN cross-party vectors for the trust-signal envelope.",
4
+ "Every party that derives a signal_hash — the portal at mint, the directory at submission and at",
5
+ "presentation, the holder and recipient daemons — MUST reproduce these bytes exactly. All three",
6
+ "repos check their SHIPPED @cello-protocol/protocol-types against this file, so a version skew",
7
+ "between repos fails loud here instead of silently forking the hash in production.",
8
+ "Preimage (array, fixed order, domain tag in slot 0 — M10-D15/D17):",
9
+ " [ 'CELLO-TSIG-v1', subject_kind, subject, issuer_kind, issuer_pubkey, type, schema_version,",
10
+ " payload, issued_at, expires_at|null, supersedes_hash|null ]",
11
+ "payload and supersedes_hash are hex-encoded HERE for legibility only; they are RAW BYTES in the",
12
+ "preimage. status/class/verified_at are deliberately NOT hashed (mutable after minting).",
13
+ "CHANGING A BYTE IN THIS FILE IS A PROTOCOL BREAK — it invalidates every signal ever minted."
14
+ ],
15
+ "vectors": [
16
+ {
17
+ "name": "reference — agent subject, portal issuer, no expiry, first mint",
18
+ "envelope": {
19
+ "subject_kind": "agent",
20
+ "subject": "agent-1",
21
+ "issuer_kind": "portal",
22
+ "issuer_pubkey": "aabb",
23
+ "type": "phone",
24
+ "schema_version": 1,
25
+ "payload": "01020304",
26
+ "issued_at": 1000000000,
27
+ "expires_at": null,
28
+ "supersedes_hash": null
29
+ },
30
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626570686f6e650144010203041a3b9aca00f6f6",
31
+ "signal_hash_hex": "760af64a292f96bffde522e21a8fcae0c31fc7826c4e2c947939a2da50803672"
32
+ },
33
+ {
34
+ "name": "account subject — one envelope, every agent under the account presents it (M10-D5)",
35
+ "envelope": {
36
+ "subject_kind": "account",
37
+ "subject": "acct_7f3a",
38
+ "issuer_kind": "portal",
39
+ "issuer_pubkey": "3d4f6a2b8c1e",
40
+ "type": "email",
41
+ "schema_version": 1,
42
+ "payload": "a1646b696e64",
43
+ "issued_at": 1768000000,
44
+ "expires_at": 1799536000,
45
+ "supersedes_hash": null
46
+ },
47
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631676163636f756e7469616363745f3766336166706f7274616c6c33643466366132623863316565656d61696c0146a1646b696e641a69618a001a6b42bd80f6",
48
+ "signal_hash_hex": "2b7ace095f07efbd1c8f5efef701a8969d2c6c8efb3f6291b27eefcd287fba16"
49
+ },
50
+ {
51
+ "name": "supersession — a re-minted track-record signal replacing an earlier one",
52
+ "envelope": {
53
+ "subject_kind": "agent",
54
+ "subject": "agent-1",
55
+ "issuer_kind": "portal",
56
+ "issuer_pubkey": "aabb",
57
+ "type": "session_count",
58
+ "schema_version": 2,
59
+ "payload": "deadbeef",
60
+ "issued_at": 1768000123,
61
+ "expires_at": 1768604923,
62
+ "supersedes_hash": "1111111111111111111111111111111111111111111111111111111111111111"
63
+ },
64
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626d73657373696f6e5f636f756e740244deadbeef1a69618a7b1a696ac4fb58201111111111111111111111111111111111111111111111111111111111111111",
65
+ "signal_hash_hex": "0184ae76433d9c4caf892d438bff3f64d44f0eed57470f0c141fee86b18e0bbb"
66
+ },
67
+ {
68
+ "name": "agent issuer — quoted-untrusted framing at the LLM (INV-FRAMING)",
69
+ "envelope": {
70
+ "subject_kind": "agent",
71
+ "subject": "agent-2",
72
+ "issuer_kind": "agent",
73
+ "issuer_pubkey": "ff00ff00",
74
+ "type": "endorsement",
75
+ "schema_version": 1,
76
+ "payload": "",
77
+ "issued_at": 1768000000,
78
+ "expires_at": null,
79
+ "supersedes_hash": null
80
+ },
81
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d32656167656e746866663030666630306b656e646f7273656d656e7401401a69618a00f6f6",
82
+ "signal_hash_hex": "39d148391745fa3ea26b8fe57b5633c4bec7064a896527abb9300355b02ec5a6"
83
+ },
84
+ {
85
+ "name": "UNKNOWN type — a type this code has never seen must hash identically (INV-TYPE-CARRY)",
86
+ "envelope": {
87
+ "subject_kind": "agent",
88
+ "subject": "agent-1",
89
+ "issuer_kind": "portal",
90
+ "issuer_pubkey": "aabb",
91
+ "type": "some_type_invented_next_year",
92
+ "schema_version": 1,
93
+ "payload": "00",
94
+ "issued_at": 1768000000,
95
+ "expires_at": null,
96
+ "supersedes_hash": null
97
+ },
98
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c6461616262781c736f6d655f747970655f696e76656e7465645f6e6578745f796561720141001a69618a00f6f6",
99
+ "signal_hash_hex": "9a637f2ca5b4fbb0f78c9eb2d33cfc433706290a7c4a4e6bf2628d258b90f1c9"
100
+ },
101
+ {
102
+ "name": "payload that LOOKS like CBOR — embedded verbatim, never parsed",
103
+ "envelope": {
104
+ "subject_kind": "agent",
105
+ "subject": "agent-1",
106
+ "issuer_kind": "portal",
107
+ "issuer_pubkey": "aabb",
108
+ "type": "canary_test",
109
+ "schema_version": 1,
110
+ "payload": "d840009fff",
111
+ "issued_at": 1768000000,
112
+ "expires_at": null,
113
+ "supersedes_hash": null
114
+ },
115
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626b63616e6172795f746573740145d840009fff1a69618a00f6f6",
116
+ "signal_hash_hex": "5dc67366d7b8fc515a6d638b432d90e29a2f9841101a3ac1745ca7b602e2f837"
117
+ },
118
+ {
119
+ "name": "FAR-FUTURE expiry past 2^32 — must hash as a CBOR uint64 (1b...), NEVER a float64 (fb...)",
120
+ "envelope": {
121
+ "subject_kind": "agent",
122
+ "subject": "agent-1",
123
+ "issuer_kind": "portal",
124
+ "issuer_pubkey": "aabb",
125
+ "type": "phone",
126
+ "schema_version": 1,
127
+ "payload": "01020304",
128
+ "issued_at": 1768000000,
129
+ "expires_at": 4920000000,
130
+ "supersedes_hash": null
131
+ },
132
+ "preimage_hex": "8b6d43454c4c4f2d545349472d7631656167656e74676167656e742d3166706f7274616c64616162626570686f6e650144010203041a69618a001b0000000125413e00f6",
133
+ "signal_hash_hex": "3c95520077ca1bb25f360c886008691bab1114e1ecad43ba90558df5d5a0b79e"
134
+ }
135
+ ]
136
+ }