@noy-db/hub 0.1.0-pre.10

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 (203) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/dist/aggregate/index.cjs +476 -0
  4. package/dist/aggregate/index.cjs.map +1 -0
  5. package/dist/aggregate/index.d.cts +38 -0
  6. package/dist/aggregate/index.d.ts +38 -0
  7. package/dist/aggregate/index.js +53 -0
  8. package/dist/aggregate/index.js.map +1 -0
  9. package/dist/blobs/index.cjs +1480 -0
  10. package/dist/blobs/index.cjs.map +1 -0
  11. package/dist/blobs/index.d.cts +45 -0
  12. package/dist/blobs/index.d.ts +45 -0
  13. package/dist/blobs/index.js +48 -0
  14. package/dist/blobs/index.js.map +1 -0
  15. package/dist/bundle/index.cjs +496 -0
  16. package/dist/bundle/index.cjs.map +1 -0
  17. package/dist/bundle/index.d.cts +7 -0
  18. package/dist/bundle/index.d.ts +7 -0
  19. package/dist/bundle/index.js +51 -0
  20. package/dist/bundle/index.js.map +1 -0
  21. package/dist/chunk-2QR2PQTT.js +217 -0
  22. package/dist/chunk-2QR2PQTT.js.map +1 -0
  23. package/dist/chunk-72UIIX3E.js +1109 -0
  24. package/dist/chunk-72UIIX3E.js.map +1 -0
  25. package/dist/chunk-A4NFZKRW.js +722 -0
  26. package/dist/chunk-A4NFZKRW.js.map +1 -0
  27. package/dist/chunk-AOYCZP2H.js +793 -0
  28. package/dist/chunk-AOYCZP2H.js.map +1 -0
  29. package/dist/chunk-CIMZBAZB.js +72 -0
  30. package/dist/chunk-CIMZBAZB.js.map +1 -0
  31. package/dist/chunk-E3AGCGJ4.js +160 -0
  32. package/dist/chunk-E3AGCGJ4.js.map +1 -0
  33. package/dist/chunk-EKX3YVCI.js +97 -0
  34. package/dist/chunk-EKX3YVCI.js.map +1 -0
  35. package/dist/chunk-EMIGCR7X.js +39 -0
  36. package/dist/chunk-EMIGCR7X.js.map +1 -0
  37. package/dist/chunk-EMMRIE3C.js +72 -0
  38. package/dist/chunk-EMMRIE3C.js.map +1 -0
  39. package/dist/chunk-EUNIORPU.js +680 -0
  40. package/dist/chunk-EUNIORPU.js.map +1 -0
  41. package/dist/chunk-FZU343FL.js +32 -0
  42. package/dist/chunk-FZU343FL.js.map +1 -0
  43. package/dist/chunk-GHGXG53C.js +795 -0
  44. package/dist/chunk-GHGXG53C.js.map +1 -0
  45. package/dist/chunk-GKA4BGJN.js +79 -0
  46. package/dist/chunk-GKA4BGJN.js.map +1 -0
  47. package/dist/chunk-HG2OWBLX.js +430 -0
  48. package/dist/chunk-HG2OWBLX.js.map +1 -0
  49. package/dist/chunk-IGAROPKM.js +34 -0
  50. package/dist/chunk-IGAROPKM.js.map +1 -0
  51. package/dist/chunk-J66GRPNH.js +111 -0
  52. package/dist/chunk-J66GRPNH.js.map +1 -0
  53. package/dist/chunk-LVMMDXFT.js +275 -0
  54. package/dist/chunk-LVMMDXFT.js.map +1 -0
  55. package/dist/chunk-M5INGEFC.js +84 -0
  56. package/dist/chunk-M5INGEFC.js.map +1 -0
  57. package/dist/chunk-NBYQNDXA.js +557 -0
  58. package/dist/chunk-NBYQNDXA.js.map +1 -0
  59. package/dist/chunk-NPC4LFV5.js +132 -0
  60. package/dist/chunk-NPC4LFV5.js.map +1 -0
  61. package/dist/chunk-NSWHB5VQ.js +1285 -0
  62. package/dist/chunk-NSWHB5VQ.js.map +1 -0
  63. package/dist/chunk-OLM4LA6K.js +392 -0
  64. package/dist/chunk-OLM4LA6K.js.map +1 -0
  65. package/dist/chunk-UAFBZWFB.js +155 -0
  66. package/dist/chunk-UAFBZWFB.js.map +1 -0
  67. package/dist/chunk-UF3BUNQZ.js +1 -0
  68. package/dist/chunk-UF3BUNQZ.js.map +1 -0
  69. package/dist/chunk-UMMAVAYW.js +17 -0
  70. package/dist/chunk-UMMAVAYW.js.map +1 -0
  71. package/dist/chunk-UPY7WLBH.js +381 -0
  72. package/dist/chunk-UPY7WLBH.js.map +1 -0
  73. package/dist/chunk-W63BWEJH.js +311 -0
  74. package/dist/chunk-W63BWEJH.js.map +1 -0
  75. package/dist/chunk-WIGI5OJK.js +90 -0
  76. package/dist/chunk-WIGI5OJK.js.map +1 -0
  77. package/dist/chunk-XNL2TKKR.js +490 -0
  78. package/dist/chunk-XNL2TKKR.js.map +1 -0
  79. package/dist/chunk-XWNUJPIS.js +367 -0
  80. package/dist/chunk-XWNUJPIS.js.map +1 -0
  81. package/dist/chunk-YWKJZZGV.js +715 -0
  82. package/dist/chunk-YWKJZZGV.js.map +1 -0
  83. package/dist/consent/index.cjs +204 -0
  84. package/dist/consent/index.cjs.map +1 -0
  85. package/dist/consent/index.d.cts +24 -0
  86. package/dist/consent/index.d.ts +24 -0
  87. package/dist/consent/index.js +23 -0
  88. package/dist/consent/index.js.map +1 -0
  89. package/dist/crdt/index.cjs +152 -0
  90. package/dist/crdt/index.cjs.map +1 -0
  91. package/dist/crdt/index.d.cts +30 -0
  92. package/dist/crdt/index.d.ts +30 -0
  93. package/dist/crdt/index.js +24 -0
  94. package/dist/crdt/index.js.map +1 -0
  95. package/dist/crypto-6PNIHP7W.js +44 -0
  96. package/dist/crypto-6PNIHP7W.js.map +1 -0
  97. package/dist/delegation-WVIVMF73.js +17 -0
  98. package/dist/delegation-WVIVMF73.js.map +1 -0
  99. package/dist/dev-unlock-D4xB0_gs.d.cts +263 -0
  100. package/dist/dev-unlock-Dz8GEbd3.d.ts +263 -0
  101. package/dist/hash--EflSV65.d.cts +63 -0
  102. package/dist/hash-CRdXYnv3.d.ts +63 -0
  103. package/dist/history/index.cjs +1215 -0
  104. package/dist/history/index.cjs.map +1 -0
  105. package/dist/history/index.d.cts +62 -0
  106. package/dist/history/index.d.ts +62 -0
  107. package/dist/history/index.js +79 -0
  108. package/dist/history/index.js.map +1 -0
  109. package/dist/i18n/index.cjs +840 -0
  110. package/dist/i18n/index.cjs.map +1 -0
  111. package/dist/i18n/index.d.cts +38 -0
  112. package/dist/i18n/index.d.ts +38 -0
  113. package/dist/i18n/index.js +68 -0
  114. package/dist/i18n/index.js.map +1 -0
  115. package/dist/index-CD1VnONm.d.cts +415 -0
  116. package/dist/index-CLRxPs-W.d.cts +1960 -0
  117. package/dist/index-CUi9wfss.d.ts +415 -0
  118. package/dist/index-DtV93TMP.d.ts +1960 -0
  119. package/dist/index.cjs +17387 -0
  120. package/dist/index.cjs.map +1 -0
  121. package/dist/index.d.cts +565 -0
  122. package/dist/index.d.ts +565 -0
  123. package/dist/index.js +7525 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/indexing/index.cjs +736 -0
  126. package/dist/indexing/index.cjs.map +1 -0
  127. package/dist/indexing/index.d.cts +36 -0
  128. package/dist/indexing/index.d.ts +36 -0
  129. package/dist/indexing/index.js +77 -0
  130. package/dist/indexing/index.js.map +1 -0
  131. package/dist/lazy-builder-BwEoBQZ9.d.ts +304 -0
  132. package/dist/lazy-builder-CZVLKh0Z.d.cts +304 -0
  133. package/dist/ledger-HBBH2NPZ.js +33 -0
  134. package/dist/ledger-HBBH2NPZ.js.map +1 -0
  135. package/dist/mime-magic-CBBSOkjm.d.cts +50 -0
  136. package/dist/mime-magic-CBBSOkjm.d.ts +50 -0
  137. package/dist/periods/index.cjs +1035 -0
  138. package/dist/periods/index.cjs.map +1 -0
  139. package/dist/periods/index.d.cts +21 -0
  140. package/dist/periods/index.d.ts +21 -0
  141. package/dist/periods/index.js +25 -0
  142. package/dist/periods/index.js.map +1 -0
  143. package/dist/predicate-SBHmi6D0.d.cts +161 -0
  144. package/dist/predicate-SBHmi6D0.d.ts +161 -0
  145. package/dist/public-envelope-TLQA6REO.js +31 -0
  146. package/dist/public-envelope-TLQA6REO.js.map +1 -0
  147. package/dist/query/index.cjs +1999 -0
  148. package/dist/query/index.cjs.map +1 -0
  149. package/dist/query/index.d.cts +3 -0
  150. package/dist/query/index.d.ts +3 -0
  151. package/dist/query/index.js +73 -0
  152. package/dist/query/index.js.map +1 -0
  153. package/dist/session/index.cjs +495 -0
  154. package/dist/session/index.cjs.map +1 -0
  155. package/dist/session/index.d.cts +45 -0
  156. package/dist/session/index.d.ts +45 -0
  157. package/dist/session/index.js +51 -0
  158. package/dist/session/index.js.map +1 -0
  159. package/dist/shadow/index.cjs +133 -0
  160. package/dist/shadow/index.cjs.map +1 -0
  161. package/dist/shadow/index.d.cts +16 -0
  162. package/dist/shadow/index.d.ts +16 -0
  163. package/dist/shadow/index.js +20 -0
  164. package/dist/shadow/index.js.map +1 -0
  165. package/dist/store/index.cjs +1083 -0
  166. package/dist/store/index.cjs.map +1 -0
  167. package/dist/store/index.d.cts +491 -0
  168. package/dist/store/index.d.ts +491 -0
  169. package/dist/store/index.js +37 -0
  170. package/dist/store/index.js.map +1 -0
  171. package/dist/strategy-BSxFXGzb.d.cts +110 -0
  172. package/dist/strategy-BSxFXGzb.d.ts +110 -0
  173. package/dist/strategy-D-SrOLCl.d.cts +548 -0
  174. package/dist/strategy-D-SrOLCl.d.ts +548 -0
  175. package/dist/sync/index.cjs +1062 -0
  176. package/dist/sync/index.cjs.map +1 -0
  177. package/dist/sync/index.d.cts +42 -0
  178. package/dist/sync/index.d.ts +42 -0
  179. package/dist/sync/index.js +28 -0
  180. package/dist/sync/index.js.map +1 -0
  181. package/dist/team/index.cjs +2606 -0
  182. package/dist/team/index.cjs.map +1 -0
  183. package/dist/team/index.d.cts +117 -0
  184. package/dist/team/index.d.ts +117 -0
  185. package/dist/team/index.js +106 -0
  186. package/dist/team/index.js.map +1 -0
  187. package/dist/tx/index.cjs +212 -0
  188. package/dist/tx/index.cjs.map +1 -0
  189. package/dist/tx/index.d.cts +20 -0
  190. package/dist/tx/index.d.ts +20 -0
  191. package/dist/tx/index.js +20 -0
  192. package/dist/tx/index.js.map +1 -0
  193. package/dist/types-DSFLtbKg.d.ts +9702 -0
  194. package/dist/types-zwwMOqkg.d.cts +9702 -0
  195. package/dist/ulid-COREQ2RQ.js +9 -0
  196. package/dist/ulid-COREQ2RQ.js.map +1 -0
  197. package/dist/util/index.cjs +230 -0
  198. package/dist/util/index.cjs.map +1 -0
  199. package/dist/util/index.d.cts +77 -0
  200. package/dist/util/index.d.ts +77 -0
  201. package/dist/util/index.js +190 -0
  202. package/dist/util/index.js.map +1 -0
  203. package/package.json +244 -0
