p2party 0.8.0 → 0.14.2

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 (182) hide show
  1. package/LICENSE.md +202 -661
  2. package/README.md +736 -404
  3. package/THIRD_PARTY_NOTICES.md +458 -0
  4. package/docs/assets/p2party-cat.svg +1 -0
  5. package/docs/getting-started.md +203 -0
  6. package/docs/protocol-v4-security.md +158 -0
  7. package/docs/references.md +157 -0
  8. package/docs/session-api.md +457 -0
  9. package/docs/wire-format.md +103 -0
  10. package/examples/standalone-e2ee.ts +194 -0
  11. package/lib/api/signalingServerApi.d.ts +6 -7
  12. package/lib/api/webrtc/baseQuery.d.ts +1 -2
  13. package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts +1 -2
  14. package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts +1 -2
  15. package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts +1 -2
  16. package/lib/api/webrtc/disconnectFromPeerQuery.d.ts +3 -3
  17. package/lib/api/webrtc/disconnectFromRoomQuery.d.ts +1 -2
  18. package/lib/api/webrtc/disconnectQuery.d.ts +1 -2
  19. package/lib/api/webrtc/iceGeneration.d.ts +17 -0
  20. package/lib/api/webrtc/iceRepair.d.ts +10 -0
  21. package/lib/api/webrtc/index.d.ts +13 -13
  22. package/lib/api/webrtc/interfaces.d.ts +67 -11
  23. package/lib/api/webrtc/negotiationLock.d.ts +12 -0
  24. package/lib/api/webrtc/openChannelQuery.d.ts +1 -2
  25. package/lib/api/webrtc/pendingIceCandidates.d.ts +14 -0
  26. package/lib/api/webrtc/roomPeer.d.ts +11 -0
  27. package/lib/api/webrtc/sendMessageQuery.d.ts +7 -4
  28. package/lib/api/webrtc/setCandidateQuery.d.ts +1 -2
  29. package/lib/api/webrtc/setDescriptionQuery.d.ts +1 -2
  30. package/lib/cryptography/coverCell.d.ts +65 -0
  31. package/lib/cryptography/cpace.d.ts +41 -0
  32. package/lib/cryptography/ed25519.d.ts +0 -1
  33. package/lib/cryptography/hashStream.d.ts +15 -0
  34. package/lib/cryptography/hkdf.d.ts +3 -0
  35. package/lib/cryptography/identityCrossSig.d.ts +7 -0
  36. package/lib/cryptography/interfaces.d.ts +28 -13
  37. package/lib/cryptography/memory.d.ts +2 -4
  38. package/lib/cryptography/merkle.d.ts +0 -1
  39. package/lib/cryptography/mlkem.d.ts +117 -0
  40. package/lib/cryptography/mnemonic.d.ts +0 -1
  41. package/lib/cryptography/pqHealing.d.ts +268 -0
  42. package/lib/cryptography/pqHealingFrame.d.ts +39 -0
  43. package/lib/cryptography/pqMessageKey.d.ts +30 -0
  44. package/lib/cryptography/random.d.ts +22 -0
  45. package/lib/cryptography/ratchet.d.ts +146 -0
  46. package/lib/cryptography/testModule.d.ts +7 -0
  47. package/lib/cryptography/utils.d.ts +0 -1
  48. package/lib/cryptography/wasmLoader.d.ts +6 -1
  49. package/lib/cryptography/x25519.d.ts +15 -0
  50. package/lib/cryptography/x3dh.d.ts +9 -0
  51. package/lib/db/api.d.ts +54 -22
  52. package/lib/db/db.worker.d.ts +0 -1
  53. package/lib/db/ratchetWrap.d.ts +36 -0
  54. package/lib/db/src/getDB.d.ts +17 -4
  55. package/lib/db/types.d.ts +274 -32
  56. package/lib/db.worker.js +1 -2
  57. package/lib/handlers/chunkFrame.d.ts +13 -0
  58. package/lib/handlers/connectionSignal.d.ts +26 -0
  59. package/lib/handlers/coverEdge.d.ts +42 -0
  60. package/lib/handlers/coverRuntime.d.ts +91 -0
  61. package/lib/handlers/coverScheduler.d.ts +184 -0
  62. package/lib/handlers/coverTransfer.d.ts +46 -0
  63. package/lib/handlers/frameType.d.ts +10 -0
  64. package/lib/handlers/handleChallenge.d.ts +0 -1
  65. package/lib/handlers/handleConnectToPeer.d.ts +0 -1
  66. package/lib/handlers/handleHandshake.d.ts +74 -0
  67. package/lib/handlers/handleMessageQueueing.d.ts +53 -4
  68. package/lib/handlers/handleOpenChannel.d.ts +4 -2
  69. package/lib/handlers/handleQueuedIceCandidates.d.ts +0 -1
  70. package/lib/handlers/handleReadReceipt.d.ts +17 -4
  71. package/lib/handlers/handleReceiveMessage.d.ts +21 -3
  72. package/lib/handlers/handleSendMessage.d.ts +110 -2
  73. package/lib/handlers/handleWebSocketMessage.d.ts +1 -3
  74. package/lib/handlers/handshakeCore.d.ts +95 -0
  75. package/lib/handlers/messageChunkCrypto.d.ts +70 -0
  76. package/lib/handlers/peerRosterDelta.d.ts +12 -0
  77. package/lib/handlers/pqHealingOrchestrator.d.ts +46 -0
  78. package/lib/handlers/pqHealingRuntime.d.ts +82 -0
  79. package/lib/handlers/ratchetGate.d.ts +18 -0
  80. package/lib/handlers/ratchetPersist.d.ts +53 -0
  81. package/lib/handlers/receiptFrame.d.ts +18 -0
  82. package/lib/handlers/receiveMessageKeyLifetime.d.ts +24 -0
  83. package/lib/handlers/reconcile.d.ts +16 -0
  84. package/lib/handlers/transferAbort.d.ts +42 -0
  85. package/lib/index.d.ts +203 -46
  86. package/lib/index.js +1 -2
  87. package/lib/index.min.js +1 -2
  88. package/lib/index.mjs +1 -2
  89. package/lib/libcrypto.provenance.json +45 -0
  90. package/lib/libcrypto.wasm +0 -0
  91. package/lib/middleware/keyPairListenerMiddleware.d.ts +0 -1
  92. package/lib/middleware/roomListenerMiddleware.d.ts +0 -1
  93. package/lib/reducers/commonSlice.d.ts +0 -1
  94. package/lib/reducers/keyPairSlice.d.ts +0 -1
  95. package/lib/reducers/roomSlice.d.ts +65 -3
  96. package/lib/reducers/signalingServerSlice.d.ts +25 -4
  97. package/lib/roomInvite.d.ts +18 -0
  98. package/lib/roomPinAttempts.d.ts +12 -0
  99. package/lib/roomPinVault.d.ts +26 -0
  100. package/lib/roomPolicy.d.ts +60 -0
  101. package/lib/session.d.ts +110 -0
  102. package/lib/session.js +1 -0
  103. package/lib/session.mjs +1 -0
  104. package/lib/store.d.ts +40 -41
  105. package/lib/utils/channelLabel.d.ts +0 -1
  106. package/lib/utils/chunkBounds.d.ts +9 -0
  107. package/lib/utils/constants.d.ts +50 -6
  108. package/lib/utils/debug.d.ts +25 -0
  109. package/lib/utils/drainAndClose.d.ts +5 -0
  110. package/lib/utils/identityRole.d.ts +19 -0
  111. package/lib/utils/interfaces.d.ts +31 -43
  112. package/lib/utils/leafHash.d.ts +13 -0
  113. package/lib/utils/messageTypes.d.ts +0 -1
  114. package/lib/utils/metadata.d.ts +6 -1
  115. package/lib/utils/mutex.d.ts +24 -0
  116. package/lib/utils/protocolVersion.d.ts +5 -0
  117. package/lib/utils/receiptToken.d.ts +7 -0
  118. package/lib/utils/sendQueueKey.d.ts +2 -0
  119. package/lib/utils/signalingAuth.d.ts +8 -0
  120. package/lib/utils/signalingBounds.d.ts +9 -0
  121. package/lib/utils/splitToChunks.d.ts +7 -4
  122. package/lib/utils/uint8array.d.ts +1 -2
  123. package/lib/utils/waitForOpen.d.ts +3 -0
  124. package/lib/utils/zeroFree.d.ts +10 -0
  125. package/package.json +109 -49
  126. package/lib/api/signalingServerApi.d.ts.map +0 -1
  127. package/lib/api/webrtc/baseQuery.d.ts.map +0 -1
  128. package/lib/api/webrtc/disconnectFromAllRoomsQuery.d.ts.map +0 -1
  129. package/lib/api/webrtc/disconnectFromChannelLabelQuery.d.ts.map +0 -1
  130. package/lib/api/webrtc/disconnectFromPeerChannelLabelQuery.d.ts.map +0 -1
  131. package/lib/api/webrtc/disconnectFromPeerQuery.d.ts.map +0 -1
  132. package/lib/api/webrtc/disconnectFromRoomQuery.d.ts.map +0 -1
  133. package/lib/api/webrtc/disconnectQuery.d.ts.map +0 -1
  134. package/lib/api/webrtc/index.d.ts.map +0 -1
  135. package/lib/api/webrtc/interfaces.d.ts.map +0 -1
  136. package/lib/api/webrtc/openChannelQuery.d.ts.map +0 -1
  137. package/lib/api/webrtc/sendMessageQuery.d.ts.map +0 -1
  138. package/lib/api/webrtc/setCandidateQuery.d.ts.map +0 -1
  139. package/lib/api/webrtc/setDescriptionQuery.d.ts.map +0 -1
  140. package/lib/cryptography/chacha20poly1305.d.ts +0 -116
  141. package/lib/cryptography/chacha20poly1305.d.ts.map +0 -1
  142. package/lib/cryptography/ed25519.d.ts.map +0 -1
  143. package/lib/cryptography/interfaces.d.ts.map +0 -1
  144. package/lib/cryptography/memory.d.ts.map +0 -1
  145. package/lib/cryptography/merkle.d.ts.map +0 -1
  146. package/lib/cryptography/mnemonic.d.ts.map +0 -1
  147. package/lib/cryptography/utils.d.ts.map +0 -1
  148. package/lib/cryptography/wasmLoader.d.ts.map +0 -1
  149. package/lib/db/api.d.ts.map +0 -1
  150. package/lib/db/db.worker.d.ts.map +0 -1
  151. package/lib/db/src/getDB.d.ts.map +0 -1
  152. package/lib/db/types.d.ts.map +0 -1
  153. package/lib/db.worker.js.map +0 -1
  154. package/lib/handlers/handleChallenge.d.ts.map +0 -1
  155. package/lib/handlers/handleConnectToPeer.d.ts.map +0 -1
  156. package/lib/handlers/handleMessageQueueing.d.ts.map +0 -1
  157. package/lib/handlers/handleOpenChannel.d.ts.map +0 -1
  158. package/lib/handlers/handleQueuedIceCandidates.d.ts.map +0 -1
  159. package/lib/handlers/handleReadReceipt.d.ts.map +0 -1
  160. package/lib/handlers/handleReceiveMessage.d.ts.map +0 -1
  161. package/lib/handlers/handleSendMessage.d.ts.map +0 -1
  162. package/lib/handlers/handleWebSocketMessage.d.ts.map +0 -1
  163. package/lib/index.d.ts.map +0 -1
  164. package/lib/index.js.map +0 -1
  165. package/lib/index.min.js.map +0 -1
  166. package/lib/index.mjs.map +0 -1
  167. package/lib/middleware/keyPairListenerMiddleware.d.ts.map +0 -1
  168. package/lib/middleware/roomListenerMiddleware.d.ts.map +0 -1
  169. package/lib/reducers/commonSlice.d.ts.map +0 -1
  170. package/lib/reducers/keyPairSlice.d.ts.map +0 -1
  171. package/lib/reducers/roomSlice.d.ts.map +0 -1
  172. package/lib/reducers/signalingServerSlice.d.ts.map +0 -1
  173. package/lib/store.d.ts.map +0 -1
  174. package/lib/utils/allocators.d.ts +0 -22
  175. package/lib/utils/allocators.d.ts.map +0 -1
  176. package/lib/utils/channelLabel.d.ts.map +0 -1
  177. package/lib/utils/constants.d.ts.map +0 -1
  178. package/lib/utils/interfaces.d.ts.map +0 -1
  179. package/lib/utils/messageTypes.d.ts.map +0 -1
  180. package/lib/utils/metadata.d.ts.map +0 -1
  181. package/lib/utils/splitToChunks.d.ts.map +0 -1
  182. package/lib/utils/uint8array.d.ts.map +0 -1
