@plane31/blind-relay 0.0.0-stage → 0.1.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 (169) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +26 -2
  3. package/dist/adapters/index.d.ts +18 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +22 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/audience.d.ts +77 -0
  8. package/dist/audience.d.ts.map +1 -0
  9. package/dist/audience.js +76 -0
  10. package/dist/audience.js.map +1 -0
  11. package/dist/bytes.d.ts +54 -0
  12. package/dist/bytes.d.ts.map +1 -0
  13. package/dist/bytes.js +200 -0
  14. package/dist/bytes.js.map +1 -0
  15. package/dist/circle.d.ts +59 -0
  16. package/dist/circle.d.ts.map +1 -0
  17. package/dist/circle.js +105 -0
  18. package/dist/circle.js.map +1 -0
  19. package/dist/client.d.ts +234 -0
  20. package/dist/client.d.ts.map +1 -0
  21. package/dist/client.js +1365 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/codec.d.ts +189 -0
  24. package/dist/codec.d.ts.map +1 -0
  25. package/dist/codec.js +512 -0
  26. package/dist/codec.js.map +1 -0
  27. package/dist/crypto/backend.d.ts +39 -0
  28. package/dist/crypto/backend.d.ts.map +1 -0
  29. package/dist/crypto/backend.js +8 -0
  30. package/dist/crypto/backend.js.map +1 -0
  31. package/dist/crypto/noble.d.ts +3 -0
  32. package/dist/crypto/noble.d.ts.map +1 -0
  33. package/dist/crypto/noble.js +107 -0
  34. package/dist/crypto/noble.js.map +1 -0
  35. package/dist/crypto/sodium.d.ts +9 -0
  36. package/dist/crypto/sodium.d.ts.map +1 -0
  37. package/dist/crypto/sodium.js +51 -0
  38. package/dist/crypto/sodium.js.map +1 -0
  39. package/dist/crypto.d.ts +66 -0
  40. package/dist/crypto.d.ts.map +1 -0
  41. package/dist/crypto.js +225 -0
  42. package/dist/crypto.js.map +1 -0
  43. package/dist/epoch.d.ts +93 -0
  44. package/dist/epoch.d.ts.map +1 -0
  45. package/dist/epoch.js +121 -0
  46. package/dist/epoch.js.map +1 -0
  47. package/dist/errors.d.ts +17 -0
  48. package/dist/errors.d.ts.map +1 -0
  49. package/dist/errors.js +30 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/historyIndex.d.ts +15 -0
  52. package/dist/historyIndex.d.ts.map +1 -0
  53. package/dist/historyIndex.js +57 -0
  54. package/dist/historyIndex.js.map +1 -0
  55. package/dist/identity.d.ts +62 -0
  56. package/dist/identity.d.ts.map +1 -0
  57. package/dist/identity.js +99 -0
  58. package/dist/identity.js.map +1 -0
  59. package/dist/inbox.d.ts +153 -0
  60. package/dist/inbox.d.ts.map +1 -0
  61. package/dist/inbox.js +472 -0
  62. package/dist/inbox.js.map +1 -0
  63. package/dist/index.d.ts +19 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +22 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/outbox.d.ts +96 -0
  68. package/dist/outbox.d.ts.map +1 -0
  69. package/dist/outbox.js +339 -0
  70. package/dist/outbox.js.map +1 -0
  71. package/dist/policy.d.ts +33 -0
  72. package/dist/policy.d.ts.map +1 -0
  73. package/dist/policy.js +71 -0
  74. package/dist/policy.js.map +1 -0
  75. package/dist/record.d.ts +49 -0
  76. package/dist/record.d.ts.map +1 -0
  77. package/dist/record.js +46 -0
  78. package/dist/record.js.map +1 -0
  79. package/dist/reset.d.ts +74 -0
  80. package/dist/reset.d.ts.map +1 -0
  81. package/dist/reset.js +127 -0
  82. package/dist/reset.js.map +1 -0
  83. package/dist/rows.d.ts +16 -0
  84. package/dist/rows.d.ts.map +1 -0
  85. package/dist/rows.js +103 -0
  86. package/dist/rows.js.map +1 -0
  87. package/dist/slots.d.ts +22 -0
  88. package/dist/slots.d.ts.map +1 -0
  89. package/dist/slots.js +38 -0
  90. package/dist/slots.js.map +1 -0
  91. package/dist/store.d.ts +357 -0
  92. package/dist/store.d.ts.map +1 -0
  93. package/dist/store.js +192 -0
  94. package/dist/store.js.map +1 -0
  95. package/dist/testing/clock.d.ts +14 -0
  96. package/dist/testing/clock.d.ts.map +1 -0
  97. package/dist/testing/clock.js +27 -0
  98. package/dist/testing/clock.js.map +1 -0
  99. package/dist/testing/fakeHost.d.ts +118 -0
  100. package/dist/testing/fakeHost.d.ts.map +1 -0
  101. package/dist/testing/fakeHost.js +462 -0
  102. package/dist/testing/fakeHost.js.map +1 -0
  103. package/dist/testing/faults.d.ts +97 -0
  104. package/dist/testing/faults.d.ts.map +1 -0
  105. package/dist/testing/faults.js +264 -0
  106. package/dist/testing/faults.js.map +1 -0
  107. package/dist/testing/fixtures.d.ts +15 -0
  108. package/dist/testing/fixtures.d.ts.map +1 -0
  109. package/dist/testing/fixtures.js +22 -0
  110. package/dist/testing/fixtures.js.map +1 -0
  111. package/dist/testing/index.d.ts +7 -0
  112. package/dist/testing/index.d.ts.map +1 -0
  113. package/dist/testing/index.js +7 -0
  114. package/dist/testing/index.js.map +1 -0
  115. package/dist/testing/providers.d.ts +16 -0
  116. package/dist/testing/providers.d.ts.map +1 -0
  117. package/dist/testing/providers.js +36 -0
  118. package/dist/testing/providers.js.map +1 -0
  119. package/dist/testing/sweep.d.ts +46 -0
  120. package/dist/testing/sweep.d.ts.map +1 -0
  121. package/dist/testing/sweep.js +366 -0
  122. package/dist/testing/sweep.js.map +1 -0
  123. package/dist/transactions.d.ts +75 -0
  124. package/dist/transactions.d.ts.map +1 -0
  125. package/dist/transactions.js +212 -0
  126. package/dist/transactions.js.map +1 -0
  127. package/dist/transport.d.ts +125 -0
  128. package/dist/transport.d.ts.map +1 -0
  129. package/dist/transport.js +6 -0
  130. package/dist/transport.js.map +1 -0
  131. package/dist/vault.d.ts +59 -0
  132. package/dist/vault.d.ts.map +1 -0
  133. package/dist/vault.js +69 -0
  134. package/dist/vault.js.map +1 -0
  135. package/package.json +67 -3
  136. package/src/adapters/index.ts +102 -0
  137. package/src/audience.ts +123 -0
  138. package/src/bytes.ts +209 -0
  139. package/src/circle.ts +127 -0
  140. package/src/client.ts +1442 -0
  141. package/src/codec.ts +642 -0
  142. package/src/crypto/backend.ts +48 -0
  143. package/src/crypto/noble.ts +104 -0
  144. package/src/crypto/node-module.d.ts +11 -0
  145. package/src/crypto/sodium.ts +53 -0
  146. package/src/crypto.ts +266 -0
  147. package/src/epoch.ts +201 -0
  148. package/src/errors.ts +78 -0
  149. package/src/historyIndex.ts +61 -0
  150. package/src/identity.ts +158 -0
  151. package/src/inbox.ts +560 -0
  152. package/src/index.ts +59 -0
  153. package/src/outbox.ts +385 -0
  154. package/src/policy.ts +88 -0
  155. package/src/record.ts +106 -0
  156. package/src/reset.ts +176 -0
  157. package/src/rows.ts +116 -0
  158. package/src/slots.ts +55 -0
  159. package/src/store.ts +557 -0
  160. package/src/testing/clock.ts +29 -0
  161. package/src/testing/fakeHost.ts +482 -0
  162. package/src/testing/faults.ts +321 -0
  163. package/src/testing/fixtures.ts +35 -0
  164. package/src/testing/index.ts +6 -0
  165. package/src/testing/providers.ts +39 -0
  166. package/src/testing/sweep.ts +407 -0
  167. package/src/transactions.ts +231 -0
  168. package/src/transport.ts +106 -0
  169. package/src/vault.ts +126 -0