@@ -0,0 +1,415 @@
1
+ import { aS as PublicEnvelope, b0 as BundleRecipient, aY as Vault } from './types-DSFLtbKg.js';
2
+ import './index-DtV93TMP.js';
3
+
4
+ /**
5
+ * `.noydb` container format — byte layout, header schema, validators.
6
+ *
7
+ *. Wraps a `vault.dump()` JSON string in a thin
8
+ * binary container with a magic-byte prefix, a minimum-disclosure
9
+ * unencrypted header, and a compressed body.
10
+ *
11
+ * **Byte layout** (read in order from offset 0):
12
+ *
13
+ * ```
14
+ * +--------+--------+--------+--------+
15
+ * | N=78 | D=68 | B=66 | 1=49 | Magic 'NDB1' (4 bytes)
16
+ * +--------+--------+--------+--------+
17
+ * | flags | compr | header_length (uint32 BE) |
18
+ * +--------+--------+--------+--------+--------+--------+--------+
19
+ * | header_length bytes of UTF-8 JSON header ...
20
+ * +--------+--------+
21
+ * | compressed body bytes ...
22
+ * ```
23
+ *
24
+ * Total fixed prefix before the header JSON is **10 bytes**:
25
+ * - 4 bytes magic
26
+ * - 1 byte flags
27
+ * - 1 byte compression algorithm
28
+ * - 4 bytes header length (uint32 big-endian)
29
+ *
30
+ * **Why a binary container** at all? `vault.dump()` already
31
+ * produces a JSON string with encrypted records inside. Wrapping it
32
+ * again seems redundant — but the wrap is what makes the file safe
33
+ * to drop into cloud storage (Drive, Dropbox, iCloud) without
34
+ * leaking the vault name and exporter identity through the
35
+ * cloud's metadata API. The minimum-disclosure header is the only
36
+ * thing visible without downloading and decompressing the body.
37
+ * The dump JSON inside the body still contains the original
38
+ * metadata, but that's only readable by someone who already has the
39
+ * file bytes — the same person who could read the encrypted records
40
+ * with the right passphrase.
41
+ *
42
+ * **Why minimum disclosure** in the header? Because consumers will
43
+ * inevitably store these in services where the filename, file size,
44
+ * and any unencrypted metadata are indexed for search. A field like
45
+ * `vault: "Acme Corp"` would let an attacker (or a curious
46
+ * cloud admin) enumerate which compartments exist and who exported
47
+ * them, even with zero access to the encrypted body. The header
48
+ * carries only what's needed to identify the file as a NOYDB
49
+ * bundle and verify its integrity — nothing about the contents.
50
+ */
51
+
52
+ /** Magic bytes 'NDB1' (ASCII), identifying a NOYDB bundle. */
53
+ declare const NOYDB_BUNDLE_MAGIC: Uint8Array<ArrayBuffer>;
54
+ /** Total fixed prefix before the header JSON: 4+1+1+4 bytes. */
55
+ declare const NOYDB_BUNDLE_PREFIX_BYTES = 10;
56
+ /** Current bundle format version. Bumped on layout changes. */
57
+ declare const NOYDB_BUNDLE_FORMAT_VERSION = 1;
58
+ /**
59
+ * Bitfield interpretation of the flags byte.
60
+ *
61
+ * Bit 0 — body is compressed (0 = raw, 1 = compressed)
62
+ * Bit 1 — header carries an integrity hash over the body bytes
63
+ * Bits 2-7 — reserved, must be 0 in
64
+ */
65
+ declare const FLAG_COMPRESSED = 1;
66
+ declare const FLAG_HAS_INTEGRITY_HASH = 2;
67
+ /**
68
+ * Compression algorithm encoding for the byte at offset 5.
69
+ *
70
+ * `none` is admitted for round-trip testing and for callers that
71
+ * want to bundle without compression (e.g. when piping into a
72
+ * separately compressed transport). `gzip` is the universally
73
+ * available baseline (Node 18+, all modern browsers). `brotli` is
74
+ * preferred when the runtime supports it — typically 30-50% smaller
75
+ * for JSON payloads — but Node 22+ / Chrome 124+ / Firefox 122+
76
+ * are required, so the writer feature-detects at runtime and falls
77
+ * back to gzip. The reader must handle all three.
78
+ */
79
+ declare const COMPRESSION_NONE = 0;
80
+ declare const COMPRESSION_GZIP = 1;
81
+ declare const COMPRESSION_BROTLI = 2;
82
+ type CompressionAlgo = 0 | 1 | 2;
83
+ /**
84
+ * The unencrypted header carried in every `.noydb` bundle.
85
+ *
86
+ * **Minimum-disclosure rules:** these are the ONLY allowed keys.
87
+ * Any other key in a parsed header causes
88
+ * `validateBundleHeader` to throw. The set is kept short to
89
+ * minimize attack surface from cloud-storage metadata indexing —
90
+ * see the file-level doc comment for the rationale.
91
+ *
92
+ * Forbidden in particular:
93
+ * - `vault` / `_compartment` — would leak the tenant name
94
+ * - `exporter` / `_exported_by` — would leak user identity
95
+ * - `timestamp` / `_exported_at` — would leak activity timing
96
+ * - `kdfParams` / salt fields — would leak crypto config that
97
+ * could narrow brute-force search space
98
+ * - any field starting with `_` (reserved by the dump format)
99
+ */
100
+ interface NoydbBundleHeader {
101
+ /** Bundle format version — bumped on layout changes. */
102
+ readonly formatVersion: number;
103
+ /**
104
+ * Opaque ULID identifier — generated once per vault and
105
+ * stable across re-exports of the same vault. Does not
106
+ * leak any information about contents (the timestamp prefix is
107
+ * just monotonicity for sortability, not exporter activity —
108
+ * see `bundle/ulid.ts` for the design notes).
109
+ */
110
+ readonly handle: string;
111
+ /** Compressed body length in bytes. Lets readers verify completeness without decompressing. */
112
+ readonly bodyBytes: number;
113
+ /** SHA-256 of the compressed body bytes (lowercase hex). Lets readers verify integrity without decompressing. */
114
+ readonly bodySha256: string;
115
+ /**
116
+ * Owner-curated public envelope (`docs/subsystems/public-envelope.md`).
117
+ * Optional — present only when the source vault has a
118
+ * `_meta/public-envelope` document AND the writer's hub is opted
119
+ * into the feature. Treat as **untrusted hint**; the body's
120
+ * encrypted contents remain the source of truth.
121
+ *
122
+ * The envelope deliberately widens the minimum-disclosure rule
123
+ * for explicit, owner-curated label fields (name, icon, …). Every
124
+ * other unknown header key still rejects at parse time.
125
+ */
126
+ readonly publicEnvelope?: PublicEnvelope;
127
+ }
128
+ /**
129
+ * Validate a parsed bundle header. Throws on any deviation from
130
+ * the minimum-disclosure schema:
131
+ *
132
+ * - Missing required field
133
+ * - Wrong type for any field
134
+ * - Any extra key not in `ALLOWED_HEADER_KEYS`
135
+ * - Unsupported `formatVersion`
136
+ * - Negative or non-integer `bodyBytes`
137
+ * - Malformed `handle` (must be 26-char Crockford base32)
138
+ * - Malformed `bodySha256` (must be 64-char lowercase hex)
139
+ *
140
+ * The error messages name the offending field so consumers can
141
+ * fix the producer rather than the reader.
142
+ */
143
+ declare function validateBundleHeader(parsed: unknown): asserts parsed is NoydbBundleHeader;
144
+ /**
145
+ * Encode a header object to UTF-8 JSON bytes after validating
146
+ * minimum disclosure. Used by the writer to serialize the header
147
+ * region of the container.
148
+ */
149
+ declare function encodeBundleHeader(header: NoydbBundleHeader): Uint8Array;
150
+ /**
151
+ * Verify the magic prefix of a bundle. Returns true if the first
152
+ * 4 bytes match `NDB1`. Used by readers as a fast file-type check
153
+ * before any further parsing.
154
+ */
155
+ declare function hasNoydbBundleMagic(bytes: Uint8Array): boolean;
156
+
157
+ /**
158
+ * `.noydb` container primitives — write, read, header-only read.
159
+ *
160
+ *. Wraps a `vault.dump()` JSON string in the
161
+ * binary container described in `format.ts`.
162
+ *
163
+ * **Three primitives:**
164
+ *
165
+ * - `writeNoydbBundle(vault, opts?)` — produces the
166
+ * full container bytes ready to write to disk or upload
167
+ * - `readNoydbBundleHeader(bytes)` — parses just the header
168
+ * without decompressing the body, fast file-type and
169
+ * metadata read for cloud listing UIs
170
+ * - `readNoydbBundle(bytes)` — full read: validates magic,
171
+ * header, integrity hash, and decompresses the body to
172
+ * return the original `dump()` JSON string for use with
173
+ * `vault.load()`
174
+ *
175
+ * **Compression strategy:** brotli when available (Node 22+,
176
+ * Chrome 124+, Firefox 122+), gzip fallback elsewhere. The
177
+ * algorithm choice is encoded in the format byte at offset 5,
178
+ * so readers handle either transparently. Brotli wins ~30-50%
179
+ * on JSON payloads with repeated keys (which vault dumps
180
+ * are).
181
+ *
182
+ * **Why split read/load?** `readNoydbBundle` returns the
183
+ * *unwrapped JSON string*, not a Vault object. The caller
184
+ * is responsible for piping that JSON into
185
+ * `vault.load(json, passphrase)`. Splitting the layers
186
+ * keeps the bundle module free of any crypto/passphrase
187
+ * concerns — it's purely a format layer. The same `readNoydbBundle`
188
+ * call can also feed verification tools, format inspectors, or
189
+ * archive utilities that don't care about decryption.
190
+ */
191
+
192
+ /**
193
+ * Options accepted by `writeNoydbBundle`.
194
+ *
195
+ * - `compression: 'auto'` (default) — try brotli, fall back to gzip
196
+ * - `compression: 'brotli'` — force brotli, throw if unsupported
197
+ * - `compression: 'gzip'` — force gzip
198
+ * - `compression: 'none'` — no compression (round-trip testing only)
199
+ *
200
+ * **Slice filtering** (added in ):
201
+ * - `collections` — allowlist of collection names to include. Internal
202
+ * collections (keyrings, ledger) and excluded user collections are
203
+ * dropped from the bundle. Records inside included collections are
204
+ * carried through verbatim.
205
+ * - `since` — only records whose envelope `_ts` is on/after the given
206
+ * instant survive. Operates on the unencrypted envelope timestamp,
207
+ * so plaintext access to records is not required.
208
+ *
209
+ * Both filters intersect (AND). When neither is provided the bundle is
210
+ * a whole-vault snapshot, identical to today's behaviour.
211
+ */
212
+ interface WriteNoydbBundleOptions {
213
+ readonly compression?: 'auto' | 'brotli' | 'gzip' | 'none';
214
+ /** Allowlist of user-collection names to include. */
215
+ readonly collections?: readonly string[];
216
+ /**
217
+ * Drop records whose envelope `_ts` is strictly older than this
218
+ * instant. Accepts a `Date` or any ISO-8601 string parseable by
219
+ * `new Date()`.
220
+ */
221
+ readonly since?: Date | string;
222
+ /**
223
+ * Plaintext-pipeline record predicate. Decrypts each record
224
+ * with the vault's per-collection DEK, runs the predicate, and
225
+ * keeps the original ciphertext for survivors (no re-encrypt —
226
+ * preserves zero-knowledge cleanly). Records the predicate returns
227
+ * `false` for are dropped from the bundle.
228
+ *
229
+ * Async predicates are supported. Mutating the record from inside
230
+ * the predicate is undefined behaviour.
231
+ */
232
+ readonly where?: (record: unknown, ctx: {
233
+ collection: string;
234
+ id: string;
235
+ }) => boolean | Promise<boolean>;
236
+ /**
237
+ * Hierarchical-tier ceiling. Records whose envelope `_tier`
238
+ * is strictly greater than this number are dropped. Operates on the
239
+ * envelope `_tier` (no decryption needed) — vault.exportStream is
240
+ * referenced in the issue body for symmetry, but the tier value
241
+ * lives on the unencrypted envelope. Vault without tiers is a no-op.
242
+ */
243
+ readonly tierAtMost?: number;
244
+ /**
245
+ * Single-recipient re-keying shorthand. When set, the
246
+ * bundle's keyring is replaced with one freshly-derived entry sealed
247
+ * with this passphrase. The recipient inherits the source keyring's
248
+ * userId, role, and permissions. Mutually exclusive with `recipients`.
249
+ */
250
+ readonly exportPassphrase?: string;
251
+ /**
252
+ * Multi-recipient re-keying. Replaces the bundle's keyring
253
+ * map with one slot per recipient, each sealed with its own
254
+ * passphrase. DEKs are unwrapped from the source keyring once and
255
+ * re-wrapped per recipient — record ciphertext is unchanged.
256
+ *
257
+ * Mutually exclusive with `exportPassphrase`. When neither is set,
258
+ * the bundle inherits the source keyring as-is (today's behaviour,
259
+ * suited to personal backup-and-restore).
260
+ */
261
+ readonly recipients?: readonly BundleRecipient[];
262
+ }
263
+ /**
264
+ * Result returned by `readNoydbBundle`. The caller is expected to
265
+ * pass `dumpJson` into `vault.load(json, passphrase)` to
266
+ * actually restore a vault. Splitting the layers keeps the
267
+ * bundle module free of crypto concerns — see file-level docs.
268
+ */
269
+ interface NoydbBundleReadResult {
270
+ readonly header: NoydbBundleHeader;
271
+ readonly dumpJson: string;
272
+ }
273
+ /** Test-only: reset the brotli detection cache between tests. */
274
+ declare function resetBrotliSupportCache(): void;
275
+ /**
276
+ * Write a `.noydb` bundle for the given vault.
277
+ *
278
+ * Pipeline:
279
+ * 1. Resolve or create the compartment's stable bundle handle
280
+ * via `vault.getBundleHandle()` — same handle on
281
+ * every export from the same vault instance, so cloud
282
+ * adapters can use it as a primary key.
283
+ * 2. `vault.dump()` → JSON string with encrypted records
284
+ * inside.
285
+ * 3. UTF-8 encode the dump string.
286
+ * 4. Compress (brotli if available, gzip fallback by default).
287
+ * 5. Compute SHA-256 of the compressed body for integrity.
288
+ * 6. Build the minimum-disclosure header from format version,
289
+ * handle, body length, body sha.
290
+ * 7. Serialize: magic (4) + flags (1) + algo (1) + headerLen (4)
291
+ * + header JSON (N) + compressed body (M).
292
+ *
293
+ * The output is a single `Uint8Array`. Consumers writing to disk
294
+ * pass it to `fs.writeFile`; consumers uploading to cloud storage
295
+ * pass it as the request body. The `@noy-db/file` adapter wraps
296
+ * this with a `saveBundle(path, vault)` helper.
297
+ */
298
+ declare function writeNoydbBundle(vault: Vault, opts?: WriteNoydbBundleOptions): Promise<Uint8Array>;
299
+ /**
300
+ * Read just the bundle header — no body decompression, no
301
+ * integrity verification. Intended for cloud-listing UIs that want
302
+ * to show the handle and size before downloading the full body.
303
+ *
304
+ * Returns the same `NoydbBundleHeader` shape as the writer, with
305
+ * minimum-disclosure validation already applied.
306
+ *
307
+ * **Cost** — O(prefix + header bytes). The header is normally well
308
+ * under 1 KB, but may grow to roughly 256 KB when a `publicEnvelope`
309
+ * with an inline icon is present. Cloud-listing UIs that previously
310
+ * assumed sub-KB header reads should account for this when sizing
311
+ * range requests against bundles that may carry icons.
312
+ */
313
+ declare function readNoydbBundleHeader(bytes: Uint8Array): NoydbBundleHeader;
314
+ /**
315
+ * Read just the bundle's public envelope (`docs/subsystems/public-envelope.md`)
316
+ * — without verifying the body or even parsing the dump JSON. Pass
317
+ * the raw bundle bytes; receive the owner-curated metadata or
318
+ * `undefined` if the bundle was written without one.
319
+ *
320
+ * Locale-resolves any `name` / `description` map fields when `locale`
321
+ * is supplied. Omitting `locale` returns the raw envelope.
322
+ *
323
+ * Same security caveat as the on-vault read path — the public
324
+ * envelope is **untrusted hint** in v1; the encrypted body remains
325
+ * the source of truth for vault contents.
326
+ */
327
+ declare function readNoydbBundlePublicEnvelope(bytes: Uint8Array, opts?: {
328
+ readonly locale?: string;
329
+ }): PublicEnvelope | undefined;
330
+ /**
331
+ * Read a full `.noydb` bundle: validate magic + header, verify
332
+ * integrity hash over the body bytes, decompress, and return the
333
+ * original `vault.dump()` JSON string ready to pass to
334
+ * `vault.load()`.
335
+ *
336
+ * Throws `BundleIntegrityError` if the body's actual SHA-256 does
337
+ * not match the value declared in the header. Distinct from a
338
+ * format error so consumers can pattern-match in catch blocks
339
+ * (corrupted-in-transit vs malformed-by-producer).
340
+ *
341
+ * Note: this function does NOT take a passphrase. The dump JSON
342
+ * inside the body still contains encrypted records — restoring
343
+ * the vault requires `vault.load(dumpJson, passphrase)`
344
+ * after this call. Splitting the layers keeps the bundle module
345
+ * free of crypto concerns and lets the same code feed format
346
+ * inspectors that never decrypt anything.
347
+ */
348
+ declare function readNoydbBundle(bytes: Uint8Array): Promise<NoydbBundleReadResult>;
349
+
350
+ /**
351
+ * Minimal ULID generator — zero dependencies, Web Crypto API only.
352
+ *
353
+ *. Used by the bundle writer to generate stable opaque
354
+ * handles for `.noydb` containers.
355
+ *
356
+ * **What's a ULID?** A 128-bit identifier encoded as 26 Crockford
357
+ * base32 characters. Layout:
358
+ *
359
+ * ```
360
+ * 01HYABCDEFGHJKMNPQRSTVWXYZ
361
+ * |--------||---------------|
362
+ * 48-bit 80-bit
363
+ * timestamp randomness
364
+ * ```
365
+ *
366
+ * The first 10 chars encode a millisecond Unix timestamp (so ULIDs
367
+ * sort lexicographically by creation time), and the remaining 16
368
+ * chars are random. Crockford base32 omits I/L/O/U to avoid
369
+ * ambiguity in handwriting and URLs.
370
+ *
371
+ * **Why hand-roll instead of pulling in `ulid`?** The package adds
372
+ * a dep, the implementation is ~30 lines, and the bundle module
373
+ * is the only consumer. Adding `ulid` would also drag in its own
374
+ * crypto polyfill that we don't need on Node 18+ or modern
375
+ * browsers.
376
+ *
377
+ * **Privacy consideration:** the timestamp prefix is observable in
378
+ * the bundle header. This is a deliberate trade-off:
379
+ * - Pro: lexicographic sortability lets bundle adapters list
380
+ * newest-first without an extra index.
381
+ * - Con: a casual observer can read the bundle's creation time
382
+ * from the handle. They cannot read it from any OTHER field
383
+ * (the header explicitly forbids `_exported_at`), and a
384
+ * creation timestamp is the same kind of metadata that
385
+ * filesystem mtime would already expose for a downloaded
386
+ * bundle. The leak is therefore equivalent to what's already
387
+ * visible from the file's mtime — not a new exposure.
388
+ *
389
+ * If a future use case needs timestamp-free handles, a v2 of the
390
+ * format could specify "use the random portion only" without a
391
+ * format break — `validateBundleHeader` only checks the regex
392
+ * shape, not the encoded timestamp.
393
+ */
394
+ /**
395
+ * Generate a fresh ULID. Uses `crypto.getRandomValues` for the
396
+ * randomness portion — same Web Crypto API the rest of the
397
+ * codebase uses for IVs and salt.
398
+ *
399
+ * Returns a 26-character string. Calling twice in the same
400
+ * millisecond produces two distinct ULIDs (the random portion
401
+ * differs); ULIDs from the same millisecond are NOT guaranteed
402
+ * to be monotonically ordered relative to each other, only
403
+ * relative to ULIDs from a different millisecond. The bundle
404
+ * format never relies on intra-millisecond ordering.
405
+ */
406
+ declare function generateULID(): string;
407
+ /**
408
+ * Validate that a string is a syntactically well-formed ULID. Used
409
+ * by the bundle header validator. Does NOT verify that the
410
+ * timestamp portion decodes to a sensible date — the format only
411
+ * cares about the encoding shape.
412
+ */
413
+ declare function isULID(value: string): boolean;
414
+
415
+ export { type CompressionAlgo as C, FLAG_COMPRESSED as F, NOYDB_BUNDLE_FORMAT_VERSION as N, type WriteNoydbBundleOptions as W, NOYDB_BUNDLE_MAGIC as a, NOYDB_BUNDLE_PREFIX_BYTES as b, type NoydbBundleHeader as c, type NoydbBundleReadResult as d, readNoydbBundleHeader as e, readNoydbBundlePublicEnvelope as f, generateULID as g, hasNoydbBundleMagic as h, isULID as i, resetBrotliSupportCache as j, COMPRESSION_BROTLI as k, COMPRESSION_GZIP as l, COMPRESSION_NONE as m, FLAG_HAS_INTEGRITY_HASH as n, encodeBundleHeader as o, readNoydbBundle as r, validateBundleHeader as v, writeNoydbBundle as w };