@@ -0,0 +1,42 @@
1
+ import type { CoverStatusChange } from "./coverScheduler";
2
+ import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
3
+ import type { RoomPolicyV1 } from "../roomPolicy";
4
+ import type { LibCrypto } from "../cryptography/libcrypto";
5
+ export interface InstallCoverEdgeOptions {
6
+ readonly epc: IRTCPeerConnection;
7
+ readonly roomId: string;
8
+ readonly policy: RoomPolicyV1;
9
+ readonly policyHash: Uint8Array;
10
+ readonly amInitiator: boolean;
11
+ readonly module: LibCrypto;
12
+ readonly onRemoteCancel?: (merkleRootHex: string) => void;
13
+ readonly onStatusChange?: (change: CoverStatusChange) => void;
14
+ }
15
+ /**
16
+ * Stop scheduled cover on an edge and release its lanes.
17
+ *
18
+ * destroy() emits the terminal "stopped" status, so any observer of
19
+ * rooms[].coverStatusByPeer stops claiming cover for this edge. Safe to call
20
+ * on an edge that never had a runtime.
21
+ */
22
+ export declare const teardownCoverEdge: (epc: IRTCPeerConnection) => void;
23
+ /**
24
+ * Construct and START the scheduled-cover runtime for one authenticated edge.
25
+ * Called after `runHandshake` on a `coverMode: "scheduled"` room. Idempotent
26
+ * per connection: a replacement handshake destroys the previous runtime first.
27
+ */
28
+ export declare const installCoverEdge: (options: InstallCoverEdgeOptions) => void;
29
+ /** Record the root→transferId mapping so a terminal receipt settles the send. */
30
+ export declare const trackScheduledSend: (epc: IRTCPeerConnection, roomId: string, peerId: string, merkleRootHex: string, transferId: string) => void;
31
+ export interface ScheduledSendParams {
32
+ readonly epc: IRTCPeerConnection;
33
+ readonly channelMessageLabel: string;
34
+ readonly totalChunks: number;
35
+ readonly sealSlotCell: (chunkIndex: number) => Promise<Uint8Array | null>;
36
+ readonly getAckedChunks?: () => ReadonlySet<number>;
37
+ }
38
+ /**
39
+ * Enqueue one message as a lazy scheduled-send job on this edge's cover
40
+ * runtime. Returns false when the edge has no cover runtime (immediate mode).
41
+ */
42
+ export declare const enqueueScheduledSend: (params: ScheduledSendParams) => boolean;
@@ -0,0 +1,91 @@
1
+ import { type CoverClock, type CoverJob, type CoverJobInterruption, type CoverJobResult, type CoverSchedule, type CoverScheduler, type CoverSchedulerStatus, type CoverStatusChange } from "./coverScheduler";
2
+ import { type CoverCellContent } from "../cryptography/coverCell";
3
+ import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
4
+ import type { LibCrypto } from "../cryptography/libcrypto";
5
+ /** The transport surface a cover lane needs from an RTCDataChannel. */
6
+ export interface CoverLaneChannel {
7
+ readonly label: string;
8
+ readonly readyState: RTCDataChannelState | string;
9
+ readonly bufferedAmount: number;
10
+ send(data: ArrayBuffer): void;
11
+ close(): void;
12
+ }
13
+ export interface CoverRuntimeOptions {
14
+ readonly epc: IRTCPeerConnection;
15
+ readonly roomId: string;
16
+ readonly module: LibCrypto;
17
+ readonly amInitiator: boolean;
18
+ /** Authenticated room policy values (already validated by roomPolicy). */
19
+ readonly schedule: Omit<CoverSchedule, "phaseOffsetMs">;
20
+ /** Authenticated canonical room-policy hash: the shared phase source. */
21
+ readonly policyHash: Uint8Array;
22
+ /** Constant-shape name element shared by real and dummy lane labels. */
23
+ readonly laneLabelName: string;
24
+ /** Open one outbound lane; the caller owns RTCDataChannel construction. */
25
+ readonly openLaneChannel: (label: string) => CoverLaneChannel;
26
+ readonly onStatusChange?: (change: CoverStatusChange) => void;
27
+ readonly onJobResult?: (result: CoverJobResult) => void;
28
+ readonly onJobInterrupted?: (interruption: CoverJobInterruption) => void;
29
+ /**
30
+ * Real-timer drift tolerance. Defaults to just under one slot spacing so a
31
+ * healthy browser's setTimeout jitter does not suspend cover; a larger slip
32
+ * still degrades. Deterministic (fake-clock) tests pass 0.
33
+ */
34
+ readonly maxTimerDriftMs?: number;
35
+ /** Authenticated remote CANCEL for one transfer root (lowercase hex). */
36
+ readonly onRemoteCancel?: (merkleRootHex: string) => void;
37
+ /** Authenticated scheduled receipt token scoped to its transfer root. */
38
+ readonly onScheduledReceipt?: (merkleRootHex: string, token: Uint8Array) => void;
39
+ readonly clock?: CoverClock;
40
+ }
41
+ /**
42
+ * Every peer derives the identical absolute phase from the authenticated
43
+ * policy hash, so all edges of the room share one cycle grid without any
44
+ * negotiation message.
45
+ */
46
+ export declare const deriveCoverPhaseOffsetMs: (policyHash: Uint8Array, coverCadenceMs: number) => number;
47
+ export declare class CoverRuntime {
48
+ #private;
49
+ constructor(options: CoverRuntimeOptions);
50
+ /** Total cells one lane emits over a full cycle: F × D. */
51
+ cellsPerLanePerCycle(): number;
52
+ /**
53
+ * Fire a one-shot callback when the given job settles (completed, cancelled,
54
+ * or failed). Used to re-arm a long control backlog across cycles.
55
+ */
56
+ onJobSettled(jobId: string, callback: () => void): void;
57
+ /** A constant-shape lane label with a fresh random root (dummy/control). */
58
+ randomLaneLabel(): string;
59
+ /** starting | active | degraded | suspended | stopped */
60
+ get status(): CoverSchedulerStatus;
61
+ start(): void;
62
+ stop(): void;
63
+ destroy(): void;
64
+ /** A browser-imposed gap: close at the boundary, never claim the gap. */
65
+ suspend(reason: string): void;
66
+ /** Resume begins at a strictly future cycle boundary; gaps stay gaps. */
67
+ resume(): void;
68
+ enqueue(job: CoverJob): void;
69
+ cancel(jobId: string): ReturnType<CoverScheduler["cancel"]>;
70
+ complete(jobId: string): void;
71
+ getQueuedJobIds(): readonly string[];
72
+ /**
73
+ * Seal one authenticated non-dummy cover cell (CANCEL or scheduled receipt)
74
+ * for substitution into an already-scheduled slot.
75
+ */
76
+ sealCoverContent(content: CoverCellContent): Uint8Array;
77
+ /**
78
+ * Authenticate one inbound FRAME_TYPE_COVER cell. Dummy cells vanish;
79
+ * CANCEL/receipt dispatch to their hooks bound to the transfer root. An
80
+ * unauthentic, replayed, or wrong-epoch cell is dropped (returns false) —
81
+ * cover lanes may legitimately race an epoch transition by one flight.
82
+ */
83
+ processInboundCoverCell(frame: Uint8Array): boolean;
84
+ /**
85
+ * A dummy lane advertises a plausible same-shape label: the identical name
86
+ * element with a fresh random 64-byte "Merkle root". Labels are visible to
87
+ * the authenticated peer only — never to the signaling server or a network
88
+ * observer — so shape (not secrecy) is the requirement.
89
+ */
90
+ makeDummyLaneLabel(): Promise<string>;
91
+ }
@@ -0,0 +1,184 @@
1
+ export type CoverSchedulerStatus = "starting" | "active" | "degraded" | "suspended" | "stopped";
2
+ export type CoverJobKind = "real" | "control";
3
+ export type CoverCellSource = CoverJobKind | "cancel" | "dummy";
4
+ type MaybePromise<T> = T | PromiseLike<T>;
5
+ export interface CoverClock {
6
+ now(): number;
7
+ setTimeout(callback: () => void, delayMs: number): unknown;
8
+ clearTimeout(handle: unknown): void;
9
+ }
10
+ /**
11
+ * The phase is an absolute clock offset. Epoch zero starts at phaseOffsetMs;
12
+ * negative epoch/cycle indices are valid when a test or monotonic clock starts
13
+ * before that offset.
14
+ */
15
+ export interface CoverSchedule {
16
+ phaseOffsetMs: number;
17
+ coverCadenceMs: number;
18
+ coverLanes: number;
19
+ coverFramesPerCell: number;
20
+ coverDurationEpochs: number;
21
+ }
22
+ export interface CoverSlot {
23
+ readonly deadlineMs: number;
24
+ readonly cycleIndex: number;
25
+ readonly cycleStartMs: number;
26
+ readonly cycleEndMs: number;
27
+ readonly epochIndex: number;
28
+ readonly epochInCycle: number;
29
+ readonly laneIndex: number;
30
+ readonly frameIndex: number;
31
+ readonly laneSlotIndex: number;
32
+ }
33
+ export interface CoverJobSlot extends CoverSlot {
34
+ readonly jobId: string;
35
+ readonly jobKind: CoverJobKind;
36
+ /** Index of the next not-yet-accepted job cell. */
37
+ readonly jobCellIndex: number;
38
+ }
39
+ /**
40
+ * A producer is deliberately lazy: ratchet state and encrypted cell bytes can
41
+ * be prepared at the scheduled slot instead of buffering a complete cycle.
42
+ * Returning null substitutes a dummy for this slot without consuming a job
43
+ * cell. declaredCellCount enforces the authenticated F x D capacity class.
44
+ */
45
+ export interface CoverJob {
46
+ readonly id: string;
47
+ readonly kind: CoverJobKind;
48
+ readonly declaredCellCount: number;
49
+ readonly priority?: number;
50
+ nextCell(slot: CoverJobSlot): MaybePromise<Uint8Array | null>;
51
+ cancelCell?(slot: CoverJobSlot): MaybePromise<Uint8Array>;
52
+ }
53
+ export interface CoverJobSummary {
54
+ readonly id: string;
55
+ readonly kind: CoverJobKind;
56
+ readonly declaredCellCount: number;
57
+ }
58
+ export interface CoverLaneOpenContext {
59
+ readonly cycleIndex: number;
60
+ readonly cycleStartMs: number;
61
+ readonly cycleEndMs: number;
62
+ readonly laneIndex: number;
63
+ readonly job: CoverJobSummary | null;
64
+ }
65
+ export interface CoverSendRequest {
66
+ readonly cell: Uint8Array;
67
+ readonly source: CoverCellSource;
68
+ readonly slot: CoverSlot;
69
+ readonly jobId?: string;
70
+ readonly jobCellIndex?: number;
71
+ }
72
+ export interface CoverLaneCloseContext extends CoverLaneOpenContext {
73
+ readonly reason: "boundary" | "late-cleanup";
74
+ }
75
+ export interface CoverLane {
76
+ /**
77
+ * Return false only when the transport rejected the cell for backpressure.
78
+ * A thrown/rejected send is treated as a transport failure.
79
+ */
80
+ send(request: CoverSendRequest): MaybePromise<boolean | void>;
81
+ close(context: CoverLaneCloseContext): void;
82
+ }
83
+ export type CoverJobOutcome = "completed" | "cancelled-before-admission" | "cancelled" | "failed";
84
+ export interface CoverJobResult {
85
+ readonly jobId: string;
86
+ readonly outcome: CoverJobOutcome;
87
+ readonly reason?: "capacity-exhausted" | "stopped";
88
+ }
89
+ export interface CoverJobInterruption {
90
+ readonly jobId: string;
91
+ readonly reason: "backpressure" | "cell-production-failed" | "lane-open-failed" | "missed-deadline" | "send-failed" | "suspended";
92
+ }
93
+ export interface CoverStatusChange {
94
+ readonly previous: CoverSchedulerStatus;
95
+ readonly status: CoverSchedulerStatus;
96
+ readonly reason?: string;
97
+ }
98
+ export interface CoverSchedulerOptions {
99
+ readonly schedule: CoverSchedule;
100
+ readonly clock: CoverClock;
101
+ readonly laneFactory: (context: CoverLaneOpenContext) => CoverLane;
102
+ readonly makeDummy: (slot: CoverSlot) => MaybePromise<Uint8Array>;
103
+ /**
104
+ * Defaults to zero. A runtime may opt into a small, explicit timer tolerance;
105
+ * deadlines and metadata remain the absolute unshifted values.
106
+ */
107
+ readonly maxTimerDriftMs?: number;
108
+ readonly onStatusChange?: (change: CoverStatusChange) => void;
109
+ readonly onJobResult?: (result: CoverJobResult) => void;
110
+ readonly onJobInterrupted?: (interruption: CoverJobInterruption) => void;
111
+ }
112
+ export type CoverCancelResult = "not-found" | "cancelled-before-admission" | "cancel-pending";
113
+ export declare class CoverScheduler {
114
+ readonly schedule: Readonly<CoverSchedule>;
115
+ private readonly clock;
116
+ private readonly laneFactory;
117
+ private readonly makeDummy;
118
+ private readonly maxTimerDriftMs;
119
+ private readonly onStatusChange?;
120
+ private readonly onJobResult?;
121
+ private readonly onJobInterrupted?;
122
+ private readonly cycleDurationMs;
123
+ private readonly slotsPerEpoch;
124
+ private readonly slotsPerLane;
125
+ private status;
126
+ private running;
127
+ private sequence;
128
+ private generation;
129
+ private timer;
130
+ private hasTimer;
131
+ private cycle?;
132
+ private readonly queued;
133
+ private readonly jobs;
134
+ constructor(options: CoverSchedulerOptions);
135
+ getStatus(): CoverSchedulerStatus;
136
+ getQueuedJobIds(): readonly string[];
137
+ enqueue(job: CoverJob): void;
138
+ cancel(jobId: string): CoverCancelResult;
139
+ /**
140
+ * Mark a producer complete without adding a terminal frame. Receipt/control
141
+ * traffic is queued as its own control job; this lane becomes dummy tail.
142
+ */
143
+ complete(jobId: string): boolean;
144
+ start(): boolean;
145
+ suspend(reason?: string): boolean;
146
+ resume(): boolean;
147
+ /**
148
+ * Stop is graceful for an already-open cover cycle: sends cease immediately,
149
+ * but channel close remains pinned to the authenticated cycle boundary.
150
+ */
151
+ stop(): boolean;
152
+ private assertClockNow;
153
+ private nextCycleBoundary;
154
+ private cycleIndexAtBoundary;
155
+ private sortQueue;
156
+ private removeQueued;
157
+ private dequeue;
158
+ private requeue;
159
+ private settle;
160
+ private setStatus;
161
+ private safeNotify;
162
+ private disarm;
163
+ private arm;
164
+ private armOpenBoundary;
165
+ private armCycleBoundary;
166
+ private armSlot;
167
+ private handleLateTimer;
168
+ private openCycle;
169
+ private slotAt;
170
+ private jobSlot;
171
+ private requestForSlot;
172
+ private cacheUnsent;
173
+ private acceptSent;
174
+ private sendSlot;
175
+ private degrade;
176
+ private interruptCycle;
177
+ private finalizeCycleJobs;
178
+ private closeCurrentCycle;
179
+ private handleCycleBoundary;
180
+ }
181
+ export declare const coverSlotSpacingMs: (schedule: CoverSchedule) => number;
182
+ export declare const validateCoverSchedule: (schedule: CoverSchedule) => void;
183
+ export declare const createCoverScheduler: (options: CoverSchedulerOptions) => CoverScheduler;
184
+ export {};
@@ -0,0 +1,46 @@
1
+ import type { CoverJob } from "./coverScheduler";
2
+ import type { CoverRuntime } from "./coverRuntime";
3
+ import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
4
+ /**
5
+ * Queue one scheduled receipt (per-cell ack or terminal completion token) for
6
+ * the next available reverse control slots. Returns false when the edge has no
7
+ * cover runtime (immediate mode) so the caller falls back to immediate
8
+ * receipts, and drops oldest-first beyond the flood bound.
9
+ */
10
+ export declare const queueScheduledReceipt: (epc: IRTCPeerConnection, merkleRoot: Uint8Array, token: Uint8Array) => boolean;
11
+ /** Wipe and drop every queued scheduled receipt (edge teardown). */
12
+ export declare const releaseScheduledReceipts: (epc: IRTCPeerConnection) => void;
13
+ /**
14
+ * A synthetic constant-shape label for receipt routing: handleReadReceipt only
15
+ * consumes the Merkle-root element, so the name element is canonical zeros.
16
+ */
17
+ export declare const syntheticScheduledLabel: (merkleRootHex: string) => string;
18
+ /** Seal exactly one already-staged chunk into its uniform 65,490-byte cell. */
19
+ export type SealTransferSlotCell = (chunkIndex: number) => Promise<Uint8Array | null> | Uint8Array | null;
20
+ export interface ScheduledSendJobOptions {
21
+ readonly runtime: CoverRuntime;
22
+ /** The compiled per-message channel label; it is the job/lane id. */
23
+ readonly channelMessageLabel: string;
24
+ readonly totalChunks: number;
25
+ /** Seal the sender's cell for one chunk index (real bytes). */
26
+ readonly sealSlotCell: SealTransferSlotCell;
27
+ /** Set of already-acked real chunk indices for reconcile skipping. */
28
+ readonly getAckedChunks?: () => ReadonlySet<number>;
29
+ /** Real chunk indices only (decoys never resend); default is all indices. */
30
+ readonly realChunkIndices?: readonly number[];
31
+ }
32
+ /**
33
+ * Build the lazy scheduled-send job for one message on one edge. It seals at
34
+ * most ONE chunk per scheduled slot (never a burst), returns null (dummy
35
+ * substitution) once every not-yet-acked real chunk has been offered this
36
+ * pass, and refuses admission if the declared cell count cannot fit the
37
+ * authenticated `F × D` capacity class. Reconcile is scheduled, not burst: an
38
+ * un-acked chunk is re-offered on the next pass through the job's cells.
39
+ */
40
+ export declare const buildScheduledSendJob: (options: ScheduledSendJobOptions) => CoverJob;
41
+ /**
42
+ * The scheduled-mode wire CANCEL for one transfer on one edge: scheduler
43
+ * cancellation emits the encrypted CANCEL cell in the job's next slot and
44
+ * leaves a dummy tail through the fixed boundary. Never a channel close.
45
+ */
46
+ export declare const cancelScheduledTransfer: (epc: IRTCPeerConnection, channelLabel: string, merkleRootHex: string) => Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ export interface ClassifiedFrame {
2
+ type: number;
3
+ payload: Uint8Array;
4
+ }
5
+ /**
6
+ * Reads the protocol-v3 1-byte frame tag and returns it together with a
7
+ * zero-copy view of the remaining bytes. An empty frame is reported as type -1
8
+ * so the caller can log-and-drop rather than throw.
9
+ */
10
+ export declare const classifyFrame: (data: Uint8Array) => ClassifiedFrame;
@@ -1,4 +1,3 @@
1
1
  import type { MiddlewareAPI } from "@reduxjs/toolkit";