package/dist/epoch.js ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Epochs, wraps, publication and the reader side (protocol section 8).
3
+ */
4
+ import { equalBytes, toHex } from "./bytes.js";
5
+ import { DocumentType, buildEpochManifest, expectType, parseDocument, verifyDocument } from "./codec.js";
6
+ import { KEY_BYTES, bodyHash, keyCommitment, openSealedBox, randomBytes, sealBox, wrapHash } from "./crypto.js";
7
+ import { KavachError } from "./errors.js";
8
+ export const MAX_COUNTER = 0xffff_ffff;
9
+ export const MAX_EPOCH = 0xffff_ffff;
10
+ /** Prepare an epoch: fresh random key, one sealed box per row, one signature. */
11
+ export function prepareEpoch(input) {
12
+ const stage = "prepareEpoch";
13
+ if (input.epoch < 1 || input.epoch > MAX_EPOCH)
14
+ throw new KavachError("COUNTER_EXHAUSTED", stage, "epoch space exhausted; replace the stream");
15
+ const epochKey = input.epochKey ?? randomBytes(KEY_BYTES);
16
+ const wraps = new Map();
17
+ const rows = input.audience.rows.map((row) => {
18
+ const wrap = sealBox(epochKey, row.boxKey);
19
+ wraps.set(toHex(row.identityId), wrap);
20
+ return { identityId: row.identityId, certificateHash: row.certificateHash, wrapHash: wrapHash(wrap) };
21
+ });
22
+ if (!rows.some((r) => equalBytes(r.identityId, input.author.identityId)))
23
+ throw new KavachError("MALFORMED", stage, "author missing from audience");
24
+ const manifest = buildEpochManifest({
25
+ streamId: input.author.streamId,
26
+ authorIdentityId: input.author.identityId,
27
+ authorCertificateHash: input.author.certificateHash,
28
+ epoch: input.epoch,
29
+ createdAt: input.createdAt,
30
+ keyCommitment: keyCommitment(input.author.streamId, input.epoch, epochKey),
31
+ audience: rows
32
+ }, input.author.signingSecretKey);
33
+ return {
34
+ epoch: input.epoch,
35
+ manifest,
36
+ manifestHash: bodyHash(manifest.subarray(0, manifest.length - 64)),
37
+ wraps,
38
+ epochKey,
39
+ capturedRevision: input.desiredRevision,
40
+ audienceKey: input.audience.key,
41
+ createdAt: input.createdAt
42
+ };
43
+ }
44
+ /**
45
+ * Resolve a publish outcome for a prepared epoch. Identical bytes at the slot mean committed; a
46
+ * different valid body at the same epoch is `EQUIVOCATION`; a higher committed epoch than the one
47
+ * prepared means local state was lost and the device must replace its stream.
48
+ */
49
+ export function resolvePublish(prepared, outcome) {
50
+ const stage = "publishEpoch";
51
+ switch (outcome.status) {
52
+ case "COMMITTED":
53
+ case "ALREADY_COMMITTED":
54
+ return "COMMITTED";
55
+ case "RETRYABLE":
56
+ return "RETRY";
57
+ case "UNAUTHORIZED":
58
+ throw new KavachError("NOT_AUTHORIZED", stage);
59
+ case "PERMANENT_FAILURE":
60
+ throw new KavachError("STORAGE_FAILURE", stage, "host refused the epoch permanently");
61
+ case "CONFLICT": {
62
+ if (outcome.currentEpoch === prepared.epoch && outcome.currentManifest) {
63
+ if (equalBytes(outcome.currentManifest, prepared.manifest))
64
+ return "COMMITTED";
65
+ const theirs = parseDocument(outcome.currentManifest);
66
+ if (theirs.type === DocumentType.EpochManifest && theirs.epoch === prepared.epoch) {
67
+ throw new KavachError("EQUIVOCATION", stage, "a different manifest is committed at this epoch");
68
+ }
69
+ }
70
+ if (outcome.currentEpoch >= prepared.epoch) {
71
+ throw new KavachError("RECOVERY_REQUIRED", stage, "the host holds a higher epoch for this stream; local state was lost");
72
+ }
73
+ return "RETRY";
74
+ }
75
+ }
76
+ }
77
+ /** After commit: promote only if the captured revision and the exact audience still match. */
78
+ export function promote(prepared, currentDesiredRevision, currentAudienceKey) {
79
+ return prepared.capturedRevision === currentDesiredRevision && prepared.audienceKey === currentAudienceKey ? "PROMOTED" : "OBSOLETE";
80
+ }
81
+ export function rotationReason(active, now, hygieneRotationMs) {
82
+ if (active.nextCounter > MAX_COUNTER)
83
+ return "counterExhausted";
84
+ if (active.lastSentAt !== undefined && now < active.lastSentAt)
85
+ return "clockReversal";
86
+ if (now - active.createdAt > hygieneRotationMs)
87
+ return "hygiene";
88
+ return undefined;
89
+ }
90
+ /**
91
+ * Every check of section 8 before a key is stored: manifest signature under the certificate it
92
+ * names, that certificate's identity and stream, the author's own row, the reader's own row at
93
+ * its current certificate, the wrap hash, the sealed box and the key commitment.
94
+ */
95
+ export function openEpoch(manifestWire, author, self, wrap) {
96
+ const stage = "openEpoch";
97
+ const parsed = expectType(parseDocument(manifestWire), DocumentType.EpochManifest, stage);
98
+ const cert = author.certificates.get(toHex(parsed.authorCertificateHash));
99
+ if (!cert)
100
+ throw new KavachError("SUPERSEDED_DEVICE", stage, "manifest names a certificate that is not the author's current one");
101
+ const manifest = expectType(verifyDocument(manifestWire, cert.signingKey), DocumentType.EpochManifest, stage);
102
+ if (!equalBytes(manifest.authorIdentityId, author.identityId) || !equalBytes(manifest.streamId, cert.streamId)) {
103
+ throw new KavachError("MALFORMED", stage, "manifest identity or stream does not match the certificate");
104
+ }
105
+ const authorRow = manifest.audience.find((r) => equalBytes(r.identityId, author.identityId));
106
+ if (!authorRow || !equalBytes(authorRow.certificateHash, manifest.authorCertificateHash)) {
107
+ throw new KavachError("MALFORMED", stage, "author row does not name the author certificate");
108
+ }
109
+ const selfRow = manifest.audience.find((r) => equalBytes(r.identityId, self.identityId));
110
+ if (!selfRow || !equalBytes(selfRow.certificateHash, self.certificateHash)) {
111
+ throw new KavachError("NOT_AUTHORIZED", stage, "this device is not addressed by the epoch");
112
+ }
113
+ if (!equalBytes(wrapHash(wrap), selfRow.wrapHash))
114
+ throw new KavachError("WRAP_HASH_MISMATCH", stage);
115
+ const epochKey = openSealedBox(wrap, self.box, stage);
116
+ if (!equalBytes(keyCommitment(manifest.streamId, manifest.epoch, epochKey), manifest.keyCommitment)) {
117
+ throw new KavachError("KEY_COMMITMENT_MISMATCH", stage);
118
+ }
119
+ return { manifest, epochKey, superseded: !cert.current };
120
+ }
121
+ //# sourceMappingURL=epoch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch.js","sourceRoot":"","sources":["../src/epoch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** Error codes of Kavach v1, protocol section 14. The set is closed: new codes are a protocol change. */
2
+ export type ErrorCode = "SIZE_LIMIT" | "MALFORMED" | "UNSUPPORTED_VERSION" | "UNSUPPORTED_TYPE" | "BAD_PAYLOAD" | "INVALID_KEY" | "BAD_SIGNATURE" | "DECRYPT_FAILED" | "WRAP_HASH_MISMATCH" | "KEY_COMMITMENT_MISMATCH" | "ACCOUNT_KEY_CHANGED" | "ACCOUNT_RECOVERY_REQUIRED" | "DEVICE_ROLLBACK" | "DEVICE_ID_REUSED" | "SUPERSEDED_DEVICE" | "VAULT_BINDING" | "RESET_BINDING" | "RESET_KEYS_REQUIRED" | "RESET_KEY_REVOKED" | "RESET_CLOCK_UNTRUSTED" | "ROLLBACK" | "EQUIVOCATION" | "METADATA_REQUIRED" | "KEY_PENDING" | "NOT_AUTHORIZED" | "ROTATION_REQUIRED" | "COUNTER_EXHAUSTED" | "RECIPIENT_LIMIT" | "CLOCK_SKEW" | "EXPIRED" | "QUEUE_FULL" | "CURSOR_OUT_OF_RANGE" | "STALE_APPEND" | "LOCKED" | "RECOVERY_REQUIRED" | "STORAGE_FAILURE"
3
+ /** The host could not be reached or did not answer in time; the same call may succeed later. A transport raises it, never a plain error. */
4
+ | "NETWORK";
5
+ /**
6
+ * Every failure the SDK reports. `code` is one of the protocol's codes, `stage` is a safe
7
+ * stage name for logs (never a secret, a payload or an identifier), `retryable` says whether
8
+ * the same call may succeed later without any change of state.
9
+ */
10
+ export declare class KavachError extends Error {
11
+ readonly code: ErrorCode;
12
+ readonly stage: string;
13
+ readonly retryable: boolean;
14
+ constructor(code: ErrorCode, stage?: string, message?: string);
15
+ }
16
+ export declare function isKavachError(value: unknown, code?: ErrorCode): value is KavachError;
17
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,MAAM,MAAM,SAAS,GAEjB,YAAY,GACZ,WAAW,GACX,qBAAqB,GACrB,kBAAkB,GAClB,aAAa,GAEb,aAAa,GACb,eAAe,GACf,gBAAgB,GAChB,oBAAoB,GACpB,yBAAyB,GAEzB,qBAAqB,GACrB,2BAA2B,GAC3B,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,qBAAqB,GACrB,mBAAmB,GACnB,uBAAuB,GACvB,UAAU,GACV,cAAc,GACd,mBAAmB,GACnB,aAAa,GACb,gBAAgB,GAChB,mBAAmB,GACnB,mBAAmB,GACnB,iBAAiB,GAEjB,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,qBAAqB,GACrB,cAAc,GAEd,QAAQ,GACR,mBAAmB,GACnB,iBAAiB;AACnB,4IAA4I;GAC1I,SAAS,CAAC;AAYd;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,IAAI,EAAE,SAAS,EAAE,KAAK,SAAY,EAAE,OAAO,CAAC,EAAE,MAAM;CAOjE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,IAAI,WAAW,CAEpF"}
package/dist/errors.js ADDED
@@ -0,0 +1,30 @@
1
+ const RETRYABLE = new Set([
2
+ "KEY_PENDING",
3
+ "METADATA_REQUIRED",
4
+ "RESET_KEYS_REQUIRED",
5
+ "ROTATION_REQUIRED",
6
+ "LOCKED",
7
+ "STORAGE_FAILURE",
8
+ "NETWORK"
9
+ ]);
10
+ /**
11
+ * Every failure the SDK reports. `code` is one of the protocol's codes, `stage` is a safe
12
+ * stage name for logs (never a secret, a payload or an identifier), `retryable` says whether
13
+ * the same call may succeed later without any change of state.
14
+ */
15
+ export class KavachError extends Error {
16
+ code;
17
+ stage;
18
+ retryable;
19
+ constructor(code, stage = "unknown", message) {
20
+ super(message ?? `${code} at ${stage}`);
21
+ this.name = "KavachError";
22
+ this.code = code;
23
+ this.stage = stage;
24
+ this.retryable = RETRYABLE.has(code);
25
+ }
26
+ }
27
+ export function isKavachError(value, code) {
28
+ return value instanceof KavachError && (code === undefined || value.code === code);
29
+ }
30
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA8CA,MAAM,SAAS,GAA2B,IAAI,GAAG,CAAY;IAC3D,aAAa;IACb,mBAAmB;IACnB,qBAAqB;IACrB,mBAAmB;IACnB,QAAQ;IACR,iBAAiB;IACjB,SAAS;CACV,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAAY;IAChB,KAAK,CAAS;IACd,SAAS,CAAU;IAE5B,YAAY,IAAe,EAAE,KAAK,GAAG,SAAS,EAAE,OAAgB;QAC9D,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,KAAc,EAAE,IAAgB;IAC5D,OAAO,KAAK,YAAY,WAAW,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { HistoryRow, Store, Tx } from "./store.js";
2
+ /** A u64 as twenty decimal digits, so that string order is numeric order. */
3
+ export declare function padTime(ms: bigint): string;
4
+ export declare function historyTuple(r: Pick<HistoryRow, "sentAt" | "epoch" | "counter" | "recordId">): string;
5
+ export declare function timelinePrefix(kind: number): string;
6
+ export declare function authorPrefix(authorIdentityId: string, kind: number): string;
7
+ /** Index a history row; written in the same transaction as the row (T6, T7). */
8
+ export declare function indexHistory(tx: Tx, r: HistoryRow): void;
9
+ export declare function unindexHistory(tx: Tx, r: HistoryRow): void;
10
+ /**
11
+ * Build the index once for a store written before it existed: every history row is indexed and
12
+ * the header records it. A fresh store is marked at T1 and never runs this. Idempotent.
13
+ */
14
+ export declare function ensureHistoryIndex(store: Store): Promise<boolean>;
15
+ //# sourceMappingURL=historyIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyIndex.d.ts","sourceRoot":"","sources":["../src/historyIndex.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AAIxD,6EAA6E;AAC7E,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC,GAAG,MAAM,CAErG;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AAOD,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,IAAI,CAExD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,IAAI,CAE1D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAUvE"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The history index: two time-ordered keys per history row, so a history query, a cursor and
3
+ * the retention purge read a bounded slice of the store instead of the whole history table.
4
+ *
5
+ * Keys sort by time and are unique: the tuple is the zero-padded `sentAt`, epoch and counter
6
+ * followed by the record id. `t:<kind>:<tuple>` is the timeline of one kind; `a:<author>:<kind>:
7
+ * <tuple>` is one author's. A query without a kind merges the two kinds that reach history
8
+ * declared with history; a slot-only kind is never history. The tuple doubles as the page cursor:
9
+ * it names a position that no later write moves.
10
+ */
11
+ import { unbig } from "./rows.js";
12
+ const META = "meta";
13
+ /** A u64 as twenty decimal digits, so that string order is numeric order. */
14
+ export function padTime(ms) {
15
+ return (ms < 0n ? 0n : ms).toString().padStart(20, "0");
16
+ }
17
+ export function historyTuple(r) {
18
+ return `${padTime(unbig(r.sentAt))}:${r.epoch.toString().padStart(10, "0")}:${r.counter.toString().padStart(10, "0")}:${r.recordId}`;
19
+ }
20
+ export function timelinePrefix(kind) {
21
+ return `t:${kind}:`;
22
+ }
23
+ export function authorPrefix(authorIdentityId, kind) {
24
+ return `a:${authorIdentityId}:${kind}:`;
25
+ }
26
+ function keysOf(r) {
27
+ const tuple = historyTuple(r);
28
+ return [timelinePrefix(r.kind) + tuple, authorPrefix(r.authorIdentityId, r.kind) + tuple];
29
+ }
30
+ /** Index a history row; written in the same transaction as the row (T6, T7). */
31
+ export function indexHistory(tx, r) {
32
+ for (const k of keysOf(r))
33
+ tx.put("historyIndex", k, { recordId: r.recordId });
34
+ }
35
+ export function unindexHistory(tx, r) {
36
+ for (const k of keysOf(r))
37
+ tx.delete("historyIndex", k);
38
+ }
39
+ /**
40
+ * Build the index once for a store written before it existed: every history row is indexed and
41
+ * the header records it. A fresh store is marked at T1 and never runs this. Idempotent.
42
+ */
43
+ export async function ensureHistoryIndex(store) {
44
+ const built = await store.read((tx) => tx.get("meta", META)?.historyIndex === 1);
45
+ if (built)
46
+ return false;
47
+ return store.transaction((tx) => {
48
+ const meta = tx.get("meta", META);
49
+ if (!meta || meta.historyIndex === 1)
50
+ return false;
51
+ for (const [, r] of tx.list("history"))
52
+ indexHistory(tx, r);
53
+ tx.put("meta", META, { ...meta, historyIndex: 1 });
54
+ return true;
55
+ });
56
+ }
57
+ //# sourceMappingURL=historyIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyIndex.js","sourceRoot":"","sources":["../src/historyIndex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlC,MAAM,IAAI,GAAG,MAAM,CAAC;AAEpB,6EAA6E;AAC7E,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAgE;IAC3F,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AACvI,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,KAAK,IAAI,GAAG,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,gBAAwB,EAAE,IAAY;IACjE,OAAO,KAAK,gBAAgB,IAAI,IAAI,GAAG,CAAC;AAC1C,CAAC;AAED,SAAS,MAAM,CAAC,CAAa;IAC3B,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;AAC5F,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,CAAa;IAChD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC;QAAE,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAM,EAAE,CAAa;IAClD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC;QAAE,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAY;IACnD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC;IACjF,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnD,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5D,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { type AccountDevice, type Parsed } from "./codec.js";
2
+ import { type BoxKeypair, type SigningKeypair } from "./crypto.js";
3
+ /** What the SDK persists per known account. */
4
+ export interface AccountPin {
5
+ readonly accountName: string;
6
+ readonly accountKey: Uint8Array;
7
+ readonly identityId: Uint8Array;
8
+ readonly generation: bigint;
9
+ readonly certificateHash: Uint8Array;
10
+ readonly certificate: Uint8Array;
11
+ readonly deviceId: Uint8Array;
12
+ readonly signingKey: Uint8Array;
13
+ readonly boxKey: Uint8Array;
14
+ readonly streamId: Uint8Array;
15
+ /** Every device id this account has used, including the current one. */
16
+ readonly deviceIds: readonly Uint8Array[];
17
+ }
18
+ export type CertificateOutcome = {
19
+ readonly outcome: "ACCEPTED";
20
+ readonly pin: AccountPin;
21
+ readonly rotate: boolean;
22
+ readonly previous: AccountPin | undefined;
23
+ } | {
24
+ readonly outcome: "DUPLICATE";
25
+ readonly pin: AccountPin;
26
+ };
27
+ export declare function pinFromCertificate(doc: Parsed<AccountDevice>, previous?: AccountPin): AccountPin;
28
+ /**
29
+ * Accept or reject a certificate against the local pin for its account.
30
+ *
31
+ * - No pin yet: first contact. If `expected` names the account the directory resolved, the
32
+ * certificate must carry that name and key; the pin is created and nothing rotates.
33
+ * - A pinned account: a different `AK` is `ACCOUNT_KEY_CHANGED` and the caller quarantines
34
+ * (never closing or re-admitting anything); a lower generation is `DEVICE_ROLLBACK`; the same
35
+ * generation is a duplicate or `EQUIVOCATION`; a higher one with a reused device id is
36
+ * `DEVICE_ID_REUSED`; otherwise it is accepted and the audience must rotate.
37
+ */
38
+ export declare function acceptCertificate(current: AccountPin | undefined, wire: Uint8Array, expected?: {
39
+ readonly accountName: string;
40
+ readonly accountKey: Uint8Array;
41
+ }): CertificateOutcome;
42
+ export interface DeviceKeys {
43
+ readonly deviceSeed: Uint8Array;
44
+ readonly deviceId: Uint8Array;
45
+ readonly signing: SigningKeypair;
46
+ readonly box: BoxKeypair;
47
+ }
48
+ export interface OwnAccount {
49
+ readonly accountName: string;
50
+ readonly accountSeed: Uint8Array;
51
+ readonly account: SigningKeypair;
52
+ readonly identityId: Uint8Array;
53
+ }
54
+ export declare function openAccount(accountName: string, accountSeed: Uint8Array): OwnAccount;
55
+ export declare function newAccountSeed(): Uint8Array;
56
+ /** A fresh installation: new device seed and a random nonzero device id. Never backed up. */
57
+ export declare function newDevice(): DeviceKeys;
58
+ export declare function deviceFromSeed(deviceSeed: Uint8Array, deviceId: Uint8Array): DeviceKeys;
59
+ /** Build and sign this account's certificate for a device at a generation. */
60
+ export declare function certify(account: OwnAccount, device: DeviceKeys, generation: bigint): Uint8Array;
61
+ export declare function pinKey(identity: Uint8Array): string;
62
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAQA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,MAAM,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EASL,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAGrB,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC;CAC3C;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAA;CAAE,GAC/H;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CAAE,CAAC;AAEhE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,GAAG,UAAU,CAiBhG;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,IAAI,EAAE,UAAU,EAChB,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;CAAE,GAC3E,kBAAkB,CAepB;AAID,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,UAAU,CAIpF;AAED,wBAAgB,cAAc,IAAI,UAAU,CAE3C;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,IAAI,UAAU,CAKtC;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,CAGvF;AAED,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAa/F;AAED,wBAAgB,MAAM,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Accounts, devices, certificates and generation rules (protocol section 4).
3
+ *
4
+ * The pin is the local trust state for one account: (`accountName`, `AK`) at first contact, then
5
+ * the greatest accepted device generation with its certificate hash and device keys. Everything
6
+ * here is a pure decision over a pin and a candidate certificate; persistence is the store's job.
7
+ */
8
+ import { concat, equalBytes, toHex } from "./bytes.js";
9
+ import { DocumentType, buildAccountDevice, expectType, verifyDocument } from "./codec.js";
10
+ import { DEVICE_ID_BYTES, SEED_BYTES, accountKeypair, deviceBoxKeypair, deviceSigningKeypair, identityId, randomBytes, streamId } from "./crypto.js";
11
+ import { KavachError } from "./errors.js";
12
+ export function pinFromCertificate(doc, previous) {
13
+ const iid = identityId(doc.accountName, doc.accountKey);
14
+ const ids = previous ? [...previous.deviceIds] : [];
15
+ if (!ids.some((d) => equalBytes(d, doc.deviceId)))
16
+ ids.push(doc.deviceId);
17
+ return {
18
+ accountName: doc.accountName,
19
+ accountKey: doc.accountKey,
20
+ identityId: iid,
21
+ generation: doc.generation,
22
+ certificateHash: doc.bodyHash,
23
+ certificate: concat(doc.body, doc.signature),
24
+ deviceId: doc.deviceId,
25
+ signingKey: doc.signingKey,
26
+ boxKey: doc.boxKey,
27
+ streamId: streamId(iid, doc.deviceId),
28
+ deviceIds: ids
29
+ };
30
+ }
31
+ /**
32
+ * Accept or reject a certificate against the local pin for its account.
33
+ *
34
+ * - No pin yet: first contact. If `expected` names the account the directory resolved, the
35
+ * certificate must carry that name and key; the pin is created and nothing rotates.
36
+ * - A pinned account: a different `AK` is `ACCOUNT_KEY_CHANGED` and the caller quarantines
37
+ * (never closing or re-admitting anything); a lower generation is `DEVICE_ROLLBACK`; the same
38
+ * generation is a duplicate or `EQUIVOCATION`; a higher one with a reused device id is
39
+ * `DEVICE_ID_REUSED`; otherwise it is accepted and the audience must rotate.
40
+ */
41
+ export function acceptCertificate(current, wire, expected) {
42
+ const stage = "acceptCertificate";
43
+ const doc = expectType(verifyDocument(wire), DocumentType.AccountDevice, stage);
44
+ const anchor = current ?? expected;
45
+ if (anchor && (anchor.accountName !== doc.accountName || !equalBytes(anchor.accountKey, doc.accountKey))) {
46
+ throw new KavachError("ACCOUNT_KEY_CHANGED", stage, `certificate for ${doc.accountName} carries a different account key`);
47
+ }
48
+ if (!current)
49
+ return { outcome: "ACCEPTED", pin: pinFromCertificate(doc), rotate: false, previous: undefined };
50
+ if (doc.generation < current.generation)
51
+ throw new KavachError("DEVICE_ROLLBACK", stage);
52
+ if (doc.generation === current.generation) {
53
+ if (equalBytes(doc.bodyHash, current.certificateHash))
54
+ return { outcome: "DUPLICATE", pin: current };
55
+ throw new KavachError("EQUIVOCATION", stage, "two certificates at one generation");
56
+ }
57
+ if (current.deviceIds.some((d) => equalBytes(d, doc.deviceId)))
58
+ throw new KavachError("DEVICE_ID_REUSED", stage);
59
+ return { outcome: "ACCEPTED", pin: pinFromCertificate(doc, current), rotate: true, previous: current };
60
+ }
61
+ export function openAccount(accountName, accountSeed) {
62
+ if (accountSeed.length !== SEED_BYTES)
63
+ throw new KavachError("INVALID_KEY", "openAccount");
64
+ const account = accountKeypair(accountSeed);
65
+ return { accountName, accountSeed, account, identityId: identityId(accountName, account.publicKey) };
66
+ }
67
+ export function newAccountSeed() {
68
+ return randomBytes(SEED_BYTES);
69
+ }
70
+ /** A fresh installation: new device seed and a random nonzero device id. Never backed up. */
71
+ export function newDevice() {
72
+ const deviceSeed = randomBytes(SEED_BYTES);
73
+ let deviceId = randomBytes(DEVICE_ID_BYTES);
74
+ while (deviceId.every((b) => b === 0))
75
+ deviceId = randomBytes(DEVICE_ID_BYTES);
76
+ return deviceFromSeed(deviceSeed, deviceId);
77
+ }
78
+ export function deviceFromSeed(deviceSeed, deviceId) {
79
+ if (deviceId.length !== DEVICE_ID_BYTES || deviceId.every((b) => b === 0))
80
+ throw new KavachError("MALFORMED", "device", "device id");
81
+ return { deviceSeed, deviceId, signing: deviceSigningKeypair(deviceSeed), box: deviceBoxKeypair(deviceSeed) };
82
+ }
83
+ /** Build and sign this account's certificate for a device at a generation. */
84
+ export function certify(account, device, generation) {
85
+ if (generation < 1n)
86
+ throw new KavachError("MALFORMED", "certify", "generation");
87
+ return buildAccountDevice({
88
+ accountName: account.accountName,
89
+ accountKey: account.account.publicKey,
90
+ generation,
91
+ deviceId: device.deviceId,
92
+ signingKey: device.signing.publicKey,
93
+ boxKey: device.box.publicKey
94
+ }, account.account.secretKey);
95
+ }
96
+ export function pinKey(identity) {
97
+ return toHex(identity);
98
+ }
99
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ import { type RecordKind } from "./codec.js";
2
+ import { type SelfView } from "./epoch.js";
3
+ import { type RetentionPolicy } from "./policy.js";
4
+ import type { Store, Tx } from "./store.js";
5
+ import type { Relay } from "./transport.js";
6
+ export declare const MAX_FUTURE_SKEW_MS = 300000n;
7
+ export declare const READ_BATCH = 100;
8
+ export type ReceiveResult = {
9
+ readonly result: "updated";
10
+ readonly recordId: Uint8Array;
11
+ readonly authorIdentityId: Uint8Array;
12
+ readonly kind: RecordKind;
13
+ } | {
14
+ readonly result: "history";
15
+ readonly recordId: Uint8Array;
16
+ readonly authorIdentityId: Uint8Array;
17
+ readonly kind: RecordKind;
18
+ }
19
+ /** Verified, of a slot-only kind, and older than the slot's holder (or from a retired device): nothing kept. */
20
+ | {
21
+ readonly result: "older";
22
+ readonly recordId: Uint8Array;
23
+ readonly authorIdentityId: Uint8Array;
24
+ readonly kind: RecordKind;
25
+ } | {
26
+ readonly result: "duplicate";
27
+ readonly recordId: Uint8Array;
28
+ } | {
29
+ readonly result: "keyPending";
30
+ readonly streamId: Uint8Array;
31
+ readonly epoch: number;
32
+ } | {
33
+ readonly result: "expired";
34
+ readonly reason: "window" | "CLOCK_SKEW";
35
+ } | {
36
+ readonly result: "invalid";
37
+ readonly reason: string;
38
+ };
39
+ export interface InboxEvents {
40
+ received?(r: ReceiveResult): void;
41
+ /** The author's current epoch has no key for this device yet: show the person as waiting. */
42
+ waiting?(authorIdentityId: Uint8Array): void;
43
+ securityError?(code: string, detail: string): void;
44
+ }
45
+ export interface InboxDeps {
46
+ readonly store: Store;
47
+ readonly relay: Relay;
48
+ readonly policy: RetentionPolicy;
49
+ readonly now: () => bigint;
50
+ readonly self: () => SelfView;
51
+ readonly events?: InboxEvents;
52
+ }
53
+ /** A record pushed by the host outside `sync`: stored, but it never advances a relay cursor. */
54
+ export declare function receive(deps: InboxDeps, wire: Uint8Array): Promise<ReceiveResult>;
55
+ export interface StreamSyncSummary {
56
+ read: number;
57
+ stored: number;
58
+ duplicate: number;
59
+ keyPending: number;
60
+ invalid: number;
61
+ expired: number;
62
+ acked: bigint | undefined;
63
+ /** The relay holds more records past the last page read; the budget stopped the catch-up. */
64
+ hasMore: boolean;
65
+ }
66
+ /**
67
+ * One pass over one author stream: read a batch above the local scan position, process every
68
+ * record, hold the prefix at any `keyPending` record, and send at most one acknowledgement.
69
+ */
70
+ /** What one sync may still read across every stream: records left and the time it must stop at. */
71
+ export interface ReadBudget {
72
+ remainingRecords: number;
73
+ readonly deadline: bigint;
74
+ }
75
+ /**
76
+ * Read a stream in pages until it is caught up or the budget is spent. The first page restarts
77
+ * from the eligible prefix while a record is unresolved (the held record is read again; stored
78
+ * ones come back as harmless duplicates); later pages of the same sync continue from the last
79
+ * sequence examined, so a held record never makes the loop re-read its own page. Each page is
80
+ * processed and acknowledged before the next is read. `hasMore` says the relay still holds
81
+ * records past the last page when the budget stopped the loop.
82
+ */
83
+ export declare function syncStream(deps: InboxDeps, streamId: Uint8Array, authorIdentityId: Uint8Array, budget?: ReadBudget): Promise<StreamSyncSummary>;
84
+ export interface HistoryEntry {
85
+ readonly recordId: Uint8Array;
86
+ readonly authorIdentityId: Uint8Array;
87
+ readonly kind: RecordKind;
88
+ /** The sender's event time for the record, when it gave one. */
89
+ readonly eventTime: bigint | undefined;
90
+ readonly sentAt: bigint;
91
+ readonly receivedAt: bigint;
92
+ readonly plaintext: Uint8Array;
93
+ readonly superseded: boolean;
94
+ readonly own: boolean;
95
+ }
96
+ /**
97
+ * A history query. Every filter is served from the index: an author, a kind, a `sentAt` window
98
+ * (`since` inclusive, `before` exclusive), the order and a page size. `cursor` continues a page
99
+ * from `HistoryPage.cursor`; it names a fixed position in time order, so pages stay stable while
100
+ * records arrive or expire.
101
+ */
102
+ export interface HistoryQuery {
103
+ readonly authorIdentityId?: Uint8Array;
104
+ readonly kind?: RecordKind;
105
+ readonly since?: bigint;
106
+ readonly before?: bigint;
107
+ /** At most this many entries; 1000 when absent. */
108
+ readonly limit?: number;
109
+ /** Oldest first by default; `"desc"` reads newest first. */
110
+ readonly order?: "asc" | "desc";
111
+ readonly cursor?: string;
112
+ }
113
+ export interface HistoryPage {
114
+ readonly entries: HistoryEntry[];
115
+ /** Present when the page was full: pass it back as `cursor` for the next page. */
116
+ readonly cursor: string | undefined;
117
+ }
118
+ export declare const HISTORY_PAGE = 1000;
119
+ /** One page of history in time order, read through the index: one bounded scan per kind, merged. */
120
+ export declare function historyPage(store: Store, policy: RetentionPolicy, q?: HistoryQuery): Promise<HistoryPage>;
121
+ /** The first page only, for callers that want a plain list. */
122
+ export declare function history(store: Store, policy: RetentionPolicy, q?: HistoryQuery): Promise<HistoryEntry[]>;
123
+ /** The newest history entry matching the filters, read as a one-entry descending page. */
124
+ export declare function newest(store: Store, policy: RetentionPolicy, q?: Pick<HistoryQuery, "authorIdentityId" | "kind">): Promise<HistoryEntry | undefined>;
125
+ /** An author's newest record of a kind: the slot's holder, with the event time it was chosen by and its age. */
126
+ export interface Newest {
127
+ readonly recordId: Uint8Array;
128
+ readonly kind: number;
129
+ readonly payload: Uint8Array;
130
+ readonly sentAt: bigint;
131
+ /** The event time the slot was ordered by, never later than `sentAt`. */
132
+ readonly eventTime: bigint;
133
+ readonly ageMs: bigint;
134
+ }
135
+ /** The newest slot of an author and kind, or `undefined` when there is none or its payload expired (the slot stays, the payload is gone). */
136
+ export declare function newestSlot(store: Store, authorIdentityId: Uint8Array, kind: number, now: bigint): Promise<Newest | undefined>;
137
+ /**
138
+ * Build the slots once for a store written before the slot table: every non-superseded history
139
+ * row of a kind declared `newest` is offered to its slot in time order. A fresh store is marked
140
+ * at T1 and never runs this. Idempotent.
141
+ */
142
+ export declare function ensureSlots(store: Store, policy: RetentionPolicy): Promise<boolean>;
143
+ /**
144
+ * Retention, per declared kind: drop history past the kind's window, and drop the payload of a
145
+ * newest slot past it while the row keeps its order, so an older record replayed later cannot
146
+ * become current.
147
+ */
148
+ export declare function purgeHistory(store: Store, policy: RetentionPolicy, now: bigint): Promise<number>;
149
+ /** Clear a removed author's newest slots and keys; history already received stays. */
150
+ export declare function forgetAuthor(store: Store, authorIdentityId: Uint8Array): Promise<void>;
151
+ /** The same, inside a caller's transaction. */
152
+ export declare function forgetAuthorIn(tx: Tx, authorIdentityId: Uint8Array): void;
153
+ //# sourceMappingURL=inbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"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"}