@useaccord/sdk 0.3.0 → 0.4.0

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.
@@ -25,7 +25,7 @@ export declare function verifyDeliveryKeyRegistration(jurorEd25519Pub: Uint8Arra
25
25
  /** Wallet `signMessage` seam (app wires its wallet adapter; ADR-0015 split). */
26
26
  export type SignMessage = (message: Uint8Array) => Promise<Uint8Array>;
27
27
  export interface RegisterDeliveryKeyParams {
28
- /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */
28
+ /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */
29
29
  endpoint: string;
30
30
  /** Juror's Solana address, base58. */
31
31
  juror: string;
@@ -1 +1 @@
1
- {"version":3,"file":"delivery-key.d.ts","sourceRoot":"","sources":["../../src/evidence/delivery-key.ts"],"names":[],"mappings":"AAgCA,gFAAgF;AAChF,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAEhD,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,SAAS,EAAE,UAAU,CAAC;IACtB,iCAAiC;IACjC,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,IAAI,eAAe,CAGrD;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM,GACrB,UAAU,CAOZ;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,UAAU,EAC3B,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,UAAU,GACd,OAAO,CAOT;AAMD,gFAAgF;AAChF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAEvE,MAAM,WAAW,yBAAyB;IACxC,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,MAAM,EAAE,UAAU,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,qBAAqB,CAAC,CAkBhC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CASxC;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC,CAWtC;AAMD,wDAAwD;AACxD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,eAAe,GAAG,UAAU,CAOrE;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAGxE;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAClC,GAAG,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAM1B"}
1
+ {"version":3,"file":"delivery-key.d.ts","sourceRoot":"","sources":["../../src/evidence/delivery-key.ts"],"names":[],"mappings":"AAgCA,gFAAgF;AAChF,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAEhD,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,SAAS,EAAE,UAAU,CAAC;IACtB,iCAAiC;IACjC,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,IAAI,eAAe,CAGrD;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM,GACrB,UAAU,CAOZ;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,UAAU,EAC3B,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,UAAU,GACd,OAAO,CAOT;AAMD,gFAAgF;AAChF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAEvE,MAAM,WAAW,yBAAyB;IACxC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,MAAM,EAAE,UAAU,CAAC;IACnB,qEAAqE;IACrE,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,qBAAqB,CAAC,CAkBhC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CASxC;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC,CAWtC;AAMD,wDAAwD;AACxD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,eAAe,GAAG,UAAU,CAOrE;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAGxE;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAClC,GAAG,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAM1B"}
@@ -1,6 +1,6 @@
1
1
  import type { JurorBundle } from "./ecies.js";
2
2
  export interface FetchDeliveryParams {
3
- /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */
3
+ /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */
4
4
  endpoint: string;
5
5
  /** Dispute address, base58. */
6
6
  dispute: string;
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/evidence/fetch.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;CACvD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAqBD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAgBlC;AAMD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAQlB"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/evidence/fetch.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;CACvD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAqBD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAgBlC;AAMD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAQlB"}
@@ -9,7 +9,7 @@
9
9
  * Kept behind a sub-path export so Arbitrables that only do `create_dispute`
10
10
  * CPI don't pull the `@noble` crypto stack.
11
11
  *
12
- * The daemon base URL (`EVIDENCE_DAEMON_URL`) is deployment-specific and stays
12
+ * The daemon base URL (app `useEvidenceDaemonUrl()`) is deployment-specific and stays
13
13
  * app-side; `publishEvidence` takes `endpoint` as a parameter (ADR-0011).
14
14
  *
15
15
  * Authority: ADR-0006 (evidence model), ADR-0011 (evidence-daemon),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/evidence/crypto.ts","../../src/evidence/keys.ts","../../src/evidence/ecies.ts","../../src/evidence/manifest.ts","../../src/evidence/options.ts","../../src/evidence/parse.ts","../../src/evidence/base64.ts","../../src/evidence/publish.ts","../../src/evidence/delivery-key.ts","../../src/evidence/fetch.ts"],"sourcesContent":["/**\n * evidence/crypto.ts — symmetric primitive spine for the Accord evidence\n * protocol (ADR-0006).\n *\n * Every primitive delegates to the audited `@noble` suite (`@noble/ciphers`,\n * `@noble/hashes`). Nothing here is hand-rolled: this module only fixes the two\n * protocol policies the claimant/operator/juror must share byte-for-byte —\n *\n * - AES-256-GCM wire blob = `nonce(12) ‖ ciphertext ‖ tag(16)`\n * - HKDF-SHA256 with an empty salt and a role-pinning `info` label\n *\n * — exactly as {@link ../methods/mst.ts} fixes a node layout over a library\n * hash. Functions are `async` to match the SPEC's stated crypto model and keep\n * callers (the daemon's injected ports, SDK consumers) await-shaped.\n *\n * Authority: apps/evidence-daemon/SPEC.md §Crypto model; ADR-0006.\n */\nimport { gcm } from \"@noble/ciphers/aes\";\nimport { hkdf } from \"@noble/hashes/hkdf\";\nimport { sha256 as nobleSha256 } from \"@noble/hashes/sha256\";\nimport { randomBytes } from \"@noble/hashes/utils\";\n\nconst AES_NONCE_BYTES = 12;\nconst AES_TAG_BYTES = 16;\n\n/** SHA-256 digest (32 bytes). */\nexport async function sha256(data: Uint8Array): Promise<Uint8Array> {\n return nobleSha256(data);\n}\n\n/**\n * HKDF-SHA256 with an empty salt (per SPEC §Crypto model) and a protocol `info`\n * label that pins the derived key to its role (ingest vs deliver).\n */\nexport async function hkdfSha256(\n ikm: Uint8Array,\n info: Uint8Array,\n length = 32,\n): Promise<Uint8Array> {\n return hkdf(nobleSha256, ikm, new Uint8Array(0), info, length);\n}\n\n/** AES-256-GCM encrypt → `nonce(12) ‖ ciphertext ‖ tag(16)` (SPEC wire format). */\nexport async function aesGcmEncrypt(\n key: Uint8Array,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n requireKey(key);\n const nonce = randomBytes(AES_NONCE_BYTES);\n const ct = gcm(key, nonce).encrypt(plaintext); // ciphertext ‖ tag(16)\n const out = new Uint8Array(AES_NONCE_BYTES + ct.length);\n out.set(nonce, 0);\n out.set(ct, AES_NONCE_BYTES);\n return out;\n}\n\n/** AES-256-GCM decrypt of a `nonce(12) ‖ ciphertext ‖ tag(16)` blob. Throws on auth failure. */\nexport async function aesGcmDecrypt(\n key: Uint8Array,\n blob: Uint8Array,\n): Promise<Uint8Array> {\n requireKey(key);\n if (blob.length < AES_NONCE_BYTES + AES_TAG_BYTES) {\n throw new Error(\"AES-GCM blob too short (missing nonce/tag)\");\n }\n const nonce = blob.subarray(0, AES_NONCE_BYTES);\n const ct = blob.subarray(AES_NONCE_BYTES);\n return gcm(key, nonce).decrypt(ct);\n}\n\nfunction requireKey(key: Uint8Array): void {\n if (key.length !== 32) {\n throw new Error(`AES-256 key must be 32 bytes, got ${key.length}`);\n }\n}\n","/**\n * evidence/keys.ts — Ed25519/X25519 key material + the operator keyring\n * contract for the evidence protocol (ADR-0006 / ADR-0011; delivery amended\n * by ADR-0034).\n *\n * Ingest (claimant → operator) dual-uses the on-chain `evidence_operator`\n * Ed25519 key: signing on-chain (Ed25519) + encryption off-chain via its\n * Montgomery (X25519) form — a libsodium-style\n * `crypto_sign_ed25519_{pk,sk}_to_curve25519`. Delivery no longer dual-uses\n * the juror's Solana key: it targets the juror's registered X25519 Delivery\n * Key (ADR-0034, `delivery-key.ts`), so these Ed→X helpers survive ONLY on\n * the ingest path.\n *\n * Backed by `@noble/curves`; the wrappers only add length + zero-key\n * validation at the public seam.\n */\nimport {\n ed25519,\n edwardsToMontgomeryPriv,\n edwardsToMontgomeryPub,\n x25519,\n} from \"@noble/curves/ed25519\";\n\n/** Ed25519 keypair material held by the operator. The secret never leaves the keyring. */\nexport interface Ed25519Keypair {\n /** 32-byte Ed25519 public key (matches the on-chain `evidence_operator` field). */\n publicKey: Uint8Array;\n /** 32-byte Ed25519 secret seed. */\n secretKey: Uint8Array;\n}\n\n/**\n * Per-Subaccord operator keyring contract. Resolves the Ed25519 secret whose\n * pubkey matches a Subaccord's on-chain `evidence_operator`, or null when this\n * operator does not operate that Subaccord (the caller maps null → 404). The\n * on-chain field is the binding: no key↔subaccord mapping is held out-of-band.\n *\n * The daemon supplies the env-backed implementation; a file/KMS source can\n * satisfy this without touching callers (ADR-0011).\n */\nexport interface Keyring {\n forOperator(operatorPubkey: Uint8Array): Promise<Ed25519Keypair | null>;\n}\n\n// --- Ed25519 <-> X25519 (libsodium-style; backed by @noble/curves) ---------\n\n/** Convert an Ed25519 public key (32 bytes) to its X25519 (Montgomery) form. */\nexport function ed25519ToX25519PublicKey(ed25519Pubkey: Uint8Array): Uint8Array {\n requireLen(ed25519Pubkey, 32, \"Ed25519 public key\");\n // Pubkey::default() converts to Montgomery u=1 (order-2) — the X25519 shared\n // secret would be all zeros and noble throws a cryptic error downstream.\n // Canon's create_list CPI still sets evidence_operator = Pubkey::default().\n if (ed25519Pubkey.every((b) => b === 0)) {\n throw new Error(\n \"Ed25519 public key is all zeros (Solana Pubkey::default — unset evidence_operator on-chain); refusing X25519 conversion\",\n );\n }\n return edwardsToMontgomeryPub(ed25519Pubkey);\n}\n\n/** Convert an Ed25519 secret seed (32 bytes) to its X25519 (Montgomery) secret. */\nexport function ed25519SecretToX25519(ed25519SecretSeed: Uint8Array): Uint8Array {\n requireLen(ed25519SecretSeed, 32, \"Ed25519 secret seed\");\n return edwardsToMontgomeryPriv(ed25519SecretSeed);\n}\n\n/** X25519 ECDH: shared secret from a Montgomery secret + a Montgomery public key. */\nexport function x25519SharedSecret(\n mySecret: Uint8Array,\n theirPublic: Uint8Array,\n): Uint8Array {\n return x25519.getSharedSecret(mySecret, theirPublic);\n}\n\n/** Fresh ephemeral X25519 keypair for one ECIES operation. */\nexport function newX25519KeyPair(): { secret: Uint8Array; publicKey: Uint8Array } {\n const secret = x25519.utils.randomPrivateKey();\n return { secret, publicKey: x25519.getPublicKey(secret) };\n}\n\n/** Derive the Ed25519 public key from a 32-byte secret seed (keyring indexing). */\nexport function ed25519PublicKeyFromSeed(seed: Uint8Array): Uint8Array {\n requireLen(seed, 32, \"Ed25519 secret seed\");\n return ed25519.getPublicKey(seed);\n}\n\nfunction requireLen(b: Uint8Array, n: number, what: string): void {\n if (b.length !== n) {\n throw new Error(`${what} must be ${n} bytes, got ${b.length}`);\n }\n}\n","/**\n * evidence/ecies.ts — the Accord evidence encryption protocol (ADR-0006,\n * delivery amended by ADR-0034).\n *\n * One ECIES-style envelope, two roles:\n *\n * Ingest (claimant → operator): the claimant encrypts plaintext to the\n * operator's Ed25519→X25519 key and posts the bundle; the operator decrypts.\n *\n * Deliver (operator → drawn juror): the operator re-encrypts plaintext to\n * the juror's registered Delivery Key (a raw X25519 key, ADR-0034); only\n * that juror's Delivery Key secret decrypts. No Ed→X dual-use here.\n *\n * This is the single byte-exact implementation all three parties import — the\n * claimant and juror run it off-daemon (they are SDK consumers), so the protocol\n * cannot drift between participants. Composition only: every primitive\n * (ECDH, HKDF, AES-GCM, Montgomery conversion) comes from {@link ./crypto} /\n * {@link ./keys}; the only thing owned here is the construction order and the\n * HKDF `info` role labels.\n *\n * Plaintext exists only ephemerally between decrypt and re-encrypt on the\n * operator side; nothing persisted is ever plaintext (SPEC §Encrypted-at-rest).\n *\n * Authority: apps/evidence-daemon/SPEC.md §Crypto model; ADR-0006, ADR-0034.\n */\nimport { randomBytes } from \"@noble/hashes/utils\";\nimport { aesGcmDecrypt, aesGcmEncrypt, hkdfSha256, sha256 } from \"./crypto.js\";\nimport {\n ed25519SecretToX25519,\n ed25519ToX25519PublicKey,\n newX25519KeyPair,\n x25519SharedSecret,\n} from \"./keys.js\";\n\n/** HKDF `info` labels pin the derivation to its protocol role. */\nexport const INGEST_INFO = \"accord-ingest-v1\";\nexport const DELIVER_INFO = \"accord-deliver-v1\";\n\nconst te = new TextEncoder();\n\n/** Stored ciphertext bundle (ciphertext only — no plaintext field). */\nexport interface IngestBundle {\n /** AES-GCM(dek, plaintext) — nonce(12) prepended. */\n ct: Uint8Array;\n /** Claimant's ephemeral X25519 public key (32 bytes). */\n claimant_ephem_pub: Uint8Array;\n /** AES-GCM(k_in, dek) — nonce(12) prepended. */\n wrapped: Uint8Array;\n /** sha256(plaintext); matches the dispute's on-chain `evidence_hash`. */\n plaintext_hash: Uint8Array;\n}\n/** A bundle re-encrypted to a single drawn juror's Delivery Key (ADR-0034). */\nexport interface JurorBundle {\n /** AES-GCM(k_out, plaintext) — nonce(12) prepended. */\n out: Uint8Array;\n /** Operator's ephemeral X25519 public key for this delivery (32 bytes). */\n operator_ephem_pub: Uint8Array;\n}\n/**\n * Claimant-side ingest encryption. Produces the bundle posted to the operator.\n * Claimants run this off-daemon; the operator never calls it in production (it\n * is exposed so claimant SDK clients share one implementation, and for\n * round-trips / test vectors).\n */\nexport async function claimantEncrypt(\n plaintext: Uint8Array,\n operatorEd25519Pub: Uint8Array,\n): Promise<IngestBundle> {\n const dek = randomBytes(32);\n const ct = await aesGcmEncrypt(dek, plaintext);\n\n const ephem = newX25519KeyPair();\n const opX25519Pub = ed25519ToX25519PublicKey(operatorEd25519Pub);\n const shared = x25519SharedSecret(ephem.secret, opX25519Pub);\n const kIn = await hkdfSha256(shared, te.encode(INGEST_INFO));\n const wrapped = await aesGcmEncrypt(kIn, dek);\n\n // dek wiped implicitly (local ref dropped); plaintext_hash is metadata, not secret.\n return {\n ct,\n claimant_ephem_pub: ephem.publicKey,\n wrapped,\n plaintext_hash: await sha256(plaintext),\n };\n}\n\n/**\n * Operator-side decrypt of a stored bundle → plaintext (in-memory only).\n * Callers MUST run {@link verifyIntegrity} next.\n */\nexport async function operatorDecrypt(\n bundle: IngestBundle,\n operatorEd25519SecretSeed: Uint8Array,\n): Promise<Uint8Array> {\n const opX25519Sk = ed25519SecretToX25519(operatorEd25519SecretSeed);\n const shared = x25519SharedSecret(opX25519Sk, bundle.claimant_ephem_pub);\n const kIn = await hkdfSha256(shared, te.encode(INGEST_INFO));\n const dek = await aesGcmDecrypt(kIn, bundle.wrapped); // throws on auth failure\n return aesGcmDecrypt(dek, bundle.ct);\n}\n\n/** Operator re-encrypts plaintext to the juror's registered Delivery Key (ADR-0034). */\nexport async function deliverToDeliveryKey(\n plaintext: Uint8Array,\n deliveryEncPub: Uint8Array,\n): Promise<JurorBundle> {\n const ephem = newX25519KeyPair();\n const shared = x25519SharedSecret(ephem.secret, deliveryEncPub);\n const kOut = await hkdfSha256(shared, te.encode(DELIVER_INFO));\n const out = await aesGcmEncrypt(kOut, plaintext);\n return { out, operator_ephem_pub: ephem.publicKey };\n}\n\n/** Juror-side decrypt of a delivered bundle with the Delivery Key secret (ADR-0034). */\nexport async function jurorDecryptDelivery(\n delivered: JurorBundle,\n deliverySecret: Uint8Array,\n): Promise<Uint8Array> {\n const shared = x25519SharedSecret(deliverySecret, delivered.operator_ephem_pub);\n const kOut = await hkdfSha256(shared, te.encode(DELIVER_INFO));\n return aesGcmDecrypt(kOut, delivered.out);\n}\n\n/**\n * Mandatory integrity gate (SPEC §3, §Failure modes): `sha256(plaintext)` MUST\n * equal the dispute's on-chain `evidence_hash`. Both operands are public hashes,\n * so a plain compare suffices (no constant-time requirement). Throws on mismatch\n * — at ingest this rejects a bad upload, at delivery it refuses + alerts.\n */\nexport async function verifyIntegrity(\n plaintext: Uint8Array,\n evidenceHash: Uint8Array,\n): Promise<void> {\n const h = await sha256(plaintext);\n if (h.length !== evidenceHash.length) {\n throw new Error(\"integrity gate failed: sha256(plaintext) != evidence_hash\");\n }\n for (let i = 0; i < h.length; i++) {\n if (h[i] !== evidenceHash[i]) {\n throw new Error(\"integrity gate failed: sha256(plaintext) != evidence_hash\");\n }\n }\n}\n","/**\n * manifest.ts — serialize the `accord-evidence/v1` manifest into a single\n * deterministic Uint8Array buffer. That one buffer feeds the YAML preview,\n * `sha256`→`evidence_hash`, and `claimantEncrypt`→POST. Never re-serialize.\n *\n * Authority: EVIDENCE-FORMAT.md §2 (no canonicalization needed — manifest\n * delivered verbatim), milestone accord-ebel §3 (single-buffer invariant).\n */\nimport type { Address } from \"@solana/kit\";\n\n/** All-zero `[u8;32]` sentinel — juror skips leaf verification (root gate still applies). */\nexport const SHA256_ZERO = new Uint8Array(32);\n\nexport interface ManifestEntryInput {\n /** URL or relative POSIX path to the evidence resource. */\n path: string;\n /** Leaf sha256 (32 bytes). Defaults to {@link SHA256_ZERO} when unknown. */\n sha256?: Uint8Array;\n}\n\nexport interface ManifestInput {\n /** Per-dispute 32-byte random salt (app-generated). */\n salt: Uint8Array;\n title: string;\n /**\n * Optional markdown claim body (the challenger's argument). Serialized as a\n * single JSON-escaped line so the manifest stays single-line-per-field and\n * the targeted parser needs no block-scalar handling. Omitted entirely when\n * absent/empty → byte-identical to a pre-`description` manifest (sha256\n * stable). Rendering is display-only; committed bytes are never altered.\n */\n description?: string;\n /** Ordered option labels; `Dispute.options[i] = sha256(salt ‖ utf8(label_i))`. */\n labels: string[];\n entries: ManifestEntryInput[];\n}\n\nexport interface ManifestCtx {\n dispute: Address;\n subaccord: Address;\n filer: Address;\n /** ISO-8601 UTC timestamp string. */\n filedAt: string;\n}\n\nfunction hex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/** Minimal YAML double-quote escaping for a string value. */\nfunction yamlQuote(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * Serialize manifest input + ctx into a single `Uint8Array` (UTF-8 YAML).\n * Hand-serialized — the manifest is a flat object with two list fields; no YAML\n * library needed (EVIDENCE-FORMAT.md §2: no canonicalization, delivered verbatim).\n */\nexport function buildManifest(\n input: ManifestInput,\n ctx: ManifestCtx,\n): Uint8Array {\n const lines: string[] = [\n \"schema: accord-evidence/v1\",\n `dispute: ${ctx.dispute}`,\n `subaccord: ${ctx.subaccord}`,\n `filer: ${ctx.filer}`,\n `filed_at: ${ctx.filedAt}`,\n \"language: en\",\n `title: ${yamlQuote(input.title)}`,\n // description is optional; omitted when absent/empty → backward-compatible\n // bytes (sha256 stable). JSON-escaped so multi-line markdown stays on one\n // logical line (no YAML block-scalar parsing needed).\n ...(input.description\n ? [`description: ${JSON.stringify(input.description)}`]\n : []),\n \"\",\n `option_salt: ${hex(input.salt)}`,\n \"options:\",\n ...input.labels.map(\n (label, index) => ` - { index: ${index}, label: ${yamlQuote(label)} }`,\n ),\n \"\",\n \"entries:\",\n ...input.entries.map(\n (e) =>\n ` - { path: ${yamlQuote(e.path)}, sha256: \"${hex(e.sha256 ?? SHA256_ZERO)}\" }`,\n ),\n ];\n return new TextEncoder().encode(lines.join(\"\\n\") + \"\\n\");\n}\n","/**\n * evidence/options.ts — option-salt derivation + self-verify.\n *\n * `Dispute.options[i] = sha256(option_salt ‖ utf8(label_i))` (EVIDENCE-FORMAT.md §4).\n * The salt is app-generated, per-dispute, and lives only in the manifest.\n * `verifyOptionHashes` runs pre-submit — nothing on-chain enforces this today\n * (ADR-0017 open; D2 trustlessly closed when it lands).\n */\nimport { sha256 } from \"./crypto.js\";\n\n/** Generate a 32-byte random salt via the Web Crypto API. */\nexport function generateSalt(): Uint8Array {\n return crypto.getRandomValues(new Uint8Array(32));\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.length + b.length);\n out.set(a);\n out.set(b, a.length);\n return out;\n}\n\nconst te = new TextEncoder();\n\n/**\n * Derive `Dispute.options[i] = sha256(option_salt ‖ utf8(label_i))` for each label.\n * Returns 32-byte digests in label order. Throws if salt is not 32 bytes — a\n * wrong-length salt produces silently-wrong hashes (correctness footgun).\n */\nexport async function deriveOptionHashes(\n salt: Uint8Array,\n labels: string[],\n): Promise<Uint8Array[]> {\n if (salt.length !== 32) {\n throw new Error(`InvalidOptionSalt: expected 32 bytes, got ${salt.length}`);\n }\n return Promise.all(\n labels.map((label) => sha256(concat(salt, te.encode(label)))),\n );\n}\n\nfunction equalBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n return a.every((v, i) => v === b[i]);\n}\n\n/**\n * Verify that `hashes[i] == sha256(salt ‖ utf8(labels[i]))` for all i.\n * Throws on mismatch — fails closed (D2 self-verify, app-side).\n */\nexport async function verifyOptionHashes(\n salt: Uint8Array,\n labels: string[],\n hashes: Uint8Array[],\n): Promise<void> {\n if (labels.length !== hashes.length) {\n throw new Error(\n `OptionHashMismatch: ${labels.length} labels vs ${hashes.length} hashes`,\n );\n }\n const derived = await deriveOptionHashes(salt, labels);\n for (let i = 0; i < derived.length; i++) {\n if (!equalBytes(derived[i]!, hashes[i]!)) {\n throw new Error(`option-hash mismatch at index ${i}`);\n }\n }\n}\n","/**\n * parse.ts — parse the `accord-evidence/v1` manifest returned by the evidence\n * daemon (`useManifest`). Shared by `EvidenceManifest` (full render) and any\n * feature that needs a slice of the manifest (e.g. option labels for voting).\n *\n * The manifest is YAML (produced by `buildManifest`); the daemon may return it\n * as a raw UTF-8 string or as a parsed JSON object. This targeted parser needs\n * no YAML dependency.\n */\n\nexport interface ParsedManifest {\n /** Top-level `schema:` value; \"\" when absent (pre-schema bytes). The daemon\n * dispatches multifile on schema + entries (milestone accord-5d0r). */\n schema: string;\n title: string;\n /** Markdown claim body; \"\" when absent (pre-`description` manifests). */\n description: string;\n filedAt: string;\n filer: string;\n subaccord: string;\n dispute: string;\n optionSalt: string;\n options: { index: number; label: string }[];\n entries: { path: string; sha256: string }[];\n}\n/** Flow-map scalar: `\"quoted\"` wins; else the run up to `,`/`}`, trimmed. */\nfunction scalar(item: string, key: string): string {\n const quoted = item.match(new RegExp(`${key}:\\\\s*\"([^\"]*)\"`))?.[1];\n if (quoted !== undefined) return quoted;\n return item.match(new RegExp(`${key}:\\\\s*([^,}]+)`))?.[1]?.trim() ?? \"\";\n}\n\n\n/** Parse the `accord-evidence/v1` YAML format (produced by `buildManifest`). */\nexport function parseManifest(text: string): ParsedManifest {\n const lines = text.split(\"\\n\");\n const getField = (key: string): string | undefined =>\n lines\n .find((l) => l.startsWith(`${key}: `))\n ?.slice(`${key}: `.length)\n .replace(/^\"(.*)\"$/, \"$1\");\n\n const options: { index: number; label: string }[] = [];\n const entries: { path: string; sha256: string }[] = [];\n let section: \"options\" | \"entries\" | null = null;\n\n for (const line of lines) {\n if (line === \"options:\") {\n section = \"options\";\n continue;\n }\n if (line === \"entries:\") {\n section = \"entries\";\n continue;\n }\n if (!line.startsWith(\" - {\")) continue;\n\n const item = line.slice(\" - \".length);\n if (section === \"options\") {\n const index = Number(item.match(/index:\\s*(\\d+)/)?.[1] ?? -1);\n const label = item.match(/label:\\s*\"([^\"]*)\"/)?.[1] ?? \"\";\n options.push({ index, label });\n } else if (section === \"entries\") {\n // Quoted (buildManifest emits yamlQuote) or unquoted scalar (riprap\n // wizard) — both valid up to the row's `,`/`}` delimiter.\n const path = scalar(item, \"path\");\n const sha256 = scalar(item, \"sha256\");\n entries.push({ path, sha256 });\n }\n }\n\n // description is JSON-escaped on a single line (see buildManifest); getField\n // strips the outer quotes, so re-quote + JSON.parse to unescape markdown.\n // Total function: malformed bytes fall back to the raw string — callers at\n // trust boundaries reject the manifest, the parser never throws.\n const descriptionRaw = getField(\"description\");\n let description = descriptionRaw ?? \"\";\n if (descriptionRaw) {\n try {\n description = JSON.parse(`\"${descriptionRaw}\"`) as string;\n } catch {\n description = descriptionRaw;\n }\n }\n\n return {\n schema: getField(\"schema\") ?? \"\",\n title: getField(\"title\") ?? \"Untitled dispute\",\n description,\n filedAt: getField(\"filed_at\") ?? \"—\",\n filer: getField(\"filer\") ?? \"—\",\n subaccord: getField(\"subaccord\") ?? \"—\",\n dispute: getField(\"dispute\") ?? \"—\",\n optionSalt: getField(\"option_salt\") ?? \"—\",\n options,\n entries,\n };\n}\n\n/**\n * Ordered option labels from a manifest fetched via `useManifest` (a YAML\n * string for the `accord-evidence/v1` format, or a parsed JSON object).\n *\n * Returns labels in index order, contiguously from 0 up to the first missing\n * or blank entry. Callers fall back to the on-chain option hash for any index\n * not covered. Returns `[]` when the manifest is absent (no bundle stored).\n */\nexport function optionLabels(manifest: unknown): string[] {\n if (manifest == null) return [];\n const parsed =\n typeof manifest === \"string\"\n ? parseManifest(manifest)\n : parseManifest(JSON.stringify(manifest));\n\n const byIndex = new Map<number, string>();\n for (const o of parsed.options) {\n if (o.index >= 0) byIndex.set(o.index, o.label);\n }\n\n const labels: string[] = [];\n for (let i = 0; i < parsed.options.length; i++) {\n const label = byIndex.get(i);\n if (typeof label === \"string\" && label.trim()) {\n labels.push(label);\n } else {\n break;\n }\n }\n return labels;\n}\n","/**\n * evidence/base64.ts — base64 codec for the daemon's JSON wire fields\n * (internal to the evidence module; not exported from the barrel).\n *\n * Standard alphabet, no padding concerns for this protocol's fixed-size\n * fields. Runs on the global `btoa`/`atob` (Node ≥16, Bun, browsers).\n */\n\n/** Uint8Array → base64 string. */\nexport function toBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!);\n }\n return btoa(binary);\n}\n\n/** base64 string → Uint8Array. Throws on invalid input. */\nexport function fromBase64(s: string): Uint8Array {\n const binary = atob(s);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * publish.ts — claimant-side encrypt + POST to the evidence daemon, and\n * manifest-hash verification for the recovery upload path.\n *\n * The daemon base URL is deployment-specific (Vite env on the app side) and is\n * NOT held here — `publishEvidence` takes `endpoint` as a parameter. App\n * consumers keep their own `EVIDENCE_DAEMON_URL` config (ADR-0011).\n *\n * Authority: ADR-0011 (daemon transport), EVIDENCE-FORMAT.md §2 (root hash),\n * milestone accord-ebel §1 (happy path step 7, recovery).\n */\nimport { claimantEncrypt } from \"./ecies.js\";\nimport { sha256 } from \"./crypto.js\";\nimport { toBase64 } from \"./base64.js\";\n\nfunction equalBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n return a.every((v, i) => v === b[i]);\n}\n\nexport interface PublishParams {\n /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */\n endpoint: string;\n subaccord: string;\n dispute: string;\n /** The single manifest buffer (same bytes that produced `evidence_hash`). */\n manifest: Uint8Array;\n /** Evidence operator's raw Ed25519 public key (32 bytes, decoded from address). */\n operatorPub: Uint8Array;\n}\n\n/** Encrypt the manifest to the operator and POST the wire bundle to `url`. */\nasync function postBundle(url: string, manifest: Uint8Array, operatorPub: Uint8Array): Promise<void> {\n const bundle = await claimantEncrypt(manifest, operatorPub);\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n ct: toBase64(bundle.ct),\n claimant_ephem_pub: toBase64(bundle.claimant_ephem_pub),\n wrapped: toBase64(bundle.wrapped),\n plaintext_hash: toBase64(bundle.plaintext_hash),\n }),\n });\n if (res.status !== 201) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`evidence publish failed: ${res.status} ${body}`);\n }\n}\n\n/**\n * Encrypt the manifest to the operator and POST it to the daemon.\n * Daemon ingest is idempotent on `(dispute, round, plaintext_hash)` — same\n * manifest → 201 no-op, first POST wins. Safe to retry without re-creating\n * the dispute.\n */\nexport async function publishEvidence(params: PublishParams): Promise<void> {\n const { endpoint, subaccord, dispute, manifest, operatorPub } = params;\n await postBundle(\n `${endpoint}/evidence/${subaccord}/${dispute}`,\n manifest,\n operatorPub,\n );\n}\n\nexport interface PublishSynodParams {\n /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */\n endpoint: string;\n /** The SynodCase PDA — the pre-dispute grouping key. */\n casePda: string;\n /** Roster slot of the joining party (0–6). */\n slot: number;\n /** The party's manifest buffer (same bytes that produced the join hash). */\n manifest: Uint8Array;\n /** Evidence operator's raw Ed25519 public key (32 bytes, decoded from address). */\n operatorPub: Uint8Array;\n}\n\n/**\n * Encrypt the party's manifest to the operator and POST it to the daemon's\n * synod pre-dispute grouping route (`POST /evidence/synod/{case}/{slot}`,\n * milestone accord-daq8). Unauthenticated by design: the on-chain\n * `SynodCase.evidence[slot]` hash committed at `join` IS the commit; pushes\n * are gated chain-side (case exists, slot live, dispute unbound). Same-bundle\n * re-pushes are 201-idempotent server-side; a different hash for a filled\n * slot is a 409.\n */\nexport async function publishSynodEvidence(\n params: PublishSynodParams,\n): Promise<void> {\n const { endpoint, casePda, slot, manifest, operatorPub } = params;\n await postBundle(\n `${endpoint}/evidence/synod/${casePda}/${slot}`,\n manifest,\n operatorPub,\n );\n}\n\n/**\n * Verify `sha256(manifest) == evidenceHash`. Throws on mismatch — the recovery\n * upload is rejected if the manifest doesn't match what was committed on-chain.\n */\nexport async function verifyManifestHash(\n manifest: Uint8Array,\n evidenceHash: Uint8Array,\n): Promise<void> {\n const hash = await sha256(manifest);\n if (!equalBytes(hash, evidenceHash)) {\n throw new Error(\n \"manifest hash mismatch — upload does not match on-chain evidence_hash\",\n );\n }\n}\n","/**\n * evidence/delivery-key.ts — juror Delivery Keys (ADR-0034, amends\n * ADR-0015 / ADR-0011).\n *\n * A Delivery Key is a browser-held X25519 keypair registered at the evidence\n * daemon under a wallet `signMessage` binding: the juror signs\n * `accord-delkey-v1\\nregistered_at:{ms}\\nenc_pub:{base58}` with its Solana\n * (Ed25519) key and PUTs it to `/jurors/{juror}/delivery-key`. Delivery then\n * targets the registered X25519 key — no registered key ⇒ no delivery (404).\n * There is no Ed→X fallback anywhere in delivery; Ed↔X dual-use survives only\n * on the claimant→operator ingest path.\n *\n * Custody: the raw 32-byte secret stays in origin-scoped browser storage; apps\n * wire the storage via the {@link DeliveryKeyStorage} port (IndexedDB,\n * localStorage, whatever) — the SDK owns only the codec + this protocol.\n * One active key per juror per daemon, last-writer-wins; rotation =\n * re-register (a higher `registered_at` wins) + re-pull — nothing stored is\n * keyed, delivery re-encrypts per request.\n *\n * Multi-origin self-heal: GET the registered key, compare with the local pub,\n * mismatch ⇒ re-register + re-pull ({@link ensureRegisteredDeliveryKey}).\n *\n * Authority: ADR-0034; apps/evidence-daemon/SPEC.md §Crypto model.\n */\nimport { ed25519 } from \"@noble/curves/ed25519\";\nimport { getBase58Decoder } from \"@solana/kit\";\n\nimport { fromBase64, toBase64 } from \"./base64.js\";\nimport { newX25519KeyPair } from \"./keys.js\";\n\nconst te = new TextEncoder();\n\n/** Prefix pinning the registration message to `accord-delkey-v1` (ADR-0034). */\nexport const DELKEY_PREFIX = \"accord-delkey-v1\";\n\n/** A juror-held X25519 Delivery Keypair. The secret never leaves origin storage. */\nexport interface DeliveryKeypair {\n /** 32-byte X25519 public key — what gets registered at the daemon. */\n publicKey: Uint8Array;\n /** 32-byte X25519 secret key. */\n secretKey: Uint8Array;\n}\n\n/** Fresh Delivery Keypair (noble X25519; ADR-0034 custody model). */\nexport function generateDeliveryKey(): DeliveryKeypair {\n const pair = newX25519KeyPair();\n return { publicKey: pair.publicKey, secretKey: pair.secret };\n}\n\n// ---------------------------------------------------------------------------\n// Registration message + signature verification (shared with the daemon)\n// ---------------------------------------------------------------------------\n\n/**\n * The wallet-signed registration message:\n * `accord-delkey-v1\\nregistered_at:{ms}\\nenc_pub:{base58}`.\n * `registered_at` is Unix ms; the daemon rejects `registered_at <= stored`\n * (kills replay-downgrade of an exfiltrated superseded key).\n */\nexport function buildDeliveryKeyMessage(\n encPub: Uint8Array,\n registeredAtMs: number,\n): Uint8Array {\n requireLen(encPub, 32, \"delivery enc_pub\");\n if (!Number.isSafeInteger(registeredAtMs) || registeredAtMs < 0) {\n throw new Error(`registered_at must be a non-negative integer (ms), got ${registeredAtMs}`);\n }\n const b58 = getBase58Decoder().decode(encPub);\n return te.encode(`${DELKEY_PREFIX}\\nregistered_at:${registeredAtMs}\\nenc_pub:${b58}`);\n}\n\n/**\n * Daemon-shared verification of a registration signature: Ed25519 over\n * {@link buildDeliveryKeyMessage} by the juror's Solana key. Returns false for\n * a bad signature OR malformed inputs (the daemon maps false → 400).\n */\nexport function verifyDeliveryKeyRegistration(\n jurorEd25519Pub: Uint8Array,\n encPub: Uint8Array,\n registeredAtMs: number,\n sig: Uint8Array,\n): boolean {\n try {\n const msg = buildDeliveryKeyMessage(encPub, registeredAtMs);\n return ed25519.verify(sig, msg, jurorEd25519Pub);\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Daemon HTTP: register / get / ensure-registered\n// ---------------------------------------------------------------------------\n\n/** Wallet `signMessage` seam (app wires its wallet adapter; ADR-0015 split). */\nexport type SignMessage = (message: Uint8Array) => Promise<Uint8Array>;\n\nexport interface RegisterDeliveryKeyParams {\n /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */\n endpoint: string;\n /** Juror's Solana address, base58. */\n juror: string;\n /** Delivery public key to register (32 bytes). */\n encPub: Uint8Array;\n /** Wallet signMessage — signs the registration message (Ed25519). */\n signMessage: SignMessage;\n}\n\n/** Registration result echoed by the daemon on 201. */\nexport interface RegisteredDeliveryKey {\n encPub: Uint8Array;\n registeredAt: number;\n}\n\n/**\n * Register (or rotate) the juror's Delivery Key:\n * `PUT /jurors/{juror}/delivery-key` {enc_pub, registered_at, sig} → 201.\n * Throws on 400 (bad sig/body) and 409 (stale `registered_at` — retry later).\n */\nexport async function registerDeliveryKey(\n params: RegisterDeliveryKeyParams,\n): Promise<RegisteredDeliveryKey> {\n const { endpoint, juror, encPub, signMessage } = params;\n const registeredAt = Date.now();\n const sig = await signMessage(buildDeliveryKeyMessage(encPub, registeredAt));\n const res = await fetch(`${endpoint}/jurors/${juror}/delivery-key`, {\n method: \"PUT\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n enc_pub: toBase64(encPub),\n registered_at: registeredAt,\n sig: toBase64(sig),\n }),\n });\n if (res.status !== 201) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`delivery-key registration failed: ${res.status} ${body}`);\n }\n return { encPub, registeredAt };\n}\n\n/**\n * The currently registered Delivery Key:\n * `GET /jurors/{juror}/delivery-key` → 200 {enc_pub, registered_at} | 404 → null.\n */\nexport async function getRegisteredDeliveryKey(params: {\n endpoint: string;\n juror: string;\n}): Promise<RegisteredDeliveryKey | null> {\n const res = await fetch(`${params.endpoint}/jurors/${params.juror}/delivery-key`);\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n const body = (await res.json()) as { enc_pub?: unknown; registered_at?: unknown };\n if (typeof body.enc_pub !== \"string\" || typeof body.registered_at !== \"number\") {\n throw new Error(\"delivery-key response malformed\");\n }\n return { encPub: fromBase64(body.enc_pub), registeredAt: body.registered_at };\n}\n\n/**\n * Multi-origin self-heal (ADR-0034): compare the daemon's registered key with\n * the local one; mismatch (or none registered — another origin rotated) ⇒\n * re-register with one `signMessage`. Returns `\"current\"` when already bound,\n * `\"re-registered\"` after a fresh PUT — then re-pull the delivery.\n */\nexport async function ensureRegisteredDeliveryKey(\n params: RegisterDeliveryKeyParams,\n): Promise<\"current\" | \"re-registered\"> {\n const current = await getRegisteredDeliveryKey(params);\n if (\n current !== null &&\n current.encPub.length === params.encPub.length &&\n current.encPub.every((b, i) => b === params.encPub[i])\n ) {\n return \"current\";\n }\n await registerDeliveryKey(params);\n return \"re-registered\";\n}\n\n// ---------------------------------------------------------------------------\n// Persistence codec + storage port (custody stays app-side, ADR-0034)\n// ---------------------------------------------------------------------------\n\n/** Serialized form: `publicKey(32) ‖ secretKey(32)`. */\nexport function encodeDeliveryKeypair(kp: DeliveryKeypair): Uint8Array {\n requireLen(kp.publicKey, 32, \"delivery public key\");\n requireLen(kp.secretKey, 32, \"delivery secret key\");\n const out = new Uint8Array(64);\n out.set(kp.publicKey, 0);\n out.set(kp.secretKey, 32);\n return out;\n}\n\n/** Inverse of {@link encodeDeliveryKeypair}; throws on a wrong length. */\nexport function decodeDeliveryKeypair(bytes: Uint8Array): DeliveryKeypair {\n requireLen(bytes, 64, \"serialized delivery keypair\");\n return { publicKey: bytes.slice(0, 32), secretKey: bytes.slice(32) };\n}\n\n/**\n * Origin-scoped storage port for the serialized keypair. Apps implement it\n * over IndexedDB / localStorage / whatever the origin offers — the SDK owns\n * only the codec, keeping DOM typing out of this module.\n */\nexport interface DeliveryKeyStorage {\n get(): Promise<Uint8Array | null>;\n set(serialized: Uint8Array): Promise<void>;\n}\n\n/**\n * Load the origin's Delivery Key, generating + persisting one on first use.\n * Generate-once semantics: every call on an empty store MUST return the same\n * keypair — re-generating would orphan the daemon registration.\n */\nexport async function loadOrCreateDeliveryKey(\n storage: DeliveryKeyStorage,\n): Promise<DeliveryKeypair> {\n const stored = await storage.get();\n if (stored !== null) return decodeDeliveryKeypair(stored);\n const kp = generateDeliveryKey();\n await storage.set(encodeDeliveryKeypair(kp));\n return kp;\n}\n\n// --- internal ---------------------------------------------------------------\n\nfunction requireLen(b: Uint8Array, n: number, what: string): void {\n if (b.length !== n) {\n throw new Error(`${what} must be ${n} bytes, got ${b.length}`);\n }\n}\n","/**\n * evidence/fetch.ts — framework-agnostic evidence-daemon fetches.\n *\n * GET {endpoint}/evidence/{dispute}/for/{juror} — juror delivery\n * (ADR-0034: decrypt each round with the registered Delivery Key secret)\n * GET {endpoint}/evidence/{subaccord}/{dispute}/{round} — public manifest\n *\n * Both are the pure fetch — React apps wrap them in hooks\n * (see apps/app and apps/canon `useManifest.ts`).\n */\nimport { fromBase64 } from \"./base64.js\";\nimport type { JurorBundle } from \"./ecies.js\";\n\n// ---------------------------------------------------------------------------\n// Juror delivery — GET /evidence/{dispute}/for/{juror}\n// ---------------------------------------------------------------------------\n\nexport interface FetchDeliveryParams {\n /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */\n endpoint: string;\n /** Dispute address, base58. */\n dispute: string;\n /** Juror address, base58 — delivery targets their registered Delivery Key. */\n juror: string;\n}\n\n/** One manifest entry's delivery status (v2 multifile derived index). */\nexport interface DeliveredFile {\n readonly path: string;\n readonly status: \"stored\" | \"pending\" | \"out_of_band\";\n}\n\n/**\n * One round's delivered package: a {@link JurorBundle} plus the v2 derived\n * per-entry index. `round` is the evidence round (or the synod party slot).\n */\nexport interface DeliveredRound extends JurorBundle {\n readonly round: number;\n readonly files: readonly DeliveredFile[];\n /** True iff every tracked entry is stored (always true for manifest-only). */\n readonly complete: boolean;\n}\n\n/** Type guard for one wire round: `{round, out, operator_ephem_pub, files?, complete?}`. */\nfunction isWireRound(v: unknown): v is {\n round: number;\n out: string;\n operator_ephem_pub: string;\n files?: DeliveredFile[];\n complete?: boolean;\n} {\n if (v === null || typeof v !== \"object\") return false;\n const r = v as Record<string, unknown>;\n return (\n typeof r[\"round\"] === \"number\" &&\n typeof r[\"out\"] === \"string\" &&\n typeof r[\"operator_ephem_pub\"] === \"string\" &&\n (r[\"files\"] === undefined || Array.isArray(r[\"files\"])) &&\n (r[\"complete\"] === undefined || typeof r[\"complete\"] === \"boolean\")\n );\n}\n\n/**\n * Pull the juror-bound delivery: one re-encrypted package per non-zero\n * evidence hash. Base64 wire fields are decoded to bytes so each round feeds\n * straight into `jurorDecryptDelivery`. `null` when nothing is deliverable\n * (404 — not drawn, no bundle, or no Delivery Key registered).\n */\nexport async function fetchDelivery(\n params: FetchDeliveryParams,\n): Promise<DeliveredRound[] | null> {\n const { endpoint, dispute, juror } = params;\n const res = await fetch(`${endpoint}/evidence/${dispute}/for/${juror}`);\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n const body = (await res.json()) as { rounds?: unknown };\n if (!Array.isArray(body.rounds) || !body.rounds.every(isWireRound)) {\n throw new Error(\"delivery response malformed\");\n }\n return body.rounds.map((r) => ({\n round: r.round,\n out: fromBase64(r.out),\n operator_ephem_pub: fromBase64(r.operator_ephem_pub),\n files: r.files ?? [],\n complete: r.complete ?? true,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Public manifest — GET /evidence/{subaccord}/{dispute}/{round}\n// ---------------------------------------------------------------------------\n\nexport interface FetchManifestParams {\n endpoint: string;\n subaccord: string;\n dispute: string;\n round: number;\n}\n\n/**\n * Fetch the decrypted manifest (a YAML string for the `accord-evidence/v1`\n * format, or a parsed JSON object if the plaintext was JSON). `null` when no\n * bundle is stored for the round (404).\n */\nexport async function fetchManifest(\n params: FetchManifestParams,\n): Promise<unknown> {\n const { endpoint, subaccord, dispute, round } = params;\n const res = await fetch(\n `${endpoint}/evidence/${subaccord}/${dispute}/${round}`,\n );\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n return res.json();\n}\n"],"mappings":"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","names":["randomBytes","randomBytes","te","sha256","equalBytes","ed25519","te","requireLen","ed25519"]}