2
2
  import type { KeyPair } from "../reducers/keyPairSlice";
3
3
  export declare const handleChallenge: (keyPair: KeyPair, peerId: string, challenge: string, store: MiddlewareAPI) => Promise<void>;
4
- //# sourceMappingURL=handleChallenge.d.ts.map
@@ -4,4 +4,3 @@ export interface IRTCPeerConnectionParams extends RTCPeerConnectionParams {
4
4
  peerConnections: IRTCPeerConnection[];
5
5
  }
6
6
  export declare const handleConnectToPeer: ({ peerId, peerPublicKey, roomId, peerConnections, rtcConfig, }: IRTCPeerConnectionParams, api: BaseQueryApi) => Promise<IRTCPeerConnection>;
7
- //# sourceMappingURL=handleConnectToPeer.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { type PersistInitialRatchetState, type RollbackInitialRatchetState } from "./ratchetPersist";
2
+ import { SparsePqHealingState } from "./pqHealingRuntime";
3
+ import { type PqHealingBootstrap } from "./handshakeCore";
4
+ import type { LibCrypto } from "../cryptography/libcrypto";
5
+ import type { RatchetRootSuite } from "../utils/constants";
6
+ import type { RatchetState } from "../cryptography/ratchet";
7
+ import type { RatchetGateLease } from "./ratchetGate";
8
+ import type { RoomPqMode } from "../roomPolicy";
9
+ import type { IRTCPeerConnection, IRTCDataChannel } from "../api/webrtc/interfaces";
10
+ /**
11
+ * Raw bytes of the `a=fingerprint:sha-256 XX:XX:...` line in an SDP blob.
12
+ * Throws if no sha-256 fingerprint attribute is present, or if the one found
13
+ * doesn't decode to exactly 32 bytes.
14
+ */
15
+ export declare const parseFingerprintFromSdp: (sdp: string) => Uint8Array;
16
+ /**
17
+ * Post-connect DTLS binding check (spec §5): the fingerprint declared in the
18
+ * SDP (and bound into the CPace channel-input via buildChannelInput) must
19
+ * equal the live certificate reported by getStats(). A disagreement means the
20
+ * DTLS transport terminates on a different certificate than the one that was
21
+ * authenticated by the handshake — i.e. a signaling server (or any on-path
22
+ * attacker) swapped the SDP fingerprint/cert after the CPace transcript was
23
+ * bound. This is the actual MITM tripwire, so it THROWS (never returns a
24
+ * boolean, never logs-and-continues) — the caller must abort the channel.
25
+ */
26
+ export declare const verifyDtlsFingerprints: (epc: IRTCPeerConnection) => Promise<void>;
27
+ /** Opaque owner of one concrete main-channel handshake attempt. */
28
+ export interface HandshakeLease {
29
+ readonly token: symbol;
30
+ }
31
+ export declare const setHandshakeChannel: (roomId: string, peerId: string, channel: IRTCDataChannel | {
32
+ send: (b: ArrayBuffer | Uint8Array) => void;
33
+ }, pqMode?: RoomPqMode, expectFinish?: boolean) => HandshakeLease;
34
+ export declare const clearHandshakeChannel: (roomId: string, peerId: string, reason?: Error, lease?: HandshakeLease) => boolean;
35
+ export declare const deliverHandshakeFrame: (roomId: string, peerId: string, payload: Uint8Array, lease: HandshakeLease) => boolean;
36
+ /**
37
+ * Consume the handshake's PQ-healing bootstrap into a live store-free runtime.
38
+ * The constructor copies the root/binding, so the caller-owned bootstrap
39
+ * buffers are wiped on every exit — including a failed construction — leaving
40
+ * the returned runtime as the only holder of the derived PQ root.
41
+ */
42
+ export declare const createHandshakePqRuntime: (module: LibCrypto, pqMode: RoomPqMode, rootSuite: RatchetRootSuite, amInitiator: boolean, bootstrap: PqHealingBootstrap) => SparsePqHealingState;
43
+ export interface EdgeCryptoCandidate {
44
+ readonly state: RatchetState;
45
+ readonly pqRuntime: SparsePqHealingState;
46
+ }
47
+ /**
48
+ * Atomically persist the handshake-established Double Ratchet together with
49
+ * the initial sparse-PQ checkpoint in ONE stable-edge row, then synchronously
50
+ * install both on the live connection. The candidate serializer hook is
51
+ * installed before the row is written, so even the very first persisted row
52
+ * carries a non-null encrypted edge state. `beforeInstall` runs inside the
53
+ * synchronous activation (lease/gate checks); if it — or persistence — throws,
54
+ * the hook is removed, the seed row is rolled back by the persistence layer,
55
+ * nothing is installed, and ownership of both candidates stays with the
56
+ * caller (which must wipe/destroy them). Ownership transfers to `epc` only
57
+ * when this resolves.
58
+ */
59
+ export declare const persistAndActivateEdgeCrypto: (epc: IRTCPeerConnection, roomId: string, candidate: EdgeCryptoCandidate, beforeInstall: () => void, persist?: PersistInitialRatchetState, rollback?: RollbackInitialRatchetState) => Promise<void>;
60
+ /**
61
+ * Orchestrates the handshake on the persistent `main` channel (spec §5): verifies
62
+ * the DTLS fingerprints (getStats vs SDP), runs the triple-confirmation core,
63
+ * then — ONLY
64
+ * on success — seeds + persists the (wrapped) ratchet together with the
65
+ * initial sparse-PQ checkpoint in one row, sets epc.ratchetState /
66
+ * epc.pqHealingState, and opens the per-peer gate. Any failure (fingerprint
67
+ * mismatch,
68
+ * CPace/interactive-3DH error, key-confirmation mismatch, a bad/short frame,
69
+ * a transport or
70
+ * persistence error) rejects the gate and re-throws, having persisted NOTHING —
71
+ * the caller tears the channel down. No PQ root, binding, or runtime survives
72
+ * a failure path outside this function.
73
+ */
74
+ export declare const runHandshake: (epc: IRTCPeerConnection, roomId: string, mode: "pin" | "nopin", pqMode: RoomPqMode, pin: Uint8Array | null, channelInput: Uint8Array, module: LibCrypto, handshakeLease: HandshakeLease, gateLease: RatchetGateLease) => Promise<void>;
@@ -1,5 +1,54 @@
1
+ import { type ReceiveMessageResult } from "./handleReceiveMessage";
1
2
  import type { LibCrypto } from "../cryptography/libcrypto";
