@kynesyslabs/demosdk 4.0.10 → 4.0.12

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 (87) hide show
  1. package/build/abstraction/Identities.d.ts +70 -0
  2. package/build/abstraction/Identities.js +170 -2
  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/finalize.d.ts +63 -0
  50. package/build/l2ps/channel/finalize.js +85 -0
  51. package/build/l2ps/channel/finalize.js.map +1 -0
  52. package/build/l2ps/channel/index.d.ts +17 -0
  53. package/build/l2ps/channel/index.js +17 -0
  54. package/build/l2ps/channel/index.js.map +1 -0
  55. package/build/l2ps/channel/negotiate.d.ts +106 -0
  56. package/build/l2ps/channel/negotiate.js +206 -0
  57. package/build/l2ps/channel/negotiate.js.map +1 -0
  58. package/build/l2ps/channel/session.d.ts +66 -0
  59. package/build/l2ps/channel/session.js +101 -0
  60. package/build/l2ps/channel/session.js.map +1 -0
  61. package/build/l2ps/channel/transcript.d.ts +48 -0
  62. package/build/l2ps/channel/transcript.js +120 -0
  63. package/build/l2ps/channel/transcript.js.map +1 -0
  64. package/build/l2ps/channel/transport.d.ts +117 -0
  65. package/build/l2ps/channel/transport.js +178 -0
  66. package/build/l2ps/channel/transport.js.map +1 -0
  67. package/build/l2ps/channel/types.d.ts +55 -0
  68. package/build/l2ps/channel/types.js +2 -0
  69. package/build/l2ps/channel/types.js.map +1 -0
  70. package/build/l2ps/index.d.ts +5 -2
  71. package/build/l2ps/index.js +3 -0
  72. package/build/l2ps/index.js.map +1 -1
  73. package/build/l2ps/l2ps.d.ts +32 -0
  74. package/build/l2ps/l2ps.js +60 -0
  75. package/build/l2ps/l2ps.js.map +1 -1
  76. package/build/l2ps/utils/hex.d.ts +26 -0
  77. package/build/l2ps/utils/hex.js +45 -0
  78. package/build/l2ps/utils/hex.js.map +1 -0
  79. package/build/types/abstraction/index.d.ts +17 -2
  80. package/build/websdk/BroadcastFailedError.js.map +1 -1
  81. package/build/websdk/TransportError.js.map +1 -1
  82. package/build/websdk/Web2Calls.js +2 -0
  83. package/build/websdk/Web2Calls.js.map +1 -1
  84. package/build/websdk/demosclass.d.ts +15 -0
  85. package/build/websdk/demosclass.js +14 -0
  86. package/build/websdk/demosclass.js.map +1 -1
  87. package/package.json +4 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finalize.js","sourceRoot":"","sources":["../../../../src/l2ps/channel/finalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * DACS-3 §8.3.3 / §8.7 — channel message envelope, session-level