1
+ {"version":3,"sources":["../../src/evidence/crypto.ts","../../src/evidence/keys.ts","../../src/evidence/ecies.ts","../../src/evidence/manifest.ts","../../src/evidence/options.ts","../../src/evidence/parse.ts","../../src/evidence/base64.ts","../../src/evidence/publish.ts","../../src/evidence/delivery-key.ts","../../src/evidence/fetch.ts"],"sourcesContent":["/**\n * evidence/crypto.ts — symmetric primitive spine for the Accord evidence\n * protocol (ADR-0006).\n *\n * Every primitive delegates to the audited `@noble` suite (`@noble/ciphers`,\n * `@noble/hashes`). Nothing here is hand-rolled: this module only fixes the two\n * protocol policies the claimant/operator/juror must share byte-for-byte —\n *\n * - AES-256-GCM wire blob = `nonce(12) ‖ ciphertext ‖ tag(16)`\n * - HKDF-SHA256 with an empty salt and a role-pinning `info` label\n *\n * — exactly as {@link ../methods/mst.ts} fixes a node layout over a library\n * hash. Functions are `async` to match the SPEC's stated crypto model and keep\n * callers (the daemon's injected ports, SDK consumers) await-shaped.\n *\n * Authority: apps/evidence-daemon/SPEC.md §Crypto model; ADR-0006.\n */\nimport { gcm } from \"@noble/ciphers/aes\";\nimport { hkdf } from \"@noble/hashes/hkdf\";\nimport { sha256 as nobleSha256 } from \"@noble/hashes/sha256\";\nimport { randomBytes } from \"@noble/hashes/utils\";\n\nconst AES_NONCE_BYTES = 12;\nconst AES_TAG_BYTES = 16;\n\n/** SHA-256 digest (32 bytes). */\nexport async function sha256(data: Uint8Array): Promise<Uint8Array> {\n return nobleSha256(data);\n}\n\n/**\n * HKDF-SHA256 with an empty salt (per SPEC §Crypto model) and a protocol `info`\n * label that pins the derived key to its role (ingest vs deliver).\n */\nexport async function hkdfSha256(\n ikm: Uint8Array,\n info: Uint8Array,\n length = 32,\n): Promise<Uint8Array> {\n return hkdf(nobleSha256, ikm, new Uint8Array(0), info, length);\n}\n\n/** AES-256-GCM encrypt → `nonce(12) ‖ ciphertext ‖ tag(16)` (SPEC wire format). */\nexport async function aesGcmEncrypt(\n key: Uint8Array,\n plaintext: Uint8Array,\n): Promise<Uint8Array> {\n requireKey(key);\n const nonce = randomBytes(AES_NONCE_BYTES);\n const ct = gcm(key, nonce).encrypt(plaintext); // ciphertext ‖ tag(16)\n const out = new Uint8Array(AES_NONCE_BYTES + ct.length);\n out.set(nonce, 0);\n out.set(ct, AES_NONCE_BYTES);\n return out;\n}\n\n/** AES-256-GCM decrypt of a `nonce(12) ‖ ciphertext ‖ tag(16)` blob. Throws on auth failure. */\nexport async function aesGcmDecrypt(\n key: Uint8Array,\n blob: Uint8Array,\n): Promise<Uint8Array> {\n requireKey(key);\n if (blob.length < AES_NONCE_BYTES + AES_TAG_BYTES) {\n throw new Error(\"AES-GCM blob too short (missing nonce/tag)\");\n }\n const nonce = blob.subarray(0, AES_NONCE_BYTES);\n const ct = blob.subarray(AES_NONCE_BYTES);\n return gcm(key, nonce).decrypt(ct);\n}\n\nfunction requireKey(key: Uint8Array): void {\n if (key.length !== 32) {\n throw new Error(`AES-256 key must be 32 bytes, got ${key.length}`);\n }\n}\n","/**\n * evidence/keys.ts — Ed25519/X25519 key material + the operator keyring\n * contract for the evidence protocol (ADR-0006 / ADR-0011; delivery amended\n * by ADR-0034).\n *\n * Ingest (claimant → operator) dual-uses the on-chain `evidence_operator`\n * Ed25519 key: signing on-chain (Ed25519) + encryption off-chain via its\n * Montgomery (X25519) form — a libsodium-style\n * `crypto_sign_ed25519_{pk,sk}_to_curve25519`. Delivery no longer dual-uses\n * the juror's Solana key: it targets the juror's registered X25519 Delivery\n * Key (ADR-0034, `delivery-key.ts`), so these Ed→X helpers survive ONLY on\n * the ingest path.\n *\n * Backed by `@noble/curves`; the wrappers only add length + zero-key\n * validation at the public seam.\n */\nimport {\n ed25519,\n edwardsToMontgomeryPriv,\n edwardsToMontgomeryPub,\n x25519,\n} from \"@noble/curves/ed25519\";\n\n/** Ed25519 keypair material held by the operator. The secret never leaves the keyring. */\nexport interface Ed25519Keypair {\n /** 32-byte Ed25519 public key (matches the on-chain `evidence_operator` field). */\n publicKey: Uint8Array;\n /** 32-byte Ed25519 secret seed. */\n secretKey: Uint8Array;\n}\n\n/**\n * Per-Subaccord operator keyring contract. Resolves the Ed25519 secret whose\n * pubkey matches a Subaccord's on-chain `evidence_operator`, or null when this\n * operator does not operate that Subaccord (the caller maps null → 404). The\n * on-chain field is the binding: no key↔subaccord mapping is held out-of-band.\n *\n * The daemon supplies the env-backed implementation; a file/KMS source can\n * satisfy this without touching callers (ADR-0011).\n */\nexport interface Keyring {\n forOperator(operatorPubkey: Uint8Array): Promise<Ed25519Keypair | null>;\n}\n\n// --- Ed25519 <-> X25519 (libsodium-style; backed by @noble/curves) ---------\n\n/** Convert an Ed25519 public key (32 bytes) to its X25519 (Montgomery) form. */\nexport function ed25519ToX25519PublicKey(ed25519Pubkey: Uint8Array): Uint8Array {\n requireLen(ed25519Pubkey, 32, \"Ed25519 public key\");\n // Pubkey::default() converts to Montgomery u=1 (order-2) — the X25519 shared\n // secret would be all zeros and noble throws a cryptic error downstream.\n // Canon's create_list CPI still sets evidence_operator = Pubkey::default().\n if (ed25519Pubkey.every((b) => b === 0)) {\n throw new Error(\n \"Ed25519 public key is all zeros (Solana Pubkey::default — unset evidence_operator on-chain); refusing X25519 conversion\",\n );\n }\n return edwardsToMontgomeryPub(ed25519Pubkey);\n}\n\n/** Convert an Ed25519 secret seed (32 bytes) to its X25519 (Montgomery) secret. */\nexport function ed25519SecretToX25519(ed25519SecretSeed: Uint8Array): Uint8Array {\n requireLen(ed25519SecretSeed, 32, \"Ed25519 secret seed\");\n return edwardsToMontgomeryPriv(ed25519SecretSeed);\n}\n\n/** X25519 ECDH: shared secret from a Montgomery secret + a Montgomery public key. */\nexport function x25519SharedSecret(\n mySecret: Uint8Array,\n theirPublic: Uint8Array,\n): Uint8Array {\n return x25519.getSharedSecret(mySecret, theirPublic);\n}\n\n/** Fresh ephemeral X25519 keypair for one ECIES operation. */\nexport function newX25519KeyPair(): { secret: Uint8Array; publicKey: Uint8Array } {\n const secret = x25519.utils.randomPrivateKey();\n return { secret, publicKey: x25519.getPublicKey(secret) };\n}\n\n/** Derive the Ed25519 public key from a 32-byte secret seed (keyring indexing). */\nexport function ed25519PublicKeyFromSeed(seed: Uint8Array): Uint8Array {\n requireLen(seed, 32, \"Ed25519 secret seed\");\n return ed25519.getPublicKey(seed);\n}\n\nfunction requireLen(b: Uint8Array, n: number, what: string): void {\n if (b.length !== n) {\n throw new Error(`${what} must be ${n} bytes, got ${b.length}`);\n }\n}\n","/**\n * evidence/ecies.ts — the Accord evidence encryption protocol (ADR-0006,\n * delivery amended by ADR-0034).\n *\n * One ECIES-style envelope, two roles:\n *\n * Ingest (claimant → operator): the claimant encrypts plaintext to the\n * operator's Ed25519→X25519 key and posts the bundle; the operator decrypts.\n *\n * Deliver (operator → drawn juror): the operator re-encrypts plaintext to\n * the juror's registered Delivery Key (a raw X25519 key, ADR-0034); only\n * that juror's Delivery Key secret decrypts. No Ed→X dual-use here.\n *\n * This is the single byte-exact implementation all three parties import — the\n * claimant and juror run it off-daemon (they are SDK consumers), so the protocol\n * cannot drift between participants. Composition only: every primitive\n * (ECDH, HKDF, AES-GCM, Montgomery conversion) comes from {@link ./crypto} /\n * {@link ./keys}; the only thing owned here is the construction order and the\n * HKDF `info` role labels.\n *\n * Plaintext exists only ephemerally between decrypt and re-encrypt on the\n * operator side; nothing persisted is ever plaintext (SPEC §Encrypted-at-rest).\n *\n * Authority: apps/evidence-daemon/SPEC.md §Crypto model; ADR-0006, ADR-0034.\n */\nimport { randomBytes } from \"@noble/hashes/utils\";\nimport { aesGcmDecrypt, aesGcmEncrypt, hkdfSha256, sha256 } from \"./crypto.js\";\nimport {\n ed25519SecretToX25519,\n ed25519ToX25519PublicKey,\n newX25519KeyPair,\n x25519SharedSecret,\n} from \"./keys.js\";\n\n/** HKDF `info` labels pin the derivation to its protocol role. */\nexport const INGEST_INFO = \"accord-ingest-v1\";\nexport const DELIVER_INFO = \"accord-deliver-v1\";\n\nconst te = new TextEncoder();\n\n/** Stored ciphertext bundle (ciphertext only — no plaintext field). */\nexport interface IngestBundle {\n /** AES-GCM(dek, plaintext) — nonce(12) prepended. */\n ct: Uint8Array;\n /** Claimant's ephemeral X25519 public key (32 bytes). */\n claimant_ephem_pub: Uint8Array;\n /** AES-GCM(k_in, dek) — nonce(12) prepended. */\n wrapped: Uint8Array;\n /** sha256(plaintext); matches the dispute's on-chain `evidence_hash`. */\n plaintext_hash: Uint8Array;\n}\n/** A bundle re-encrypted to a single drawn juror's Delivery Key (ADR-0034). */\nexport interface JurorBundle {\n /** AES-GCM(k_out, plaintext) — nonce(12) prepended. */\n out: Uint8Array;\n /** Operator's ephemeral X25519 public key for this delivery (32 bytes). */\n operator_ephem_pub: Uint8Array;\n}\n/**\n * Claimant-side ingest encryption. Produces the bundle posted to the operator.\n * Claimants run this off-daemon; the operator never calls it in production (it\n * is exposed so claimant SDK clients share one implementation, and for\n * round-trips / test vectors).\n */\nexport async function claimantEncrypt(\n plaintext: Uint8Array,\n operatorEd25519Pub: Uint8Array,\n): Promise<IngestBundle> {\n const dek = randomBytes(32);\n const ct = await aesGcmEncrypt(dek, plaintext);\n\n const ephem = newX25519KeyPair();\n const opX25519Pub = ed25519ToX25519PublicKey(operatorEd25519Pub);\n const shared = x25519SharedSecret(ephem.secret, opX25519Pub);\n const kIn = await hkdfSha256(shared, te.encode(INGEST_INFO));\n const wrapped = await aesGcmEncrypt(kIn, dek);\n\n // dek wiped implicitly (local ref dropped); plaintext_hash is metadata, not secret.\n return {\n ct,\n claimant_ephem_pub: ephem.publicKey,\n wrapped,\n plaintext_hash: await sha256(plaintext),\n };\n}\n\n/**\n * Operator-side decrypt of a stored bundle → plaintext (in-memory only).\n * Callers MUST run {@link verifyIntegrity} next.\n */\nexport async function operatorDecrypt(\n bundle: IngestBundle,\n operatorEd25519SecretSeed: Uint8Array,\n): Promise<Uint8Array> {\n const opX25519Sk = ed25519SecretToX25519(operatorEd25519SecretSeed);\n const shared = x25519SharedSecret(opX25519Sk, bundle.claimant_ephem_pub);\n const kIn = await hkdfSha256(shared, te.encode(INGEST_INFO));\n const dek = await aesGcmDecrypt(kIn, bundle.wrapped); // throws on auth failure\n return aesGcmDecrypt(dek, bundle.ct);\n}\n\n/** Operator re-encrypts plaintext to the juror's registered Delivery Key (ADR-0034). */\nexport async function deliverToDeliveryKey(\n plaintext: Uint8Array,\n deliveryEncPub: Uint8Array,\n): Promise<JurorBundle> {\n const ephem = newX25519KeyPair();\n const shared = x25519SharedSecret(ephem.secret, deliveryEncPub);\n const kOut = await hkdfSha256(shared, te.encode(DELIVER_INFO));\n const out = await aesGcmEncrypt(kOut, plaintext);\n return { out, operator_ephem_pub: ephem.publicKey };\n}\n\n/** Juror-side decrypt of a delivered bundle with the Delivery Key secret (ADR-0034). */\nexport async function jurorDecryptDelivery(\n delivered: JurorBundle,\n deliverySecret: Uint8Array,\n): Promise<Uint8Array> {\n const shared = x25519SharedSecret(deliverySecret, delivered.operator_ephem_pub);\n const kOut = await hkdfSha256(shared, te.encode(DELIVER_INFO));\n return aesGcmDecrypt(kOut, delivered.out);\n}\n\n/**\n * Mandatory integrity gate (SPEC §3, §Failure modes): `sha256(plaintext)` MUST\n * equal the dispute's on-chain `evidence_hash`. Both operands are public hashes,\n * so a plain compare suffices (no constant-time requirement). Throws on mismatch\n * — at ingest this rejects a bad upload, at delivery it refuses + alerts.\n */\nexport async function verifyIntegrity(\n plaintext: Uint8Array,\n evidenceHash: Uint8Array,\n): Promise<void> {\n const h = await sha256(plaintext);\n if (h.length !== evidenceHash.length) {\n throw new Error(\"integrity gate failed: sha256(plaintext) != evidence_hash\");\n }\n for (let i = 0; i < h.length; i++) {\n if (h[i] !== evidenceHash[i]) {\n throw new Error(\"integrity gate failed: sha256(plaintext) != evidence_hash\");\n }\n }\n}\n","/**\n * manifest.ts — serialize the `accord-evidence/v1` manifest into a single\n * deterministic Uint8Array buffer. That one buffer feeds the YAML preview,\n * `sha256`→`evidence_hash`, and `claimantEncrypt`→POST. Never re-serialize.\n *\n * Authority: EVIDENCE-FORMAT.md §2 (no canonicalization needed — manifest\n * delivered verbatim), milestone accord-ebel §3 (single-buffer invariant).\n */\nimport type { Address } from \"@solana/kit\";\n\n/** All-zero `[u8;32]` sentinel — juror skips leaf verification (root gate still applies). */\nexport const SHA256_ZERO = new Uint8Array(32);\n\nexport interface ManifestEntryInput {\n /** URL or relative POSIX path to the evidence resource. */\n path: string;\n /** Leaf sha256 (32 bytes). Defaults to {@link SHA256_ZERO} when unknown. */\n sha256?: Uint8Array;\n}\n\nexport interface ManifestInput {\n /** Per-dispute 32-byte random salt (app-generated). */\n salt: Uint8Array;\n title: string;\n /**\n * Optional markdown claim body (the challenger's argument). Serialized as a\n * single JSON-escaped line so the manifest stays single-line-per-field and\n * the targeted parser needs no block-scalar handling. Omitted entirely when\n * absent/empty → byte-identical to a pre-`description` manifest (sha256\n * stable). Rendering is display-only; committed bytes are never altered.\n */\n description?: string;\n /** Ordered option labels; `Dispute.options[i] = sha256(salt ‖ utf8(label_i))`. */\n labels: string[];\n entries: ManifestEntryInput[];\n}\n\nexport interface ManifestCtx {\n dispute: Address;\n subaccord: Address;\n filer: Address;\n /** ISO-8601 UTC timestamp string. */\n filedAt: string;\n}\n\nfunction hex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/** Minimal YAML double-quote escaping for a string value. */\nfunction yamlQuote(s: string): string {\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * Serialize manifest input + ctx into a single `Uint8Array` (UTF-8 YAML).\n * Hand-serialized — the manifest is a flat object with two list fields; no YAML\n * library needed (EVIDENCE-FORMAT.md §2: no canonicalization, delivered verbatim).\n */\nexport function buildManifest(\n input: ManifestInput,\n ctx: ManifestCtx,\n): Uint8Array {\n const lines: string[] = [\n \"schema: accord-evidence/v1\",\n `dispute: ${ctx.dispute}`,\n `subaccord: ${ctx.subaccord}`,\n `filer: ${ctx.filer}`,\n `filed_at: ${ctx.filedAt}`,\n \"language: en\",\n `title: ${yamlQuote(input.title)}`,\n // description is optional; omitted when absent/empty → backward-compatible\n // bytes (sha256 stable). JSON-escaped so multi-line markdown stays on one\n // logical line (no YAML block-scalar parsing needed).\n ...(input.description\n ? [`description: ${JSON.stringify(input.description)}`]\n : []),\n \"\",\n `option_salt: ${hex(input.salt)}`,\n \"options:\",\n ...input.labels.map(\n (label, index) => ` - { index: ${index}, label: ${yamlQuote(label)} }`,\n ),\n \"\",\n \"entries:\",\n ...input.entries.map(\n (e) =>\n ` - { path: ${yamlQuote(e.path)}, sha256: \"${hex(e.sha256 ?? SHA256_ZERO)}\" }`,\n ),\n ];\n return new TextEncoder().encode(lines.join(\"\\n\") + \"\\n\");\n}\n","/**\n * evidence/options.ts — option-salt derivation + self-verify.\n *\n * `Dispute.options[i] = sha256(option_salt ‖ utf8(label_i))` (EVIDENCE-FORMAT.md §4).\n * The salt is app-generated, per-dispute, and lives only in the manifest.\n * `verifyOptionHashes` runs pre-submit — nothing on-chain enforces this today\n * (ADR-0017 open; D2 trustlessly closed when it lands).\n */\nimport { sha256 } from \"./crypto.js\";\n\n/** Generate a 32-byte random salt via the Web Crypto API. */\nexport function generateSalt(): Uint8Array {\n return crypto.getRandomValues(new Uint8Array(32));\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.length + b.length);\n out.set(a);\n out.set(b, a.length);\n return out;\n}\n\nconst te = new TextEncoder();\n\n/**\n * Derive `Dispute.options[i] = sha256(option_salt ‖ utf8(label_i))` for each label.\n * Returns 32-byte digests in label order. Throws if salt is not 32 bytes — a\n * wrong-length salt produces silently-wrong hashes (correctness footgun).\n */\nexport async function deriveOptionHashes(\n salt: Uint8Array,\n labels: string[],\n): Promise<Uint8Array[]> {\n if (salt.length !== 32) {\n throw new Error(`InvalidOptionSalt: expected 32 bytes, got ${salt.length}`);\n }\n return Promise.all(\n labels.map((label) => sha256(concat(salt, te.encode(label)))),\n );\n}\n\nfunction equalBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n return a.every((v, i) => v === b[i]);\n}\n\n/**\n * Verify that `hashes[i] == sha256(salt ‖ utf8(labels[i]))` for all i.\n * Throws on mismatch — fails closed (D2 self-verify, app-side).\n */\nexport async function verifyOptionHashes(\n salt: Uint8Array,\n labels: string[],\n hashes: Uint8Array[],\n): Promise<void> {\n if (labels.length !== hashes.length) {\n throw new Error(\n `OptionHashMismatch: ${labels.length} labels vs ${hashes.length} hashes`,\n );\n }\n const derived = await deriveOptionHashes(salt, labels);\n for (let i = 0; i < derived.length; i++) {\n if (!equalBytes(derived[i]!, hashes[i]!)) {\n throw new Error(`option-hash mismatch at index ${i}`);\n }\n }\n}\n","/**\n * parse.ts — parse the `accord-evidence/v1` manifest returned by the evidence\n * daemon (`useManifest`). Shared by `EvidenceManifest` (full render) and any\n * feature that needs a slice of the manifest (e.g. option labels for voting).\n *\n * The manifest is YAML (produced by `buildManifest`); the daemon may return it\n * as a raw UTF-8 string or as a parsed JSON object. This targeted parser needs\n * no YAML dependency.\n */\n\nexport interface ParsedManifest {\n /** Top-level `schema:` value; \"\" when absent (pre-schema bytes). The daemon\n * dispatches multifile on schema + entries (milestone accord-5d0r). */\n schema: string;\n title: string;\n /** Markdown claim body; \"\" when absent (pre-`description` manifests). */\n description: string;\n filedAt: string;\n filer: string;\n subaccord: string;\n dispute: string;\n optionSalt: string;\n options: { index: number; label: string }[];\n entries: { path: string; sha256: string }[];\n}\n/** Flow-map scalar: `\"quoted\"` wins; else the run up to `,`/`}`, trimmed. */\nfunction scalar(item: string, key: string): string {\n const quoted = item.match(new RegExp(`${key}:\\\\s*\"([^\"]*)\"`))?.[1];\n if (quoted !== undefined) return quoted;\n return item.match(new RegExp(`${key}:\\\\s*([^,}]+)`))?.[1]?.trim() ?? \"\";\n}\n\n\n/** Parse the `accord-evidence/v1` YAML format (produced by `buildManifest`). */\nexport function parseManifest(text: string): ParsedManifest {\n const lines = text.split(\"\\n\");\n const getField = (key: string): string | undefined =>\n lines\n .find((l) => l.startsWith(`${key}: `))\n ?.slice(`${key}: `.length)\n .replace(/^\"(.*)\"$/, \"$1\");\n\n const options: { index: number; label: string }[] = [];\n const entries: { path: string; sha256: string }[] = [];\n let section: \"options\" | \"entries\" | null = null;\n\n for (const line of lines) {\n if (line === \"options:\") {\n section = \"options\";\n continue;\n }\n if (line === \"entries:\") {\n section = \"entries\";\n continue;\n }\n if (!line.startsWith(\" - {\")) continue;\n\n const item = line.slice(\" - \".length);\n if (section === \"options\") {\n const index = Number(item.match(/index:\\s*(\\d+)/)?.[1] ?? -1);\n const label = item.match(/label:\\s*\"([^\"]*)\"/)?.[1] ?? \"\";\n options.push({ index, label });\n } else if (section === \"entries\") {\n // Quoted (buildManifest emits yamlQuote) or unquoted scalar (riprap\n // wizard) — both valid up to the row's `,`/`}` delimiter.\n const path = scalar(item, \"path\");\n const sha256 = scalar(item, \"sha256\");\n entries.push({ path, sha256 });\n }\n }\n\n // description is JSON-escaped on a single line (see buildManifest); getField\n // strips the outer quotes, so re-quote + JSON.parse to unescape markdown.\n // Total function: malformed bytes fall back to the raw string — callers at\n // trust boundaries reject the manifest, the parser never throws.\n const descriptionRaw = getField(\"description\");\n let description = descriptionRaw ?? \"\";\n if (descriptionRaw) {\n try {\n description = JSON.parse(`\"${descriptionRaw}\"`) as string;\n } catch {\n description = descriptionRaw;\n }\n }\n\n return {\n schema: getField(\"schema\") ?? \"\",\n title: getField(\"title\") ?? \"Untitled dispute\",\n description,\n filedAt: getField(\"filed_at\") ?? \"—\",\n filer: getField(\"filer\") ?? \"—\",\n subaccord: getField(\"subaccord\") ?? \"—\",\n dispute: getField(\"dispute\") ?? \"—\",\n optionSalt: getField(\"option_salt\") ?? \"—\",\n options,\n entries,\n };\n}\n\n/**\n * Ordered option labels from a manifest fetched via `useManifest` (a YAML\n * string for the `accord-evidence/v1` format, or a parsed JSON object).\n *\n * Returns labels in index order, contiguously from 0 up to the first missing\n * or blank entry. Callers fall back to the on-chain option hash for any index\n * not covered. Returns `[]` when the manifest is absent (no bundle stored).\n */\nexport function optionLabels(manifest: unknown): string[] {\n if (manifest == null) return [];\n const parsed =\n typeof manifest === \"string\"\n ? parseManifest(manifest)\n : parseManifest(JSON.stringify(manifest));\n\n const byIndex = new Map<number, string>();\n for (const o of parsed.options) {\n if (o.index >= 0) byIndex.set(o.index, o.label);\n }\n\n const labels: string[] = [];\n for (let i = 0; i < parsed.options.length; i++) {\n const label = byIndex.get(i);\n if (typeof label === \"string\" && label.trim()) {\n labels.push(label);\n } else {\n break;\n }\n }\n return labels;\n}\n","/**\n * evidence/base64.ts — base64 codec for the daemon's JSON wire fields\n * (internal to the evidence module; not exported from the barrel).\n *\n * Standard alphabet, no padding concerns for this protocol's fixed-size\n * fields. Runs on the global `btoa`/`atob` (Node ≥16, Bun, browsers).\n */\n\n/** Uint8Array → base64 string. */\nexport function toBase64(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!);\n }\n return btoa(binary);\n}\n\n/** base64 string → Uint8Array. Throws on invalid input. */\nexport function fromBase64(s: string): Uint8Array {\n const binary = atob(s);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","/**\n * publish.ts — claimant-side encrypt + POST to the evidence daemon, and\n * manifest-hash verification for the recovery upload path.\n *\n * The daemon base URL is deployment-specific (Vite env on the app side) and is\n * NOT held here — `publishEvidence` takes `endpoint` as a parameter. App\n * consumers keep their own daemon-URL config (`useEvidenceDaemonUrl()`, ADR-0011).\n *\n * Authority: ADR-0011 (daemon transport), EVIDENCE-FORMAT.md §2 (root hash),\n * milestone accord-ebel §1 (happy path step 7, recovery).\n */\nimport { claimantEncrypt } from \"./ecies.js\";\nimport { sha256 } from \"./crypto.js\";\nimport { toBase64 } from \"./base64.js\";\n\nfunction equalBytes(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n return a.every((v, i) => v === b[i]);\n}\n\nexport interface PublishParams {\n /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */\n endpoint: string;\n subaccord: string;\n dispute: string;\n /** The single manifest buffer (same bytes that produced `evidence_hash`). */\n manifest: Uint8Array;\n /** Evidence operator's raw Ed25519 public key (32 bytes, decoded from address). */\n operatorPub: Uint8Array;\n}\n\n/** Encrypt the manifest to the operator and POST the wire bundle to `url`. */\nasync function postBundle(url: string, manifest: Uint8Array, operatorPub: Uint8Array): Promise<void> {\n const bundle = await claimantEncrypt(manifest, operatorPub);\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n ct: toBase64(bundle.ct),\n claimant_ephem_pub: toBase64(bundle.claimant_ephem_pub),\n wrapped: toBase64(bundle.wrapped),\n plaintext_hash: toBase64(bundle.plaintext_hash),\n }),\n });\n if (res.status !== 201) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`evidence publish failed: ${res.status} ${body}`);\n }\n}\n\n/**\n * Encrypt the manifest to the operator and POST it to the daemon.\n * Daemon ingest is idempotent on `(dispute, round, plaintext_hash)` — same\n * manifest → 201 no-op, first POST wins. Safe to retry without re-creating\n * the dispute.\n */\nexport async function publishEvidence(params: PublishParams): Promise<void> {\n const { endpoint, subaccord, dispute, manifest, operatorPub } = params;\n await postBundle(\n `${endpoint}/evidence/${subaccord}/${dispute}`,\n manifest,\n operatorPub,\n );\n}\n\nexport interface PublishSynodParams {\n /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */\n endpoint: string;\n /** The SynodCase PDA — the pre-dispute grouping key. */\n casePda: string;\n /** Roster slot of the joining party (0–6). */\n slot: number;\n /** The party's manifest buffer (same bytes that produced the join hash). */\n manifest: Uint8Array;\n /** Evidence operator's raw Ed25519 public key (32 bytes, decoded from address). */\n operatorPub: Uint8Array;\n}\n\n/**\n * Encrypt the party's manifest to the operator and POST it to the daemon's\n * synod pre-dispute grouping route (`POST /evidence/synod/{case}/{slot}`,\n * milestone accord-daq8). Unauthenticated by design: the on-chain\n * `SynodCase.evidence[slot]` hash committed at `join` IS the commit; pushes\n * are gated chain-side (case exists, slot live, dispute unbound). Same-bundle\n * re-pushes are 201-idempotent server-side; a different hash for a filled\n * slot is a 409.\n */\nexport async function publishSynodEvidence(\n params: PublishSynodParams,\n): Promise<void> {\n const { endpoint, casePda, slot, manifest, operatorPub } = params;\n await postBundle(\n `${endpoint}/evidence/synod/${casePda}/${slot}`,\n manifest,\n operatorPub,\n );\n}\n\n/**\n * Verify `sha256(manifest) == evidenceHash`. Throws on mismatch — the recovery\n * upload is rejected if the manifest doesn't match what was committed on-chain.\n */\nexport async function verifyManifestHash(\n manifest: Uint8Array,\n evidenceHash: Uint8Array,\n): Promise<void> {\n const hash = await sha256(manifest);\n if (!equalBytes(hash, evidenceHash)) {\n throw new Error(\n \"manifest hash mismatch — upload does not match on-chain evidence_hash\",\n );\n }\n}\n","/**\n * evidence/delivery-key.ts — juror Delivery Keys (ADR-0034, amends\n * ADR-0015 / ADR-0011).\n *\n * A Delivery Key is a browser-held X25519 keypair registered at the evidence\n * daemon under a wallet `signMessage` binding: the juror signs\n * `accord-delkey-v1\\nregistered_at:{ms}\\nenc_pub:{base58}` with its Solana\n * (Ed25519) key and PUTs it to `/jurors/{juror}/delivery-key`. Delivery then\n * targets the registered X25519 key — no registered key ⇒ no delivery (404).\n * There is no Ed→X fallback anywhere in delivery; Ed↔X dual-use survives only\n * on the claimant→operator ingest path.\n *\n * Custody: the raw 32-byte secret stays in origin-scoped browser storage; apps\n * wire the storage via the {@link DeliveryKeyStorage} port (IndexedDB,\n * localStorage, whatever) — the SDK owns only the codec + this protocol.\n * One active key per juror per daemon, last-writer-wins; rotation =\n * re-register (a higher `registered_at` wins) + re-pull — nothing stored is\n * keyed, delivery re-encrypts per request.\n *\n * Multi-origin self-heal: GET the registered key, compare with the local pub,\n * mismatch ⇒ re-register + re-pull ({@link ensureRegisteredDeliveryKey}).\n *\n * Authority: ADR-0034; apps/evidence-daemon/SPEC.md §Crypto model.\n */\nimport { ed25519 } from \"@noble/curves/ed25519\";\nimport { getBase58Decoder } from \"@solana/kit\";\n\nimport { fromBase64, toBase64 } from \"./base64.js\";\nimport { newX25519KeyPair } from \"./keys.js\";\n\nconst te = new TextEncoder();\n\n/** Prefix pinning the registration message to `accord-delkey-v1` (ADR-0034). */\nexport const DELKEY_PREFIX = \"accord-delkey-v1\";\n\n/** A juror-held X25519 Delivery Keypair. The secret never leaves origin storage. */\nexport interface DeliveryKeypair {\n /** 32-byte X25519 public key — what gets registered at the daemon. */\n publicKey: Uint8Array;\n /** 32-byte X25519 secret key. */\n secretKey: Uint8Array;\n}\n\n/** Fresh Delivery Keypair (noble X25519; ADR-0034 custody model). */\nexport function generateDeliveryKey(): DeliveryKeypair {\n const pair = newX25519KeyPair();\n return { publicKey: pair.publicKey, secretKey: pair.secret };\n}\n\n// ---------------------------------------------------------------------------\n// Registration message + signature verification (shared with the daemon)\n// ---------------------------------------------------------------------------\n\n/**\n * The wallet-signed registration message:\n * `accord-delkey-v1\\nregistered_at:{ms}\\nenc_pub:{base58}`.\n * `registered_at` is Unix ms; the daemon rejects `registered_at <= stored`\n * (kills replay-downgrade of an exfiltrated superseded key).\n */\nexport function buildDeliveryKeyMessage(\n encPub: Uint8Array,\n registeredAtMs: number,\n): Uint8Array {\n requireLen(encPub, 32, \"delivery enc_pub\");\n if (!Number.isSafeInteger(registeredAtMs) || registeredAtMs < 0) {\n throw new Error(`registered_at must be a non-negative integer (ms), got ${registeredAtMs}`);\n }\n const b58 = getBase58Decoder().decode(encPub);\n return te.encode(`${DELKEY_PREFIX}\\nregistered_at:${registeredAtMs}\\nenc_pub:${b58}`);\n}\n\n/**\n * Daemon-shared verification of a registration signature: Ed25519 over\n * {@link buildDeliveryKeyMessage} by the juror's Solana key. Returns false for\n * a bad signature OR malformed inputs (the daemon maps false → 400).\n */\nexport function verifyDeliveryKeyRegistration(\n jurorEd25519Pub: Uint8Array,\n encPub: Uint8Array,\n registeredAtMs: number,\n sig: Uint8Array,\n): boolean {\n try {\n const msg = buildDeliveryKeyMessage(encPub, registeredAtMs);\n return ed25519.verify(sig, msg, jurorEd25519Pub);\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Daemon HTTP: register / get / ensure-registered\n// ---------------------------------------------------------------------------\n\n/** Wallet `signMessage` seam (app wires its wallet adapter; ADR-0015 split). */\nexport type SignMessage = (message: Uint8Array) => Promise<Uint8Array>;\n\nexport interface RegisterDeliveryKeyParams {\n /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */\n endpoint: string;\n /** Juror's Solana address, base58. */\n juror: string;\n /** Delivery public key to register (32 bytes). */\n encPub: Uint8Array;\n /** Wallet signMessage — signs the registration message (Ed25519). */\n signMessage: SignMessage;\n}\n\n/** Registration result echoed by the daemon on 201. */\nexport interface RegisteredDeliveryKey {\n encPub: Uint8Array;\n registeredAt: number;\n}\n\n/**\n * Register (or rotate) the juror's Delivery Key:\n * `PUT /jurors/{juror}/delivery-key` {enc_pub, registered_at, sig} → 201.\n * Throws on 400 (bad sig/body) and 409 (stale `registered_at` — retry later).\n */\nexport async function registerDeliveryKey(\n params: RegisterDeliveryKeyParams,\n): Promise<RegisteredDeliveryKey> {\n const { endpoint, juror, encPub, signMessage } = params;\n const registeredAt = Date.now();\n const sig = await signMessage(buildDeliveryKeyMessage(encPub, registeredAt));\n const res = await fetch(`${endpoint}/jurors/${juror}/delivery-key`, {\n method: \"PUT\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n enc_pub: toBase64(encPub),\n registered_at: registeredAt,\n sig: toBase64(sig),\n }),\n });\n if (res.status !== 201) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`delivery-key registration failed: ${res.status} ${body}`);\n }\n return { encPub, registeredAt };\n}\n\n/**\n * The currently registered Delivery Key:\n * `GET /jurors/{juror}/delivery-key` → 200 {enc_pub, registered_at} | 404 → null.\n */\nexport async function getRegisteredDeliveryKey(params: {\n endpoint: string;\n juror: string;\n}): Promise<RegisteredDeliveryKey | null> {\n const res = await fetch(`${params.endpoint}/jurors/${params.juror}/delivery-key`);\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n const body = (await res.json()) as { enc_pub?: unknown; registered_at?: unknown };\n if (typeof body.enc_pub !== \"string\" || typeof body.registered_at !== \"number\") {\n throw new Error(\"delivery-key response malformed\");\n }\n return { encPub: fromBase64(body.enc_pub), registeredAt: body.registered_at };\n}\n\n/**\n * Multi-origin self-heal (ADR-0034): compare the daemon's registered key with\n * the local one; mismatch (or none registered — another origin rotated) ⇒\n * re-register with one `signMessage`. Returns `\"current\"` when already bound,\n * `\"re-registered\"` after a fresh PUT — then re-pull the delivery.\n */\nexport async function ensureRegisteredDeliveryKey(\n params: RegisterDeliveryKeyParams,\n): Promise<\"current\" | \"re-registered\"> {\n const current = await getRegisteredDeliveryKey(params);\n if (\n current !== null &&\n current.encPub.length === params.encPub.length &&\n current.encPub.every((b, i) => b === params.encPub[i])\n ) {\n return \"current\";\n }\n await registerDeliveryKey(params);\n return \"re-registered\";\n}\n\n// ---------------------------------------------------------------------------\n// Persistence codec + storage port (custody stays app-side, ADR-0034)\n// ---------------------------------------------------------------------------\n\n/** Serialized form: `publicKey(32) ‖ secretKey(32)`. */\nexport function encodeDeliveryKeypair(kp: DeliveryKeypair): Uint8Array {\n requireLen(kp.publicKey, 32, \"delivery public key\");\n requireLen(kp.secretKey, 32, \"delivery secret key\");\n const out = new Uint8Array(64);\n out.set(kp.publicKey, 0);\n out.set(kp.secretKey, 32);\n return out;\n}\n\n/** Inverse of {@link encodeDeliveryKeypair}; throws on a wrong length. */\nexport function decodeDeliveryKeypair(bytes: Uint8Array): DeliveryKeypair {\n requireLen(bytes, 64, \"serialized delivery keypair\");\n return { publicKey: bytes.slice(0, 32), secretKey: bytes.slice(32) };\n}\n\n/**\n * Origin-scoped storage port for the serialized keypair. Apps implement it\n * over IndexedDB / localStorage / whatever the origin offers — the SDK owns\n * only the codec, keeping DOM typing out of this module.\n */\nexport interface DeliveryKeyStorage {\n get(): Promise<Uint8Array | null>;\n set(serialized: Uint8Array): Promise<void>;\n}\n\n/**\n * Load the origin's Delivery Key, generating + persisting one on first use.\n * Generate-once semantics: every call on an empty store MUST return the same\n * keypair — re-generating would orphan the daemon registration.\n */\nexport async function loadOrCreateDeliveryKey(\n storage: DeliveryKeyStorage,\n): Promise<DeliveryKeypair> {\n const stored = await storage.get();\n if (stored !== null) return decodeDeliveryKeypair(stored);\n const kp = generateDeliveryKey();\n await storage.set(encodeDeliveryKeypair(kp));\n return kp;\n}\n\n// --- internal ---------------------------------------------------------------\n\nfunction requireLen(b: Uint8Array, n: number, what: string): void {\n if (b.length !== n) {\n throw new Error(`${what} must be ${n} bytes, got ${b.length}`);\n }\n}\n","/**\n * evidence/fetch.ts — framework-agnostic evidence-daemon fetches.\n *\n * GET {endpoint}/evidence/{dispute}/for/{juror} — juror delivery\n * (ADR-0034: decrypt each round with the registered Delivery Key secret)\n * GET {endpoint}/evidence/{subaccord}/{dispute}/{round} — public manifest\n *\n * Both are the pure fetch — React apps wrap them in hooks\n * (see apps/app and apps/canon `useManifest.ts`).\n */\nimport { fromBase64 } from \"./base64.js\";\nimport type { JurorBundle } from \"./ecies.js\";\n\n// ---------------------------------------------------------------------------\n// Juror delivery — GET /evidence/{dispute}/for/{juror}\n// ---------------------------------------------------------------------------\n\nexport interface FetchDeliveryParams {\n /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */\n endpoint: string;\n /** Dispute address, base58. */\n dispute: string;\n /** Juror address, base58 — delivery targets their registered Delivery Key. */\n juror: string;\n}\n\n/** One manifest entry's delivery status (v2 multifile derived index). */\nexport interface DeliveredFile {\n readonly path: string;\n readonly status: \"stored\" | \"pending\" | \"out_of_band\";\n}\n\n/**\n * One round's delivered package: a {@link JurorBundle} plus the v2 derived\n * per-entry index. `round` is the evidence round (or the synod party slot).\n */\nexport interface DeliveredRound extends JurorBundle {\n readonly round: number;\n readonly files: readonly DeliveredFile[];\n /** True iff every tracked entry is stored (always true for manifest-only). */\n readonly complete: boolean;\n}\n\n/** Type guard for one wire round: `{round, out, operator_ephem_pub, files?, complete?}`. */\nfunction isWireRound(v: unknown): v is {\n round: number;\n out: string;\n operator_ephem_pub: string;\n files?: DeliveredFile[];\n complete?: boolean;\n} {\n if (v === null || typeof v !== \"object\") return false;\n const r = v as Record<string, unknown>;\n return (\n typeof r[\"round\"] === \"number\" &&\n typeof r[\"out\"] === \"string\" &&\n typeof r[\"operator_ephem_pub\"] === \"string\" &&\n (r[\"files\"] === undefined || Array.isArray(r[\"files\"])) &&\n (r[\"complete\"] === undefined || typeof r[\"complete\"] === \"boolean\")\n );\n}\n\n/**\n * Pull the juror-bound delivery: one re-encrypted package per non-zero\n * evidence hash. Base64 wire fields are decoded to bytes so each round feeds\n * straight into `jurorDecryptDelivery`. `null` when nothing is deliverable\n * (404 — not drawn, no bundle, or no Delivery Key registered).\n */\nexport async function fetchDelivery(\n params: FetchDeliveryParams,\n): Promise<DeliveredRound[] | null> {\n const { endpoint, dispute, juror } = params;\n const res = await fetch(`${endpoint}/evidence/${dispute}/for/${juror}`);\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n const body = (await res.json()) as { rounds?: unknown };\n if (!Array.isArray(body.rounds) || !body.rounds.every(isWireRound)) {\n throw new Error(\"delivery response malformed\");\n }\n return body.rounds.map((r) => ({\n round: r.round,\n out: fromBase64(r.out),\n operator_ephem_pub: fromBase64(r.operator_ephem_pub),\n files: r.files ?? [],\n complete: r.complete ?? true,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Public manifest — GET /evidence/{subaccord}/{dispute}/{round}\n// ---------------------------------------------------------------------------\n\nexport interface FetchManifestParams {\n endpoint: string;\n subaccord: string;\n dispute: string;\n round: number;\n}\n\n/**\n * Fetch the decrypted manifest (a YAML string for the `accord-evidence/v1`\n * format, or a parsed JSON object if the plaintext was JSON). `null` when no\n * bundle is stored for the round (404).\n */\nexport async function fetchManifest(\n params: FetchManifestParams,\n): Promise<unknown> {\n const { endpoint, subaccord, dispute, round } = params;\n const res = await fetch(\n `${endpoint}/evidence/${subaccord}/${dispute}/${round}`,\n );\n if (res.status === 404) return null;\n if (!res.ok) throw new Error(`evidence daemon returned ${res.status}`);\n return res.json();\n}\n"],"mappings":"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","names":["randomBytes","randomBytes","te","sha256","equalBytes","ed25519","te","requireLen","ed25519"]}
@@ -1,5 +1,5 @@
1
1
  export interface PublishParams {
2
- /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */
2
+ /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */
3
3
  endpoint: string;
4
4
  subaccord: string;
5
5
  dispute: string;
@@ -16,7 +16,7 @@ export interface PublishParams {
16
16
  */
17
17
  export declare function publishEvidence(params: PublishParams): Promise<void>;
18
18
  export interface PublishSynodParams {
19
- /** Daemon base URL (app-side `EVIDENCE_DAEMON_URL`). */
19
+ /** Daemon base URL (app-side `useEvidenceDaemonUrl()`). */
20
20
  endpoint: string;
21
21
  /** The SynodCase PDA — the pre-dispute grouping key. */
22
22
  casePda: string;
@@ -1 +1 @@
1
- {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/evidence/publish.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,EAAE,UAAU,CAAC;IACrB,mFAAmF;IACnF,WAAW,EAAE,UAAU,CAAC;CACzB;AAqBD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,UAAU,CAAC;IACrB,mFAAmF;IACnF,WAAW,EAAE,UAAU,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,UAAU,GACvB,OAAO,CAAC,IAAI,CAAC,CAOf"}
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/evidence/publish.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,QAAQ,EAAE,UAAU,CAAC;IACrB,mFAAmF;IACnF,WAAW,EAAE,UAAU,CAAC;CACzB;AAqBD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAO1E;AAED,MAAM,WAAW,kBAAkB;IACjC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,UAAU,CAAC;IACrB,mFAAmF;IACnF,WAAW,EAAE,UAAU,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,UAAU,GACvB,OAAO,CAAC,IAAI,CAAC,CAOf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useaccord/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "TypeScript SDK for the Accord program on Solana",
5
5
  "repository": {
6
6
  "type": "git",