2
- import type { BaseQueryApi } from "@reduxjs/toolkit/dist/query";
3
- import type { IRTCDataChannel } from "../api/webrtc/interfaces";
4
- export declare const enqueue: (data: Uint8Array, queue: Uint8Array[], seen: Set<string>, draining: boolean, api: BaseQueryApi, roomId: string, peerId: string, channelLabel: string, merkleRootHex: string, extChannel: IRTCDataChannel | undefined, decrypted: Uint8Array | undefined, messageArray: Uint8Array | undefined, merkleRootArray: Uint8Array | undefined, senderPublicKeyArray: Uint8Array | undefined, receiverSecretKeyArray: Uint8Array | undefined, receiveMessageModule: LibCrypto) => void;
5
- //# sourceMappingURL=handleMessageQueueing.d.ts.map
3
+ import type { BaseQueryApi } from "@reduxjs/toolkit/query";
4
+ import type { IRTCDataChannel, IRTCPeerConnection } from "../api/webrtc/interfaces";
5
+ /**
6
+ * True while this room/peer edge still has queued inbound chunk frames or an
7
+ * in-flight frame handler. The sparse-PQ orchestrator drains to quiescence
8
+ * before an epoch transition so old-epoch first-chunks are not orphaned.
9
+ */
10
+ export declare const hasQueuedEdgeReceiveWork: (roomId: string, peerId: string) => boolean;
11
+ export declare const waitForEdgeReceiveQuiescence: (roomId: string, peerId: string) => Promise<void>;
12
+ /**
13
+ * A terminal receipt may ride an immediate frame only in immediate mode. In a
14
+ * scheduled-cover room acknowledgement substitutes into a reverse cover slot,
15
+ * so an immediate frame here would be an unscheduled emission correlated with
16
+ * message completion.
17
+ */
18
+ export declare const shouldSendImmediateTerminalReceipt: (epc?: Pick<IRTCPeerConnection, "coverRuntime">) => boolean;
19
+ export declare const MAX_QUEUED_FRAMES_PER_CHANNEL = 64;
20
+ export declare const MAX_QUEUED_BYTES_PER_EDGE: number;
21
+ export declare const MAX_QUEUED_RECEIPTS_PER_CHANNEL = 2048;
22
+ export declare const MAX_QUEUED_RECEIPTS_PER_EDGE = 8192;
23
+ export interface MessageProcessingState {
24
+ value: boolean;
25
+ released?: boolean;
26
+ idleWaiters?: Set<() => void>;
27
+ }
28
+ export declare const waitForQueuedMessageFrames: (queue: Uint8Array[], state: MessageProcessingState) => Promise<void>;
29
+ export declare const releaseQueuedMessageFrames: (queue: Uint8Array[], roomId: string, peerId: string, state?: MessageProcessingState) => void;
30
+ export interface ReceiptProcessingQueue {
31
+ readonly roomId: string;
32
+ readonly peerId: string;
33
+ readonly pending: Uint8Array[];
34
+ readonly seen: Set<string>;
35
+ draining: boolean;
36
+ released: boolean;
37
+ }
38
+ export declare const createReceiptProcessingQueue: (roomId: string, peerId: string) => ReceiptProcessingQueue;
39
+ export declare const releaseQueuedReceipts: (state: ReceiptProcessingQueue) => void;
40
+ /**
41
+ * Queue one already-classified 64-byte receipt token. This is deliberately a
42
+ * bounded data queue, not one promise per onmessage event. At most one handler
43
+ * executes per room/peer edge, even when many message channels are open.
44
+ */
45
+ export declare const enqueueReceipt: (receipt: Uint8Array, state: ReceiptProcessingQueue, processReceipt: (receipt: Uint8Array) => Promise<void>) => boolean;
46
+ type PerFrameReceiptResult = Pick<ReceiveMessageResult, "chunkHash" | "chunkIndex" | "chunkSize" | "totalSize">;
47
+ /**
48
+ * Emit exactly one receipt-shaped frame for every inbound chunk frame. Real
49
+ * cells use their rooted token; cover/dropped cells use an unlinkable random
50
+ * token so an observer cannot infer which slot carried bytes.
51
+ */
52
+ export declare const sendReceiveFrameReceipt: (result: PerFrameReceiptResult, channel: IRTCDataChannel | undefined) => boolean;
53
+ export declare const enqueue: (data: Uint8Array, queue: Uint8Array[], seen: Set<string>, drainingRef: MessageProcessingState, api: BaseQueryApi, roomId: string, peerId: string, channelLabel: string, merkleRootHex: string, merkleRoot: Uint8Array, extChannel: IRTCDataChannel | undefined, epc: IRTCPeerConnection | undefined, receiveMessageModule: LibCrypto, signal?: AbortSignal) => boolean;
54
+ export {};
@@ -1,10 +1,12 @@
1
1
  import type { BaseQueryApi } from "@reduxjs/toolkit/query";