3
+ * sequence + channelId enforcement, transcript export (SR-4 WI-2).
4
+ *
5
+ * All signatures use the sender's primary-claim key (on Demos: the
6
+ * connected Demos Ed25519 keypair) via WI-0's `signWithPrimaryClaim`;
7
+ * never the RSA L2PS subnet key.
8
+ */
9
+ export type { ChannelMessage, ChannelMessageSignature, ChannelMessageType, ChannelTranscript, TranscriptSignature, UnsignedChannelMessage, UnsignedChannelTranscript, } from "./types";
10
+ export { CHANNEL_MESSAGE_DOMAIN_PREFIX, TRANSCRIPT_DOMAIN_PREFIX, channelMessageSigningBytes, envelopeHashHex, signatureFromHex, signatureToHex, stripChannelMessageSignature, stripTranscriptSignatures, transcriptHashHex, transcriptSigningBytes, } from "./canonical";
11
+ export { isSenderInMembers, signChannelMessage, verifyChannelMessage, } from "./envelope";
12
+ export { InMemoryChannelIdRegistry, type ChannelIdRegistry, } from "./channelIdRegistry";
13
+ export { ChannelSession, type ChannelSessionOpts } from "./session";
14
+ export { buildUnsignedTranscript, exportTranscript, signTranscript, verifyTranscript, type TranscriptVerificationResult, } from "./transcript";
15
+ export { L2PSChannelTransport, type L2PSChannelTransportOpts, type ChannelSessionLike, type L2PSMessagingPeerLike, type SerializedEncryptedMessage, type IncomingMessagePayload, } from "./transport";
16
+ export { RfqSession, type RfqState, type RfqOutcome, type RfqSessionOpts, type RfqProposalBody, type RfqAcceptBody, type RfqEndBody, type StandingProposal, } from "./negotiate";
17
+ export { finalizeRfq, type FinalizeRfqOpts, type RfqFinalizeResult, type RfqLike, type ChannelSessionView, } from "./finalize";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * DACS-3 §8.3.3 / §8.7 — channel message envelope, session-level
3
+ * sequence + channelId enforcement, transcript export (SR-4 WI-2).
4
+ *
5
+ * All signatures use the sender's primary-claim key (on Demos: the
6
+ * connected Demos Ed25519 keypair) via WI-0's `signWithPrimaryClaim`;
7
+ * never the RSA L2PS subnet key.
8
+ */
9
+ export { CHANNEL_MESSAGE_DOMAIN_PREFIX, TRANSCRIPT_DOMAIN_PREFIX, channelMessageSigningBytes, envelopeHashHex, signatureFromHex, signatureToHex, stripChannelMessageSignature, stripTranscriptSignatures, transcriptHashHex, transcriptSigningBytes, } from "./canonical.js";
10
+ export { isSenderInMembers, signChannelMessage, verifyChannelMessage, } from "./envelope.js";
11
+ export { InMemoryChannelIdRegistry, } from "./channelIdRegistry.js";
12
+ export { ChannelSession } from "./session.js";
13
+ export { buildUnsignedTranscript, exportTranscript, signTranscript, verifyTranscript, } from "./transcript.js";
14
+ export { L2PSChannelTransport, } from "./transport.js";
15
+ export { RfqSession, } from "./negotiate.js";
16
+ export { finalizeRfq, } from "./finalize.js";
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/l2ps/channel/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,OAAO,EACH,6BAA6B,EAC7B,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,4BAA4B,EAC5B,yBAAyB,EACzB,iBAAiB,EACjB,sBAAsB,GACzB,MAAM,aAAa,CAAA;AAEpB,OAAO,EACH,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACH,yBAAyB,GAE5B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,cAAc,EAA2B,MAAM,WAAW,CAAA;AAEnE,OAAO,EACH,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,GAEnB,MAAM,cAAc,CAAA;AAErB,OAAO,EACH,oBAAoB,GAMvB,MAAM,aAAa,CAAA;AAEpB,OAAO,EACH,UAAU,GAQb,MAAM,aAAa,CAAA;AAEpB,OAAO,EACH,WAAW,GAKd,MAAM,YAAY,CAAA"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * SR-4 WI-B — `negotiate-rfq` phase logic.
3
+ *
4
+ * A request-for-quote negotiation is a turn-based exchange of `offer` /
5
+ * `counter` proposals terminating in `accept`, `reject`, or `abort`
6
+ * (CH-5 termination; DACS-3 §8.3). Per the SR-4 brief the RFQ message
7
+ * *bodies* are implementation-defined (only sealed-envelope bodies are
8
+ * spec-locked, §8.4.3), so this layer fixes a minimal body schema and
9
+ * the state machine while staying agnostic to the actual `terms`.
10
+ *
11
+ * `RfqSession` is a pure protocol state machine. It does not move bytes:
12
+ * the caller wires its `send` callback to a transport (e.g.
13
+ * `L2PSChannelTransport.send`) and feeds verified inbound envelopes to
14
+ * `onIncoming`. By the time a message reaches `onIncoming` the channel
15
+ * layer has already verified the signature, `sender ∈ members`, the
16
+ * channelId, and monotonic sequence — so this layer only enforces
17
+ * protocol-level rules (legal transitions, body shape, referencing an
18
+ * existing offer).
19
+ */
20
+ import type { ChannelMessage, ChannelMessageType } from "./types";
21
+ import type { ClaimReference } from "../../identity/cci";
22
+ /** Terminal + in-progress states (CH-5). */
23
+ export type RfqState = "open" | "accepted" | "rejected" | "aborted";
24
+ /** Body of an `offer` / `counter` — `terms` is opaque (impl-defined). */
25
+ export interface RfqProposalBody {
26
+ terms: unknown;
27
+ }
28
+ /** Body of an `accept` — references the proposal sequence being accepted. */
29
+ export interface RfqAcceptBody {
30
+ acceptedSequence: number;
31
+ }
32
+ /** Body of a `reject` / `abort`. */
33
+ export interface RfqEndBody {
34
+ reason?: string;
35
+ }
36
+ /** A standing proposal on the table. */
37
+ export interface StandingProposal {
38
+ sequence: number;
39
+ sender: ClaimReference;
40
+ terms: unknown;
41
+ }
42
+ export interface RfqOutcome {
43
+ state: RfqState;
44
+ /** Set when `state === "accepted"`: the terms that were agreed. */
45
+ agreedTerms?: unknown;
46
+ /** Sequence of the proposal that was accepted. */
47
+ acceptedSequence?: number;
48
+ /** Reason for a reject / abort, if given. */
49
+ reason?: string;
50
+ }
51
+ export interface RfqSessionOpts {
52
+ /** This party's CCI primary claim. */
53
+ me: ClaimReference;
54
+ /**
55
+ * Delivers a signed channel message. Wire to the transport's send
56
+ * (e.g. `L2PSChannelTransport.send`). Returns the signed envelope so
57
+ * the session can track the sequence it produced.
58
+ */
59
+ send: (opts: {
60
+ type: ChannelMessageType;
61
+ body: unknown;
62
+ repliesTo?: number;
63
+ }) => Promise<ChannelMessage>;
64
+ /** Fired on every state transition (including the terminal one). */
65
+ onStateChange?: (outcome: RfqOutcome) => void;
66
+ /** Fired when a fresh proposal (offer/counter) lands, ours or theirs. */
67
+ onProposal?: (proposal: StandingProposal) => void;
68
+ }
69
+ export declare class RfqSession {
70
+ private readonly me;
71
+ private readonly sendFn;
72
+ private readonly onStateChange?;
73
+ private readonly onProposal?;
74
+ private _state;
75
+ private _standing;
76
+ /** Every proposal seen, by sequence — so `accept` can resolve its terms. */
77
+ private readonly proposals;
78
+ private _outcome;
79
+ constructor(opts: RfqSessionOpts);
80
+ get state(): RfqState;
81
+ /** The proposal currently on the table, or null before the first offer. */
82
+ get standingProposal(): StandingProposal | null;
83
+ outcome(): RfqOutcome;
84
+ /** Open a negotiation with the first proposal. */
85
+ offer(terms: unknown): Promise<ChannelMessage>;
86
+ /** Counter the standing proposal with new terms. */
87
+ counter(terms: unknown): Promise<ChannelMessage>;
88
+ /** Accept the standing proposal — terminal (CH-5). */
89
+ accept(): Promise<ChannelMessage>;
90
+ /** Reject the negotiation — terminal (CH-5). */
91
+ reject(reason?: string): Promise<ChannelMessage>;
92
+ /** Abort the negotiation — terminal (CH-5). */
93
+ abort(reason?: string): Promise<ChannelMessage>;
94
+ /**
95
+ * Feed a verified inbound channel message. No-op for non-RFQ types or
96
+ * messages arriving after a terminal state (a late duplicate of an
97
+ * end message). Throws only on a protocol violation that the channel
98
+ * layer can't catch (e.g. accept referencing an unknown proposal).
99
+ */
100
+ onIncoming(msg: ChannelMessage): void;
101
+ /** Record a verified inbound offer/counter as the standing proposal. */
102
+ private acceptProposal;
103
+ private proposeOutgoing;
104
+ private settle;
105
+ private assertOpen;
106
+ }
@@ -0,0 +1,206 @@
1
+ /**
2
+ * SR-4 WI-B — `negotiate-rfq` phase logic.
3
+ *
4
+ * A request-for-quote negotiation is a turn-based exchange of `offer` /
5
+ * `counter` proposals terminating in `accept`, `reject`, or `abort`
6
+ * (CH-5 termination; DACS-3 §8.3). Per the SR-4 brief the RFQ message
7
+ * *bodies* are implementation-defined (only sealed-envelope bodies are
8
+ * spec-locked, §8.4.3), so this layer fixes a minimal body schema and
9
+ * the state machine while staying agnostic to the actual `terms`.
10
+ *
11
+ * `RfqSession` is a pure protocol state machine. It does not move bytes:
12
+ * the caller wires its `send` callback to a transport (e.g.
13
+ * `L2PSChannelTransport.send`) and feeds verified inbound envelopes to
14
+ * `onIncoming`. By the time a message reaches `onIncoming` the channel
15
+ * layer has already verified the signature, `sender ∈ members`, the
16
+ * channelId, and monotonic sequence — so this layer only enforces
17
+ * protocol-level rules (legal transitions, body shape, referencing an
18
+ * existing offer).
19
+ */
20
+ const RFQ_TYPES = new Set([
21
+ "offer",
22
+ "counter",
23
+ "accept",
24
+ "reject",
25
+ "abort",
26
+ ]);
27
+ export class RfqSession {
28
+ constructor(opts) {
29
+ this._state = "open";
30
+ this._standing = null;
31
+ /** Every proposal seen, by sequence — so `accept` can resolve its terms. */
32
+ this.proposals = new Map();
33
+ this._outcome = { state: "open" };
34
+ this.me = opts.me;
35
+ this.sendFn = opts.send;
36
+ this.onStateChange = opts.onStateChange;
37
+ this.onProposal = opts.onProposal;
38
+ }
39
+ get state() {
40
+ return this._state;
41
+ }
42
+ /** The proposal currently on the table, or null before the first offer. */
43
+ get standingProposal() {
44
+ return this._standing;
45
+ }
46
+ outcome() {
47
+ return this._outcome;
48
+ }
49
+ /** Open a negotiation with the first proposal. */
50
+ async offer(terms) {
51
+ this.assertOpen("offer");
52
+ if (this._standing)
53
+ throw new Error("RfqSession: a proposal already stands; use counter()");
54
+ return this.proposeOutgoing("offer", terms);
55
+ }
56
+ /** Counter the standing proposal with new terms. */
57
+ async counter(terms) {
58
+ this.assertOpen("counter");
59
+ if (!this._standing)
60
+ throw new Error("RfqSession: nothing to counter — no standing offer");
61
+ return this.proposeOutgoing("counter", terms, this._standing.sequence);
62
+ }
63
+ /** Accept the standing proposal — terminal (CH-5). */
64
+ async accept() {
65
+ this.assertOpen("accept");
66
+ if (!this._standing)
67
+ throw new Error("RfqSession: nothing to accept — no standing offer");
68
+ // You cannot accept your own proposal: acceptance is the
69
+ // counterparty agreeing to the terms on the table. Accepting a
70
+ // self-authored standing proposal would settle the negotiation
71
+ // (and produce a transcript) without the other side ever agreeing.
72
+ if (this._standing.sender === this.me)
73
+ throw new Error("RfqSession: cannot accept your own proposal — wait for the counterparty");
74
+ const accepted = this._standing;
75
+ const body = { acceptedSequence: accepted.sequence };
76
+ const msg = await this.sendFn({
77
+ type: "accept",
78
+ body,
79
+ repliesTo: accepted.sequence,
80
+ });
81
+ this.settle({
82
+ state: "accepted",
83
+ agreedTerms: accepted.terms,
84
+ acceptedSequence: accepted.sequence,
85
+ });
86
+ return msg;
87
+ }
88
+ /** Reject the negotiation — terminal (CH-5). */
89
+ async reject(reason) {
90
+ this.assertOpen("reject");
91
+ const body = reason ? { reason } : {};
92
+ const msg = await this.sendFn({ type: "reject", body });
93
+ this.settle({ state: "rejected", reason });
94
+ return msg;
95
+ }
96
+ /** Abort the negotiation — terminal (CH-5). */
97
+ async abort(reason) {
98
+ this.assertOpen("abort");
99
+ const body = reason ? { reason } : {};
100
+ const msg = await this.sendFn({ type: "abort", body });
101
+ this.settle({ state: "aborted", reason });
102
+ return msg;
103
+ }
104
+ /**
105
+ * Feed a verified inbound channel message. No-op for non-RFQ types or
106
+ * messages arriving after a terminal state (a late duplicate of an
107
+ * end message). Throws only on a protocol violation that the channel
108
+ * layer can't catch (e.g. accept referencing an unknown proposal).
109
+ */
110
+ onIncoming(msg) {
111
+ if (!RFQ_TYPES.has(msg.type))
112
+ return;
113
+ if (msg.sender === this.me)
114
+ return; // our own echo, already applied
115
+ if (this._state !== "open")
116
+ return; // terminal — ignore trailing traffic
117
+ switch (msg.type) {
118
+ case "offer": {
119
+ // An offer opens a negotiation: it is only legal when no
120
+ // proposal stands yet. A second inbound offer must not
121
+ // overwrite the standing proposal — `offer()` rejects the
122
+ // same case outbound, so the inbound path enforces it too.
123
+ if (this._standing)
124
+ throw new Error(`RfqSession: inbound offer (seq ${msg.sequence}) but a proposal already stands (seq ${this._standing.sequence})`);
125
+ this.acceptProposal(msg);
126
+ break;
127
+ }
128
+ case "counter": {
129
+ // A counter replaces the standing proposal — illegal before
130
+ // any offer exists, mirroring `counter()` outbound.
131
+ if (!this._standing)
132
+ throw new Error(`RfqSession: inbound counter (seq ${msg.sequence}) but no standing offer to counter`);
133
+ this.acceptProposal(msg);
134
+ break;
135
+ }
136
+ case "accept": {
137
+ const seq = msg.body?.acceptedSequence;
138
+ const accepted = this.proposals.get(seq);
139
+ if (!accepted)
140
+ throw new Error(`RfqSession: accept references unknown proposal sequence ${seq}`);
141
+ // The accept must reference the proposal currently on the
142
+ // table. Once a counter supersedes an earlier offer, that
143
+ // older offer is off the table — accepting it would settle
144
+ // obsolete terms instead of what is currently being
145
+ // negotiated.
146
+ if (!this._standing || seq !== this._standing.sequence)
147
+ throw new Error(`RfqSession: accept references stale proposal seq ${seq}; standing proposal is seq ${this._standing?.sequence ?? "none"}`);
148
+ this.settle({
149
+ state: "accepted",
150
+ agreedTerms: accepted.terms,
151
+ acceptedSequence: seq,
152
+ });
153
+ break;
154
+ }
155
+ case "reject": {
156
+ this.settle({
157
+ state: "rejected",
158
+ reason: msg.body?.reason,
159
+ });
160
+ break;
161
+ }
162
+ case "abort": {
163
+ this.settle({
164
+ state: "aborted",
165
+ reason: msg.body?.reason,
166
+ });
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ /** Record a verified inbound offer/counter as the standing proposal. */
172
+ acceptProposal(msg) {
173
+ const terms = msg.body?.terms;
174
+ const proposal = {
175
+ sequence: msg.sequence,
176
+ sender: msg.sender,
177
+ terms,
178
+ };
179
+ this.proposals.set(msg.sequence, proposal);
180
+ this._standing = proposal;
181
+ this.onProposal?.(proposal);
182
+ }
183
+ async proposeOutgoing(type, terms, repliesTo) {
184
+ const body = { terms };
185
+ const msg = await this.sendFn({ type, body, repliesTo });
186
+ const proposal = {
187
+ sequence: msg.sequence,
188
+ sender: this.me,
189
+ terms,
190
+ };
191
+ this.proposals.set(msg.sequence, proposal);
192
+ this._standing = proposal;
193
+ this.onProposal?.(proposal);
194
+ return msg;
195
+ }
196
+ settle(outcome) {
197
+ this._state = outcome.state;
198
+ this._outcome = outcome;
199
+ this.onStateChange?.(outcome);
200
+ }
201
+ assertOpen(action) {
202
+ if (this._state !== "open")
203
+ throw new Error(`RfqSession: cannot ${action} — negotiation is ${this._state}`);
204
+ }
205
+ }
206
+ //# sourceMappingURL=negotiate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiate.js","sourceRoot":"","sources":["../../../../src/l2ps/channel/negotiate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { Demos } from "../../websdk/demosclass";
2
+ import type { ClaimReference } from "../../identity/cci";
3
+ import type { ChannelMessage, ChannelMessageType } from "./types";
4
+ import type { ChannelIdRegistry } from "./channelIdRegistry";
5
+ export interface ChannelSessionOpts {
6
+ channelId: string;
7
+ members: ClaimReference[];
8
+ /** This party's primary claim — must be in `members`. */
9
+ me: ClaimReference;
10
+ demos: Demos;
11
+ /**
12
+ * Optional CH-6 enforcement. If provided, `open()` registers the
13
+ * channelId here and throws on reuse.
14
+ */
15
+ channelIdRegistry?: ChannelIdRegistry;
16
+ }
17
+ /**
18
+ * Per-channel session — owns the monotonic sequence counter and applies
19
+ * the four invariants from §8.3.3 / §8.12 on every message:
20
+ *
21
+ * 1. signature verifies under sender's CCI key
22
+ * 2. sender ∈ members (CH-1)
23
+ * 3. sequence is monotonic per channel (anti-replay)
24
+ * 4. channelId matches the session's channelId
25
+ *
26
+ * A single counter is shared across senders, as the brief specifies
27
+ * (`sequence: number // monotonic per channel, starts at 1`). Outgoing
28
+ * messages get `current + 1`; incoming messages must use a sequence
29
+ * strictly greater than every value already seen.
30
+ */
31
+ export declare class ChannelSession {
32
+ readonly channelId: string;
33
+ readonly members: ReadonlyArray<ClaimReference>;
34
+ readonly me: ClaimReference;
35
+ private readonly demos;
36
+ private readonly registry;
37
+ private opened;
38
+ private highestSeen;
39
+ private readonly transcript;
40
+ constructor(opts: ChannelSessionOpts);
41
+ /**
42
+ * Register the channelId (CH-6) and prepare for messaging. Idempotent
43
+ * within a session — calling twice throws via the registry.
44
+ */
45
+ open(): Promise<void>;
46
+ /**
47
+ * Build, sign, append-to-local-transcript, and return the next
48
+ * outgoing `ChannelMessage`. Sequence is `highestSeen + 1` so it is
49
+ * monotonic across both directions.
50
+ */
51
+ sendOutgoing(opts: {
52
+ type: ChannelMessageType;
53
+ body: unknown;
54
+ sentAt?: number;
55
+ repliesTo?: number;
56
+ }): Promise<ChannelMessage>;
57
+ /**
58
+ * Validate an incoming `ChannelMessage`. Throws with a precise reason
59
+ * on any failure — caller should treat throw as channel-fatal per
60
+ * §8.12 (a single tampered message invalidates the session's record).
61
+ */
62
+ receiveIncoming(msg: ChannelMessage): Promise<void>;
63
+ /** Read-only snapshot of all messages this session has signed or accepted. */
64
+ messages(): ReadonlyArray<ChannelMessage>;
65
+ private assertOpened;
66
+ }
@@ -0,0 +1,101 @@
1
+ import { isSenderInMembers, signChannelMessage, verifyChannelMessage, } from "./envelope.js";
2
+ /**
3
+ * Per-channel session — owns the monotonic sequence counter and applies
4
+ * the four invariants from §8.3.3 / §8.12 on every message:
5
+ *
6
+ * 1. signature verifies under sender's CCI key
7
+ * 2. sender ∈ members (CH-1)
8
+ * 3. sequence is monotonic per channel (anti-replay)
9
+ * 4. channelId matches the session's channelId
10
+ *
11
+ * A single counter is shared across senders, as the brief specifies
12
+ * (`sequence: number // monotonic per channel, starts at 1`). Outgoing
13
+ * messages get `current + 1`; incoming messages must use a sequence
14
+ * strictly greater than every value already seen.
15
+ */
16
+ export class ChannelSession {
17
+ constructor(opts) {
18
+ this.opened = false;
19
+ this.highestSeen = 0;
20
+ this.transcript = [];
21
+ if (!opts.channelId)
22
+ throw new Error("ChannelSession: channelId required");
23
+ if (!opts.members?.length)
24
+ throw new Error("ChannelSession: members required");
25
+ if (!opts.members.includes(opts.me))
26
+ throw new Error(`ChannelSession: me (${opts.me}) is not in members`);
27
+ this.channelId = opts.channelId;
28
+ this.members = [...opts.members];
29
+ this.me = opts.me;
30
+ this.demos = opts.demos;
31
+ this.registry = opts.channelIdRegistry;
32
+ }
33
+ /**
34
+ * Register the channelId (CH-6) and prepare for messaging. Idempotent
35
+ * within a session — calling twice throws via the registry.
36
+ */
37
+ async open() {
38
+ if (this.opened)
39
+ throw new Error("ChannelSession: already opened");
40
+ if (this.registry)
41
+ await this.registry.register(this.channelId);
42
+ this.opened = true;
43
+ }
44
+ /**
45
+ * Build, sign, append-to-local-transcript, and return the next
46
+ * outgoing `ChannelMessage`. Sequence is `highestSeen + 1` so it is
47
+ * monotonic across both directions.
48
+ */
49
+ async sendOutgoing(opts) {
50
+ this.assertOpened();
51
+ // Reserve the sequence slot SYNCHRONOUSLY before any await — two
52
+ // concurrent sendOutgoing() calls would otherwise read the same
53
+ // `highestSeen`, sign two messages with identical sequence
54
+ // numbers, and silently violate the anti-replay invariant on the
55
+ // receiver side.
56
+ const sequence = ++this.highestSeen;
57
+ const unsigned = {
58
+ channelId: this.channelId,
59
+ sequence,
60
+ sender: this.me,
61
+ sentAt: opts.sentAt ?? Date.now(),
62
+ type: opts.type,
63
+ body: opts.body,
64
+ ...(opts.repliesTo !== undefined && {
65
+ refs: { repliesTo: opts.repliesTo },
66
+ }),
67
+ };
68
+ const signed = await signChannelMessage(unsigned, this.demos);
69
+ this.transcript.push(signed);
70
+ return signed;
71
+ }
72
+ /**
73
+ * Validate an incoming `ChannelMessage`. Throws with a precise reason
74
+ * on any failure — caller should treat throw as channel-fatal per
75
+ * §8.12 (a single tampered message invalidates the session's record).
76
+ */
77
+ async receiveIncoming(msg) {
78
+ this.assertOpened();
79
+ if (msg.channelId !== this.channelId)
80
+ throw new Error(`ChannelSession: channelId mismatch (expected ${this.channelId}, got ${msg.channelId})`);
81
+ if (!isSenderInMembers(msg, this.members))
82
+ throw new Error(`ChannelSession: sender "${msg.sender}" not in members (CH-1)`);
83
+ if (!Number.isInteger(msg.sequence) || msg.sequence < 1)
84
+ throw new Error(`ChannelSession: invalid sequence ${msg.sequence}`);
85
+ if (msg.sequence <= this.highestSeen)
86
+ throw new Error(`ChannelSession: non-monotonic sequence ${msg.sequence} (highest seen ${this.highestSeen})`);
87
+ if (!verifyChannelMessage(msg))
88
+ throw new Error("ChannelSession: signature verification failed");
89
+ this.highestSeen = msg.sequence;
90
+ this.transcript.push(msg);
91
+ }
92
+ /** Read-only snapshot of all messages this session has signed or accepted. */
93
+ messages() {
94
+ return this.transcript;
95
+ }
96
+ assertOpened() {
97
+ if (!this.opened)
98
+ throw new Error("ChannelSession: call open() before sending or receiving");
99
+ }
100
+ }
101
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../../src/l2ps/channel/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { type ClaimReference } from "../../identity/cci";
2
+ import { Demos } from "../../websdk/demosclass";
3
+ import type { ChannelMessage, ChannelTranscript, TranscriptSignature, UnsignedChannelTranscript } from "./types";
4
+ /**
5
+ * Build the unsigned transcript shell. Messages must already be in
6
+ * sequence order — typically `session.messages()`.
7
+ */
8
+ export declare function buildUnsignedTranscript(opts: {
9
+ channelId: string;
10
+ members: ClaimReference[];
11
+ messages: ReadonlyArray<ChannelMessage>;
12
+ generatedAt?: number;
13
+ }): UnsignedChannelTranscript;
14
+ /**
15
+ * Produce one party's `TranscriptSignature` over the unsigned transcript.
16
+ * Domain-separated under `dacs-transcript:v1:` — cannot be confused with
17
+ * a channel-message or binding signature.
18
+ */
19
+ export declare function signTranscript(unsigned: UnsignedChannelTranscript, signer: ClaimReference, demos: Demos): Promise<TranscriptSignature>;
20
+ /**
21
+ * Convenience: assemble the full transcript with one or more party
22
+ * signatures. Pass signers in the order their signatures should appear.
23
+ */
24
+ export declare function exportTranscript(opts: {
25
+ channelId: string;
26
+ members: ClaimReference[];
27
+ messages: ReadonlyArray<ChannelMessage>;
28
+ signers: Array<{
29
+ claim: ClaimReference;
30
+ demos: Demos;
31
+ }>;
32
+ generatedAt?: number;
33
+ }): Promise<ChannelTranscript>;
34
+ export interface TranscriptVerificationResult {
35
+ ok: boolean;
36
+ errors: string[];
37
+ }
38
+ /**
39
+ * Verify every claim about a transcript a non-member can check given only
40
+ * the public keys: (a) each `TranscriptSignature` is valid, (b) each
41
+ * `ChannelMessage` carries a valid signature, (c) every message's sender
42
+ * is in `members`, (d) sequences are strictly monotonic per channel,
43
+ * (e) every message's `channelId` matches the transcript.
44
+ *
45
+ * Returns a list of errors rather than throwing so auditors can collect
46
+ * every failure in one pass.
47
+ */
48
+ export declare function verifyTranscript(t: ChannelTranscript): TranscriptVerificationResult;