@kynesyslabs/demosdk 4.0.9 → 4.0.11

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 (76) hide show
  1. package/build/abstraction/Identities.d.ts +70 -0
  2. package/build/abstraction/Identities.js +160 -0
  3. package/build/abstraction/Identities.js.map +1 -1
  4. package/build/identity/cci/claim.d.ts +47 -0
  5. package/build/identity/cci/claim.js +86 -0
  6. package/build/identity/cci/claim.js.map +1 -0
  7. package/build/identity/cci/index.d.ts +11 -0
  8. package/build/identity/cci/index.js +11 -0
  9. package/build/identity/cci/index.js.map +1 -0
  10. package/build/identity/cci/signing.d.ts +44 -0
  11. package/build/identity/cci/signing.js +83 -0
  12. package/build/identity/cci/signing.js.map +1 -0
  13. package/build/identity/cci/types.d.ts +14 -0
  14. package/build/identity/cci/types.js +10 -0
  15. package/build/identity/cci/types.js.map +1 -0
  16. package/build/identity/index.d.ts +2 -0
  17. package/build/identity/index.js +3 -0
  18. package/build/identity/index.js.map +1 -0
  19. package/build/l2ps/anchor/anchor.d.ts +92 -0
  20. package/build/l2ps/anchor/anchor.js +186 -0
  21. package/build/l2ps/anchor/anchor.js.map +1 -0
  22. package/build/l2ps/anchor/index.d.ts +10 -0
  23. package/build/l2ps/anchor/index.js +10 -0
  24. package/build/l2ps/anchor/index.js.map +1 -0
  25. package/build/l2ps/anchor/types.d.ts +36 -0
  26. package/build/l2ps/anchor/types.js +2 -0
  27. package/build/l2ps/anchor/types.js.map +1 -0
  28. package/build/l2ps/binding/binding.d.ts +72 -0
  29. package/build/l2ps/binding/binding.js +160 -0
  30. package/build/l2ps/binding/binding.js.map +1 -0
  31. package/build/l2ps/binding/canonical.d.ts +19 -0
  32. package/build/l2ps/binding/canonical.js +30 -0
  33. package/build/l2ps/binding/canonical.js.map +1 -0
  34. package/build/l2ps/binding/index.d.ts +10 -0
  35. package/build/l2ps/binding/index.js +10 -0
  36. package/build/l2ps/binding/index.js.map +1 -0
  37. package/build/l2ps/binding/types.d.ts +20 -0
  38. package/build/l2ps/binding/types.js +2 -0
  39. package/build/l2ps/binding/types.js.map +1 -0
  40. package/build/l2ps/channel/canonical.d.ts +24 -0
  41. package/build/l2ps/channel/canonical.js +44 -0
  42. package/build/l2ps/channel/canonical.js.map +1 -0
  43. package/build/l2ps/channel/channelIdRegistry.d.ts +19 -0
  44. package/build/l2ps/channel/channelIdRegistry.js +16 -0
  45. package/build/l2ps/channel/channelIdRegistry.js.map +1 -0
  46. package/build/l2ps/channel/envelope.d.ts +26 -0
  47. package/build/l2ps/channel/envelope.js +62 -0
  48. package/build/l2ps/channel/envelope.js.map +1 -0
  49. package/build/l2ps/channel/index.d.ts +14 -0
  50. package/build/l2ps/channel/index.js +14 -0
  51. package/build/l2ps/channel/index.js.map +1 -0
  52. package/build/l2ps/channel/session.d.ts +66 -0
  53. package/build/l2ps/channel/session.js +101 -0
  54. package/build/l2ps/channel/session.js.map +1 -0
  55. package/build/l2ps/channel/transcript.d.ts +48 -0
  56. package/build/l2ps/channel/transcript.js +120 -0
  57. package/build/l2ps/channel/transcript.js.map +1 -0
  58. package/build/l2ps/channel/types.d.ts +55 -0
  59. package/build/l2ps/channel/types.js +2 -0
  60. package/build/l2ps/channel/types.js.map +1 -0
  61. package/build/l2ps/index.d.ts +5 -2
  62. package/build/l2ps/index.js +3 -0
  63. package/build/l2ps/index.js.map +1 -1
  64. package/build/l2ps/l2ps.d.ts +32 -0
  65. package/build/l2ps/l2ps.js +60 -0
  66. package/build/l2ps/l2ps.js.map +1 -1
  67. package/build/l2ps/utils/hex.d.ts +26 -0
  68. package/build/l2ps/utils/hex.js +45 -0
  69. package/build/l2ps/utils/hex.js.map +1 -0
  70. package/build/types/abstraction/index.d.ts +17 -2
  71. package/build/websdk/Web2Calls.js +2 -0
  72. package/build/websdk/Web2Calls.js.map +1 -1
  73. package/build/websdk/demosclass.d.ts +15 -0
  74. package/build/websdk/demosclass.js +25 -0
  75. package/build/websdk/demosclass.js.map +1 -1
  76. package/package.json +4 -2