2
2
  import type { IRTCPeerConnection, IRTCDataChannel } from "../api/webrtc/interfaces";
3
+ export declare const MAX_MESSAGE_CHANNELS_PER_EDGE = 64;
3
4
  export interface OpenChannelHelperParams {
4
5
  channel: string | RTCDataChannel;
5
6
  epc: IRTCPeerConnection;
6
7
  roomId: string;
7
8
  dataChannels: IRTCDataChannel[];
9
+ /** True only for a channel delivered by RTCPeerConnection.ondatachannel. */
10
+ incoming?: boolean;
8
11
  }
9
- export declare const handleOpenChannel: ({ channel, epc, roomId, dataChannels }: OpenChannelHelperParams, api: BaseQueryApi) => Promise<IRTCDataChannel>;
10
- //# sourceMappingURL=handleOpenChannel.d.ts.map
12
+ export declare const handleOpenChannel: ({ channel, epc, roomId, dataChannels, incoming, }: OpenChannelHelperParams, api: BaseQueryApi) => Promise<IRTCDataChannel>;
@@ -1,3 +1,2 @@
1
1
  import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
2
2
  export declare const handleQueuedIceCandidates: (epc: IRTCPeerConnection) => Promise<void>;
3
- //# sourceMappingURL=handleQueuedIceCandidates.d.ts.map
@@ -1,4 +1,17 @@
1
- import type { BaseQueryApi } from "@reduxjs/toolkit/dist/query";
2
- import type { Room } from "../reducers/roomSlice";
3
- export declare const handleReadReceipt: (receivedChunkHash: Uint8Array, channel: string, peerId: string, room: Room, api: BaseQueryApi) => Promise<void>;
4
- //# sourceMappingURL=handleReadReceipt.d.ts.map
1
+ import type { BaseQueryApi } from "@reduxjs/toolkit/query";
2
+ export type ReadReceiptOutcome = {
3
+ kind: "ignored";
4
+ peerId: string;
5
+ } | {
6
+ kind: "chunk-acked";
7
+ peerId: string;
8
+ transferId: string;
9
+ chunkIndex: number;
10
+ newlyAccepted: boolean;
11
+ } | {
12
+ kind: "peer-complete";
13
+ peerId: string;
14
+ transferId: string;
15
+ newlyAccepted: boolean;
16
+ };
17
+ export declare const handleReadReceipt: (receivedChunkHash: Uint8Array, channel: string, peerId: string, roomId: string, api: BaseQueryApi) => Promise<ReadReceiptOutcome>;
@@ -1,5 +1,8 @@
1
1
  import type { LibCrypto } from "../cryptography/libcrypto";