@@ -0,0 +1,83 @@
1
+ import { Cryptography } from "../../encryption/Cryptography.js";
2
+ import { demosAddressFromClaim, normalizeDemosAddress, parseClaimRef, } from "./claim.js";
3
+ /**
4
+ * Sign a payload with the key controlling a CCI primary claim.
5
+ *
6
+ * The brief invariant (DACS-3 §0 / §8.3.2): in-channel signatures MUST be
7
+ * produced by the key that controls the sender's primary claim — the same
8
+ * key that holds value on-chain. On Demos that is the Demos Ed25519 key,
9
+ * NOT the RSA L2PS subnet key.
10
+ *
11
+ * Domain separation is the caller's responsibility — pass the full payload
12
+ * including the `dacs-*:v1:` prefix. WI-0 stays scheme-agnostic.
13
+ *
14
+ * @param claim - The signer's primary `demos:` ClaimReference. Refuses any
15
+ * other scheme until a controlling-key resolver for them
16
+ * is added.
17
+ * @param payload - Pre-domain-separated bytes to cover. Caller composes
18
+ * `dacs-*:v1: || canonical_hash`.
19
+ * @param demos - Connected `Demos` instance. Its Ed25519 address must
20
+ * match `claim`'s identifier — otherwise we refuse to
21
+ * produce a misattributable signature.
22
+ * @returns Raw 64-byte Ed25519 signature.
23
+ * @throws {Error} If the scheme is not `demos`, the wallet is not
24
+ * connected, or the connected address does not match the
25
+ * claim's address.
26
+ */
27
+ export async function signWithPrimaryClaim(claim, payload, demos) {
28
+ const { scheme } = parseClaimRef(claim);
29
+ if (scheme !== "demos") {
30
+ throw new Error(`signWithPrimaryClaim: unsupported scheme "${scheme}". Only "demos" is currently supported.`);
31
+ }
32
+ if (!demos.walletConnected) {
33
+ throw new Error("signWithPrimaryClaim: Demos wallet not connected");
34
+ }
35
+ const claimAddress = demosAddressFromClaim(claim);
36
+ const connected = normalizeDemosAddress(await demos.getEd25519Address());
37
+ if (claimAddress !== connected) {
38
+ throw new Error(`signWithPrimaryClaim: claim "${claim}" does not match connected Demos wallet ${connected}`);
39
+ }
40
+ const signed = await demos.crypto.sign("ed25519", payload);
41
+ return toUint8Array(signed.signature);
42
+ }
43
+ /**
44
+ * Verify an Ed25519 signature produced by `signWithPrimaryClaim`.
45
+ *
46
+ * Recovers the public key from the claim identifier and verifies forge-style.
47
+ * Caller-supplied payload must match the bytes that were signed (including
48
+ * any `dacs-*:v1:` domain prefix).
49
+ *
50
+ * @param claim - The claimed signer's `demos:` ClaimReference.
51
+ * @param payload - Exact bytes that were signed (must roundtrip through
52
+ * UTF-8). Caller composes `dacs-*:v1: || hash`.
53
+ * @param signature - Raw 64-byte Ed25519 signature.
54
+ * @returns `true` iff the signature verifies under the public key
55
+ * encoded in `claim`; `false` for cryptographic mismatch.
56
+ * @throws {Error} If the scheme is not `demos` or the recovered public
57
+ * key is not 32 bytes (malformed claim).
58
+ */
59
+ export function verifyPrimaryClaimSignature(claim, payload, signature) {
60
+ const { scheme } = parseClaimRef(claim);
61
+ if (scheme !== "demos") {
62
+ throw new Error(`verifyPrimaryClaimSignature: unsupported scheme "${scheme}"`);
63
+ }
64
+ const publicKey = hexAddressToBytes(demosAddressFromClaim(claim));
65
+ if (publicKey.length !== 32) {
66
+ throw new Error(`verifyPrimaryClaimSignature: expected 32-byte Ed25519 public key, got ${publicKey.length}`);
67
+ }
68
+ return Cryptography.verify(new TextDecoder().decode(payload), Buffer.from(signature), Buffer.from(publicKey));
69
+ }
70
+ function hexAddressToBytes(address) {
71
+ const hex = address.startsWith("0x") ? address.slice(2) : address;
72
+ const out = new Uint8Array(hex.length / 2);
73
+ for (let i = 0; i < hex.length; i += 2) {
74
+ out[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
75
+ }
76
+ return out;
77
+ }
78
+ function toUint8Array(buf) {
79
+ if (buf instanceof Uint8Array)
80
+ return buf;
81
+ return Uint8Array.from(buf);
82
+ }
83
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../../../../src/identity/cci/signing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Cross-Context Identity (CCI) — type definitions.
3
+ *
4
+ * A ClaimReference is the label that names an identity across substrates.
5
+ * It is NOT a key; the key controlling the claim signs in-channel messages.
6
+ * See `docs/l2ps-sr4-implementation-brief.md` §0 in the node repo for the
7
+ * full claim-vs-key distinction.
8
+ */
9
+ export type ClaimScheme = "demos" | (string & {});
10
+ export type ClaimReference = `${string}:${string}`;
11
+ export interface ParsedClaimRef {
12
+ scheme: ClaimScheme;
13
+ identifier: string;
14
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Cross-Context Identity (CCI) — type definitions.
3
+ *
4
+ * A ClaimReference is the label that names an identity across substrates.
5
+ * It is NOT a key; the key controlling the claim signs in-channel messages.
6
+ * See `docs/l2ps-sr4-implementation-brief.md` §0 in the node repo for the
7
+ * full claim-vs-key distinction.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/identity/cci/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,2 @@
1
+ export * as humanpassport from "./humanpassport";
2
+ export * as cci from "./cci";
@@ -0,0 +1,3 @@
1
+ export * as humanpassport from "./humanpassport/index.js";
2
+ export * as cci from "./cci/index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/identity/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,GAAG,MAAM,OAAO,CAAA"}
@@ -0,0 +1,92 @@
1
+ import { type ClaimReference } from "../../identity/cci";
2
+ import { Demos } from "../../websdk/demosclass";
3
+ import L2PS from "../l2ps";
4
+ import type { ChannelTranscript } from "../channel/types";
5
+ import type { AttestationRef, TranscriptDisclosurePolicy } from "./types";
6
+ /**
7
+ * Deterministic per-channel Storage Program name. One anchor per
8
+ * channelId; a redo of the same channel reuses the slot (the SP's
9
+ * `mode: "owner"` ACL guarantees only the original signer can overwrite).
10
+ *
11
+ * @param channelId - Per-session `channelId` (matches `ChannelTranscript.channelId`).
12
+ * @returns The SR-2 program name used by `anchorEncryptedTranscript`.
13
+ */
14
+ export declare function anchorProgramName(channelId: string): string;
15
+ export interface AnchorEncryptedTranscriptOpts {
16
+ transcript: ChannelTranscript;
17
+ /** Provides the AES-GCM key for encrypt-to-member-set. */
18
+ l2ps: L2PS;
19
+ demos: Demos;
20
+ /** Must be a member of `transcript.members` and match the connected Demos wallet. */
21
+ signer: ClaimReference;
22
+ policy: TranscriptDisclosurePolicy;
23
+ /** Required to actually anchor when `policy === "encrypted-anchored-recommended"`. */
24
+ consent?: boolean;
25
+ }
26
+ /**
27
+ * §8.7 anchor entrypoint. Encrypts a `ChannelTranscript` to the L2PS
28
+ * member set, anchors the ciphertext via SR-2, and signs the plaintext
29
+ * hash with the connected Demos key.
30
+ *
31
+ * Policy behaviour:
32
+ * - `none` → throws (caller should not have invoked this).
33
+ * - `recommended` → only anchors when `consent === true`; otherwise returns `null`.
34
+ * - `required` → always anchors; broadcast failures propagate so
35
+ * the caller's phase can fail per §8.7.
36
+ *
37
+ * The SP is deployed via SR-2 with `mode: "public"` (anyone reads the
38
+ * ciphertext + the public content hash for tamper-evidence; only the
39
+ * owner can overwrite).
40
+ *
41
+ * @param opts - Transcript + L2PS instance (for the AES-GCM key) +
42
+ * connected Demos (for the signing key + broadcast) +
43
+ * signer claim + policy/consent.
44
+ * @returns `{ anchor, contentHash }` on success, `null` only when the
45
+ * recommended policy is in effect and consent is missing.
46
+ * @throws {Error} On `policy === "none"`, non-demos signer, non-member
47
+ * signer, signer-wallet mismatch, or broadcast failure
48
+ * under `required` policy.
49
+ */
50
+ export declare function anchorEncryptedTranscript(opts: AnchorEncryptedTranscriptOpts): Promise<AttestationRef | null>;
51
+ export interface DecryptAnchoredTranscriptOpts {
52
+ rpcUrl: string;
53
+ storageAddress: string;
54
+ l2ps: L2PS;
55
+ }
56
+ /**
57
+ * Fetch + decrypt + fully verify the original transcript.
58
+ *
59
+ * @param opts - RPC URL + Storage Program address + L2PS instance (caller
60
+ * must be a subnet member; otherwise AES-GCM auth fails).
61
+ * @returns A `ChannelTranscript` whose every component has been
62
+ * validated end-to-end — callers do not need to run
63
+ * `verifyTranscript` again.
64
+ * @throws {Error} On the first of:
65
+ * - SP not found / wrong shape / unknown transcriptVersion
66
+ * - ciphertext hash does not match the stored `contentHash` (tamper)
67
+ * - AES-GCM auth fails (= caller's L2PS instance is not a subnet member)
68
+ * - decrypted `channelId` does not match the anchor's `channelId`
69
+ * - anchor-party transcript signature is invalid
70
+ * - any embedded ChannelMessage signature, sequence, sender ∈ members,
71
+ * or per-message channelId check fails (delegated to `verifyTranscript`).
72
+ */
73
+ export declare function decryptAnchoredTranscript(opts: DecryptAnchoredTranscriptOpts): Promise<ChannelTranscript>;
74
+ export interface VerifyAnchorIntegrityResult {
75
+ ok: boolean;
76
+ errors: string[];
77
+ }
78
+ /**
79
+ * Public tamper-check that needs no decryption keys. Hashes the on-chain
80
+ * ciphertext and compares with the stored `contentHash`; also asserts the
81
+ * SP owner address matches the embedded signer's CCI claim. Useful for
82
+ * auditors and non-members verifying an anchor without subnet access.
83
+ *
84
+ * @param opts - RPC URL + Storage Program address.
85
+ * @returns `{ ok, errors }` — `ok` only when every public check passes;
86
+ * `errors` contains every failure (collected in one pass so
87
+ * auditors see the full picture, not the first failure).
88
+ */
89
+ export declare function verifyAnchorIntegrity(opts: {
90
+ rpcUrl: string;
91
+ storageAddress: string;
92
+ }): Promise<VerifyAnchorIntegrityResult>;
@@ -0,0 +1,186 @@
1
+ import { sha256 } from "@noble/hashes/sha2";
2
+ import { canonicalJSONStringify } from "../../websdk/utils/canonicalJson.js";
3
+ import { demosAddressFromClaim, isDemosClaim, normalizeDemosAddress, signWithPrimaryClaim, verifyPrimaryClaimSignature, } from "../../identity/cci/index.js";
4
+ import { DemosTransactions } from "../../websdk/DemosTransactions.js";
5
+ import { StorageProgram } from "../../storage/StorageProgram.js";
6
+ import { stripTranscriptSignatures, transcriptSigningBytes, } from "../channel/canonical.js";
7
+ import { verifyTranscript } from "../channel/transcript.js";
8
+ import { bytesToHex, signatureFromHex, signatureToHex } from "../utils/hex.js";
9
+ /**
10
+ * Deterministic per-channel Storage Program name. One anchor per
11
+ * channelId; a redo of the same channel reuses the slot (the SP's
12
+ * `mode: "owner"` ACL guarantees only the original signer can overwrite).
13
+ *
14
+ * @param channelId - Per-session `channelId` (matches `ChannelTranscript.channelId`).
15
+ * @returns The SR-2 program name used by `anchorEncryptedTranscript`.
16
+ */
17
+ export function anchorProgramName(channelId) {
18
+ return `l2ps-transcript:${encodeURIComponent(channelId)}`;
19
+ }
20
+ /**
21
+ * §8.7 anchor entrypoint. Encrypts a `ChannelTranscript` to the L2PS
22
+ * member set, anchors the ciphertext via SR-2, and signs the plaintext
23
+ * hash with the connected Demos key.
24
+ *
25
+ * Policy behaviour:
26
+ * - `none` → throws (caller should not have invoked this).
27
+ * - `recommended` → only anchors when `consent === true`; otherwise returns `null`.
28
+ * - `required` → always anchors; broadcast failures propagate so
29
+ * the caller's phase can fail per §8.7.
30
+ *
31
+ * The SP is deployed via SR-2 with `mode: "public"` (anyone reads the
32
+ * ciphertext + the public content hash for tamper-evidence; only the
33
+ * owner can overwrite).
34
+ *
35
+ * @param opts - Transcript + L2PS instance (for the AES-GCM key) +
36
+ * connected Demos (for the signing key + broadcast) +
37
+ * signer claim + policy/consent.
38
+ * @returns `{ anchor, contentHash }` on success, `null` only when the
39
+ * recommended policy is in effect and consent is missing.
40
+ * @throws {Error} On `policy === "none"`, non-demos signer, non-member
41
+ * signer, signer-wallet mismatch, or broadcast failure
42
+ * under `required` policy.
43
+ */
44
+ export async function anchorEncryptedTranscript(opts) {
45
+ if (opts.policy === "none") {
46
+ throw new Error("anchorEncryptedTranscript: policy is 'none' — do not call this helper");
47
+ }
48
+ if (opts.policy === "encrypted-anchored-recommended" &&
49
+ opts.consent !== true) {
50
+ return null;
51
+ }
52
+ if (!isDemosClaim(opts.signer)) {
53
+ throw new Error(`anchorEncryptedTranscript: signer must be a demos: ClaimReference, got "${opts.signer}"`);
54
+ }
55
+ if (!opts.transcript.members.includes(opts.signer)) {
56
+ throw new Error(`anchorEncryptedTranscript: signer "${opts.signer}" is not in transcript.members`);
57
+ }
58
+ const signerAddress = demosAddressFromClaim(opts.signer);
59
+ const connected = normalizeDemosAddress(await opts.demos.getEd25519Address());
60
+ if (signerAddress !== connected) {
61
+ throw new Error(`anchorEncryptedTranscript: signer "${opts.signer}" does not match connected wallet ${connected}`);
62
+ }
63
+ const unsignedView = stripTranscriptSignatures(opts.transcript);
64
+ const plaintextBytes = new TextEncoder().encode(canonicalJSONStringify(unsignedView));
65
+ const encrypted = await opts.l2ps.encryptBytes(plaintextBytes);
66
+ const contentHash = bytesToHex(sha256(base64ToBytes(encrypted.ciphertext)));
67
+ const sigBytes = await signWithPrimaryClaim(opts.signer, transcriptSigningBytes(unsignedView), opts.demos);
68
+ const payload = {
69
+ transcriptVersion: "1",
70
+ channelId: opts.transcript.channelId,
71
+ encrypted,
72
+ contentHash,
73
+ signature: {
74
+ sigVersion: "1",
75
+ signer: opts.signer,
76
+ signature: signatureToHex(sigBytes),
77
+ },
78
+ };
79
+ const storageAddress = await deployAnchorSP(opts.demos, anchorProgramName(opts.transcript.channelId), payload);
80
+ return { anchor: storageAddress, contentHash };
81
+ }
82
+ async function deployAnchorSP(demos, programName, payload) {
83
+ const deployer = normalizeDemosAddress(await demos.getEd25519Address());
84
+ const nonce = (await demos.getAddressNonce(deployer)) + 1;
85
+ const spPayload = StorageProgram.createStorageProgram(deployer, programName, payload, "json", { mode: "public" }, { nonce });
86
+ const tx = DemosTransactions.empty();
87
+ tx.content.to = spPayload.storageAddress;
88
+ tx.content.nonce = nonce;
89
+ tx.content.amount = 0;
90
+ tx.content.type = "storageProgram";
91
+ tx.content.timestamp = Date.now();
92
+ tx.content.data = ["storageProgram", spPayload];
93
+ const signed = await demos.sign(tx);
94
+ const validity = await demos.confirm(signed);
95
+ await demos.broadcast(validity);
96
+ return spPayload.storageAddress;
97
+ }
98
+ /**
99
+ * Fetch + decrypt + fully verify the original transcript.
100
+ *
101
+ * @param opts - RPC URL + Storage Program address + L2PS instance (caller
102
+ * must be a subnet member; otherwise AES-GCM auth fails).
103
+ * @returns A `ChannelTranscript` whose every component has been
104
+ * validated end-to-end — callers do not need to run
105
+ * `verifyTranscript` again.
106
+ * @throws {Error} On the first of:
107
+ * - SP not found / wrong shape / unknown transcriptVersion
108
+ * - ciphertext hash does not match the stored `contentHash` (tamper)
109
+ * - AES-GCM auth fails (= caller's L2PS instance is not a subnet member)
110
+ * - decrypted `channelId` does not match the anchor's `channelId`
111
+ * - anchor-party transcript signature is invalid
112
+ * - any embedded ChannelMessage signature, sequence, sender ∈ members,
113
+ * or per-message channelId check fails (delegated to `verifyTranscript`).
114
+ */
115
+ export async function decryptAnchoredTranscript(opts) {
116
+ const sp = await StorageProgram.getByAddress(opts.rpcUrl, opts.storageAddress);
117
+ if (!sp?.data || typeof sp.data !== "object")
118
+ throw new Error(`decryptAnchoredTranscript: no storage program at ${opts.storageAddress}`);
119
+ const payload = sp.data;
120
+ if (payload.transcriptVersion !== "1")
121
+ throw new Error("decryptAnchoredTranscript: unknown transcriptVersion");
122
+ const ctHash = bytesToHex(sha256(base64ToBytes(payload.encrypted.ciphertext)));
123
+ if (ctHash !== payload.contentHash)
124
+ throw new Error("decryptAnchoredTranscript: ciphertext hash does not match stored contentHash (tamper)");
125
+ const plaintext = await opts.l2ps.decryptBytes(payload.encrypted);
126
+ const transcript = JSON.parse(new TextDecoder().decode(plaintext));
127
+ if (transcript.channelId !== payload.channelId)
128
+ throw new Error("decryptAnchoredTranscript: decrypted channelId does not match anchor channelId");
129
+ const sigOk = verifyPrimaryClaimSignature(payload.signature.signer, transcriptSigningBytes(transcript), signatureFromHex(payload.signature.signature));
130
+ if (!sigOk)
131
+ throw new Error("decryptAnchoredTranscript: transcript signature verification failed");
132
+ const fullTranscript = {
133
+ ...transcript,
134
+ signatures: [payload.signature],
135
+ };
136
+ // Run the audit verifier on the assembled transcript so the caller's
137
+ // return value is end-to-end validated, not just the anchor wrapper.
138
+ const audit = verifyTranscript(fullTranscript);
139
+ if (!audit.ok)
140
+ throw new Error(`decryptAnchoredTranscript: transcript audit failed — ${audit.errors.join("; ")}`);
141
+ return fullTranscript;
142
+ }
143
+ /**
144
+ * Public tamper-check that needs no decryption keys. Hashes the on-chain
145
+ * ciphertext and compares with the stored `contentHash`; also asserts the
146
+ * SP owner address matches the embedded signer's CCI claim. Useful for
147
+ * auditors and non-members verifying an anchor without subnet access.
148
+ *
149
+ * @param opts - RPC URL + Storage Program address.
150
+ * @returns `{ ok, errors }` — `ok` only when every public check passes;
151
+ * `errors` contains every failure (collected in one pass so
152
+ * auditors see the full picture, not the first failure).
153
+ */
154
+ export async function verifyAnchorIntegrity(opts) {
155
+ const errors = [];
156
+ const sp = await StorageProgram.getByAddress(opts.rpcUrl, opts.storageAddress);
157
+ if (!sp?.data || typeof sp.data !== "object") {
158
+ errors.push(`no storage program at ${opts.storageAddress}`);
159
+ return { ok: false, errors };
160
+ }
161
+ const payload = sp.data;
162
+ if (payload.transcriptVersion !== "1")
163
+ errors.push(`unknown transcriptVersion ${payload.transcriptVersion}`);
164
+ try {
165
+ const hash = bytesToHex(sha256(base64ToBytes(payload.encrypted.ciphertext)));
166
+ if (hash !== payload.contentHash)
167
+ errors.push("ciphertext hash does not match stored contentHash");
168
+ }
169
+ catch (e) {
170
+ errors.push(`hash check failed: ${e.message}`);
171
+ }
172
+ try {
173
+ const signerAddress = demosAddressFromClaim(payload.signature.signer);
174
+ if (normalizeDemosAddress(sp.owner) !== signerAddress)
175
+ errors.push(`storage program owner ${sp.owner} does not match signer claim ${payload.signature.signer}`);
176
+ }
177
+ catch (e) {
178
+ errors.push(`signer claim address check failed: ${e.message}`);
179
+ }
180
+ return { ok: errors.length === 0, errors };
181
+ }
182
+ function base64ToBytes(b64) {
183
+ const bin = Buffer.from(b64, "base64");
184
+ return new Uint8Array(bin.buffer, bin.byteOffset, bin.byteLength);
185
+ }
186
+ //# sourceMappingURL=anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../../../src/l2ps/anchor/anchor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * DACS-3 §8.7 — encrypted transcript anchoring (SR-4 WI-3).
3
+ *
4
+ * Encrypts a `ChannelTranscript` to the subnet member set via the
5
+ * existing L2PS AES-GCM key, anchors the ciphertext + public content hash
6
+ * via SR-2 Storage Program, and signs the transcript-plaintext hash with
7
+ * the caller's Demos key.
8
+ */
9
+ export { anchorEncryptedTranscript, anchorProgramName, decryptAnchoredTranscript, verifyAnchorIntegrity, type AnchorEncryptedTranscriptOpts, type DecryptAnchoredTranscriptOpts, type VerifyAnchorIntegrityResult, } from "./anchor";
10
+ export type { AnchoredTranscriptPayload, AnchoredTranscriptSignature, AttestationRef, TranscriptDisclosurePolicy, } from "./types";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * DACS-3 §8.7 — encrypted transcript anchoring (SR-4 WI-3).
3
+ *
4
+ * Encrypts a `ChannelTranscript` to the subnet member set via the
5
+ * existing L2PS AES-GCM key, anchors the ciphertext + public content hash
6
+ * via SR-2 Storage Program, and signs the transcript-plaintext hash with
7
+ * the caller's Demos key.
8
+ */
9
+ export { anchorEncryptedTranscript, anchorProgramName, decryptAnchoredTranscript, verifyAnchorIntegrity, } from "./anchor.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/l2ps/anchor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACH,yBAAyB,EACzB,iBAAiB,EACjB,yBAAyB,EACzB,qBAAqB,GAIxB,MAAM,UAAU,CAAA"}
@@ -0,0 +1,36 @@
1
+ import type { ClaimReference } from "../../identity/cci";
2
+ import type { L2PSEncryptedBytes } from "../l2ps";
3
+ /**
4
+ * §8.7 disclosure policy values. Naming verbatim from the brief.
5
+ */
6
+ export type TranscriptDisclosurePolicy = "none" | "encrypted-anchored-recommended" | "encrypted-anchored-required";
7
+ /**
8
+ * The reference the negotiate-* phase output carries (brief §2 WI-3 step 4):
9
+ * a Storage Program address + a hex content hash that anyone can re-derive
10
+ * by hashing the on-chain ciphertext.
11
+ */
12
+ export interface AttestationRef {
13
+ anchor: string;
14
+ contentHash: string;
15
+ }
16
+ /**
17
+ * Signature over the transcript-plaintext hash, written verbatim into the
18
+ * Storage Program so members can re-verify after decryption.
19
+ */
20
+ export interface AnchoredTranscriptSignature {
21
+ sigVersion: "1";
22
+ signer: ClaimReference;
23
+ signature: string;
24
+ }
25
+ /**
26
+ * Wire shape stored inside the Storage Program. Self-contained so a
27
+ * verifier can run `verifyAnchorIntegrity` without prior context.
28
+ */
29
+ export interface AnchoredTranscriptPayload {
30
+ transcriptVersion: "1";
31
+ channelId: string;
32
+ encrypted: L2PSEncryptedBytes;
33
+ /** Hex sha256 of the decoded ciphertext bytes — public tamper-evidence. */
34
+ contentHash: string;
35
+ signature: AnchoredTranscriptSignature;
36
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/l2ps/anchor/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ import { type ClaimReference } from "../../identity/cci";
2
+ import { Demos } from "../../websdk/demosclass";
3
+ import type { L2PSMembershipBinding } from "./types";
4
+ import L2PS from "../l2ps";
5
+ /**
6
+ * Deterministic Storage Program name for `(channelId, subnetMemberId)`.
7
+ * Public so `resolveMember` can re-derive it; included verbatim in tests.
8
+ *
9
+ * Components are percent-encoded so the layout stays one-to-one. Without
10
+ * this guard, the pairs `("a:b", "c")` and `("a", "b:c")` would collide on
11
+ * the same SP name and `resolveMember` could be tricked into returning a
12
+ * binding from one channel/member context for a lookup of another.
13
+ */
14
+ export declare function bindingProgramName(channelId: string, subnetMemberId: string): string;
15
+ /**
16
+ * Brief calls subnetMemberId "the member's L2PS/RSA identity within the
17
+ * subnet". L2PS already publishes a stable fingerprint of the RSA key —
18
+ * we reuse that so the value is derivable on both sides without a separate
19
+ * registry.
20
+ */
21
+ export declare function subnetMemberIdFromL2PS(l2ps: L2PS): Promise<string>;
22
+ export interface CreateMembershipBindingOpts {
23
+ channelId: string;
24
+ subnetMemberId: string;
25
+ claim: ClaimReference;
26
+ demos: Demos;
27
+ /** Defaults to Date.now(). Useful for deterministic test fixtures. */
28
+ boundAt?: number;
29
+ }
30
+ /**
31
+ * Build and sign a binding. The signature is produced by the key
32
+ * controlling `claim`. Throws if `claim`'s scheme is not `demos:` or if
33
+ * the controlling address does not match the connected wallet — the
34
+ * invariant that ties channel signatures to on-chain identity.
35
+ */
36
+ export declare function createMembershipBinding(opts: CreateMembershipBindingOpts): Promise<L2PSMembershipBinding>;
37
+ /**
38
+ * Pure signature check — no chain access. Verifies the embedded signature
39
+ * against the Demos public key encoded in `cciPrimaryClaim`.
40
+ *
41
+ * Returns `false` (not throws) on any structural problem so callers can
42
+ * use it as a filter when iterating SP candidates in `resolveMember`.
43
+ */
44
+ export declare function verifyMembershipBinding(binding: L2PSMembershipBinding): boolean;
45
+ export interface AnchorMembershipBindingResult {
46
+ storageAddress: string;
47
+ txHash: string;
48
+ }
49
+ /**
50
+ * Anchor a signed binding to chain via SR-2 (Storage Program). The SP is
51
+ * deployed under `bindingProgramName(...)` so `resolveMember` can find it
52
+ * by name. The deployer (= connected Demos wallet) becomes the SP owner;
53
+ * that owner check is what makes the resolver safe against impostor SPs
54
+ * published under the same name.
55
+ *
56
+ * Returns once the broadcast succeeds. Callers that need on-chain finality
57
+ * should poll the returned tx hash before opening the channel.
58
+ */
59
+ export declare function anchorMembershipBinding(binding: L2PSMembershipBinding, demos: Demos): Promise<AnchorMembershipBindingResult>;
60
+ /**
61
+ * Find the bound `ClaimReference` for `(channelId, subnetMemberId)`.
62
+ *
63
+ * Two-stage check on every candidate Storage Program:
64
+ * 1. Embedded signature verifies under the embedded claim's key.
65
+ * 2. SP owner's Demos address matches that claim's address — so only
66
+ * the actual key-holder could have deployed this SP under this name.
67
+ *
68
+ * Both checks must pass. Returns `null` when no candidate qualifies.
69
+ * Membership is fixed for channel lifetime (CH-1) so callers SHOULD cache
70
+ * the result for the session.
71
+ */
72
+ export declare function resolveMember(channelId: string, subnetMemberId: string, rpcUrl: string): Promise<ClaimReference | null>;