2
- export declare const handleReceiveMessage: (decrypted: Uint8Array, messageArray: Uint8Array, merkleRootArray: Uint8Array, senderPublicKeyArray: Uint8Array, receiverSecretKeyArray: Uint8Array, module: LibCrypto) => Promise<{
2
+ import type { IRTCPeerConnection } from "../api/webrtc/interfaces";
3
+ import type { ReceiveChunk, ReceiveChunkStoreResult } from "../db/types";
4
+ import type { PersistRatchetState } from "./ratchetPersist";
5
+ export interface ReceiveMessageResult {
3
6
  date: Date;
4
7
  chunkSize: number;
5
8
  chunkIndex: number;
@@ -8,7 +11,22 @@ export declare const handleReceiveMessage: (decrypted: Uint8Array, messageArray:
8
11
  totalSize: number;
9
12
  messageType: number;
10
13
  filename: string;
14
+ /** 64-byte root/index/leaf-bound receipt token (legacy field name). */
11
15
  chunkHash: Uint8Array;
12
16
  messageHash: Uint8Array;
13
- }>;
14
- //# sourceMappingURL=handleReceiveMessage.d.ts.map
17
+ }
18
+ type ReceiveChunkStore = (chunk: ReceiveChunk) => Promise<ReceiveChunkStoreResult>;
19
+ export interface ReceiveMessageDependencies {
20
+ /** Test/fault-injection seam; production uses the ratchet worker database. */
21
+ persistRatchetState?: PersistRatchetState;
22
+ /** Test/fault-injection seam; production uses the receive-chunk worker. */
23
+ storeReceiveChunk?: ReceiveChunkStore;
24
+ }
25
+ /**
26
+ * Worker storage is the durability boundary for a receipt. A failed write must
27
+ * look exactly like a decoy/drop to the caller, and the owned real-byte copy is
28
+ * erased after postMessage has cloned it (or after an injected failure).
29
+ */
30
+ export declare const storeReceiveChunkFailClosed: (chunk: Omit<ReceiveChunk, "data">, realChunk: Uint8Array, store?: ReceiveChunkStore) => Promise<ReceiveChunkStoreResult | null>;
31
+ export declare const handleReceiveMessage: (frame: Uint8Array, roomId: string, channelLabel: string, epc: IRTCPeerConnection, merkleRoot: Uint8Array, module: LibCrypto, signal?: AbortSignal, dependencies?: ReceiveMessageDependencies) => Promise<ReceiveMessageResult>;
32
+ export {};