@soulcraft/cor 3.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +328 -0
  3. package/dist/aggregation/NativeAggregationEngine.d.ts +118 -0
  4. package/dist/aggregation/NativeAggregationEngine.js +186 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +236 -0
  7. package/dist/graph/GraphAccelerationAdapter.d.ts +153 -0
  8. package/dist/graph/GraphAccelerationAdapter.js +326 -0
  9. package/dist/graph/NativeGraphAdjacencyIndex.d.ts +286 -0
  10. package/dist/graph/NativeGraphAdjacencyIndex.js +944 -0
  11. package/dist/hnsw/AdaptiveDiskAnnModeSelector.d.ts +187 -0
  12. package/dist/hnsw/AdaptiveDiskAnnModeSelector.js +313 -0
  13. package/dist/hnsw/NativeDiskAnnWrapper.d.ts +489 -0
  14. package/dist/hnsw/NativeDiskAnnWrapper.js +1456 -0
  15. package/dist/index.d.ts +28 -0
  16. package/dist/index.js +29 -0
  17. package/dist/legacyLayoutGuard.d.ts +93 -0
  18. package/dist/legacyLayoutGuard.js +237 -0
  19. package/dist/license/constants.d.ts +30 -0
  20. package/dist/license/constants.js +30 -0
  21. package/dist/license/onlineValidator.d.ts +43 -0
  22. package/dist/license/onlineValidator.js +225 -0
  23. package/dist/license/types.d.ts +39 -0
  24. package/dist/license/types.js +5 -0
  25. package/dist/license.d.ts +64 -0
  26. package/dist/license.js +165 -0
  27. package/dist/migration/MigrationCoordinator.d.ts +171 -0
  28. package/dist/migration/MigrationCoordinator.js +249 -0
  29. package/dist/migration/indexEpochGuard.d.ts +163 -0
  30. package/dist/migration/indexEpochGuard.js +202 -0
  31. package/dist/native/NativeEmbeddingEngine.d.ts +79 -0
  32. package/dist/native/NativeEmbeddingEngine.js +318 -0
  33. package/dist/native/NativeRoaringBitmap32.d.ts +114 -0
  34. package/dist/native/NativeRoaringBitmap32.js +221 -0
  35. package/dist/native/ffi.d.ts +20 -0
  36. package/dist/native/ffi.js +48 -0
  37. package/dist/native/idMapperTestSupport.d.ts +67 -0
  38. package/dist/native/idMapperTestSupport.js +112 -0
  39. package/dist/native/index.d.ts +49 -0
  40. package/dist/native/index.js +87 -0
  41. package/dist/native/napi.d.ts +21 -0
  42. package/dist/native/napi.js +88 -0
  43. package/dist/native/types.d.ts +1298 -0
  44. package/dist/native/types.js +16 -0
  45. package/dist/plugin.d.ts +50 -0
  46. package/dist/plugin.js +388 -0
  47. package/dist/providerContracts.d.ts +277 -0
  48. package/dist/providerContracts.js +38 -0
  49. package/dist/resource/OsMemoryProbe.d.ts +175 -0
  50. package/dist/resource/OsMemoryProbe.js +206 -0
  51. package/dist/resource/ResourceManager.d.ts +491 -0
  52. package/dist/resource/ResourceManager.js +960 -0
  53. package/dist/utils/ColumnManifest.d.ts +97 -0
  54. package/dist/utils/ColumnManifest.js +129 -0
  55. package/dist/utils/NativeColumnStore.d.ts +284 -0
  56. package/dist/utils/NativeColumnStore.js +685 -0
  57. package/dist/utils/NativeMetadataIndex.d.ts +882 -0
  58. package/dist/utils/NativeMetadataIndex.js +2631 -0
  59. package/dist/utils/NativeUnifiedCache.d.ts +87 -0
  60. package/dist/utils/NativeUnifiedCache.js +273 -0
  61. package/dist/utils/binaryIdMapperFactory.d.ts +59 -0
  62. package/dist/utils/binaryIdMapperFactory.js +94 -0
  63. package/dist/utils/collation.d.ts +30 -0
  64. package/dist/utils/collation.js +40 -0
  65. package/dist/utils/columnStoreTypes.d.ts +161 -0
  66. package/dist/utils/columnStoreTypes.js +29 -0
  67. package/dist/utils/nativeBinaryEntityIdMapper.d.ts +211 -0
  68. package/dist/utils/nativeBinaryEntityIdMapper.js +381 -0
  69. package/dist/utils/nativeEntityIdMapper.d.ts +111 -0
  70. package/dist/utils/nativeEntityIdMapper.js +170 -0
  71. package/docs/ADR-002-diskann-100-percent-rust.md +294 -0
  72. package/docs/ADR-003-semantic-time-travel.md +100 -0
  73. package/docs/aggregation.md +251 -0
  74. package/docs/comparison.md +162 -0
  75. package/docs/deployment-limits.md +87 -0
  76. package/docs/diskann.md +184 -0
  77. package/docs/migration-3.0.md +396 -0
  78. package/docs/performance-budget.md +274 -0
  79. package/docs/performance.md +117 -0
  80. package/docs/scaling.md +439 -0
  81. package/docs/snapshot-safety.md +246 -0
  82. package/docs/u64-id-space.md +214 -0
  83. package/docs/verification-report.md +670 -0
  84. package/native/brainy-native.node +0 -0
  85. package/native/index.d.ts +3916 -0
  86. package/package.json +92 -0
  87. package/scripts/migrate-cortex-2x-to-3x.mjs +970 -0
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: U64 Entity IDs
3
+ slug: cor/u64-id-space
4
+ public: true
5
+ category: cor
6
+ template: guide
7
+ order: 6
8
+ description: How to opt into Cor's 64-bit entity IdSpace for brains above 4.29 B entities. Same APIs, two wire widths — pick once at create time, then everything in cor routes through the right type.
9
+ next:
10
+ - cor/scaling
11
+ - cor/diskann
12
+ ---
13
+
14
+ # U64 Entity IDs
15
+
16
+ Cor maps every entity UUID to a compact integer id internally. Two
17
+ parts of the stack key off that integer — the
18
+ [roaring bitmaps](./performance.md) inside the metadata index, and the
19
+ mmap-backed `BinaryEntityIdMapper` that persists the UUID ↔ int
20
+ mapping at billion scale. Both of those layers historically used
21
+ 32-bit ints (`Roaring32`, `u32`), which caps a single brain at
22
+ **4 294 967 295 entities** — about 4.29 B.
23
+
24
+ That ceiling is fine for almost every workload. It is *not* fine for
25
+ the corpora cor 3.0 targets: the design point of the release is "1
26
+ B vectors on a single $1000 box", and the long tail goes well past 4
27
+ B. So cor 3.0 adds a second IdSpace — `'u64'` — that lifts the
28
+ ceiling to 2⁶⁴ - 1 (≈ 1.84 × 10¹⁹) while keeping every public API the
29
+ same.
30
+
31
+ ## Picking an IdSpace
32
+
33
+ **Cor 3.0's default is `'u64'`** — there's nothing to configure for new brains. You only need to think about this if you specifically want a brainy-7.x-compatible 4.29 B-capped brain.
34
+
35
+ ```typescript
36
+ import { BrainyData } from '@soulcraft/brainy'
37
+ import { register as registerCor } from '@soulcraft/cor'
38
+
39
+ // Cor 3.0 production default — no config needed for new brains.
40
+ const brain = new BrainyData({
41
+ storage: { type: 'filesystem', rootDirectory: '/data/idx' },
42
+ })
43
+ await registerCor(brain)
44
+ await brain.init()
45
+ // brain is u64 internally — supports up to 2^64 - 1 entities.
46
+
47
+ // Explicit opt-in to u32 (brainy 7.x compatible mode):
48
+ const brainCompat = new BrainyData({
49
+ storage: { type: 'filesystem', rootDirectory: '/data/legacy-brain' },
50
+ entityIdMapper: { idSpace: 'u32' },
51
+ })
52
+ ```
53
+
54
+ Use **U64** (default) when:
55
+
56
+ - You're starting a new brain on cor 3.0 — it's the default for a reason.
57
+ - You expect any chance of exceeding 4.29 B entities over the brain's lifetime.
58
+ - Brain holds high-fanout graphs where the verb-int count can grow faster than the entity-int count.
59
+
60
+ Use **U32** (explicit opt-in) when:
61
+
62
+ - You're restoring a brainy 7.x snapshot whose on-disk format was u32 and you want bit-identical compatibility with a brainy 7.x reader.
63
+ - You're running a cortex 2.x-compatible deployment that interops with old tooling.
64
+ - You need byte-identical wire format with cortex 2.x — existing `.cidx` segments and chunked metadata JSON envelopes are unchanged.
65
+
66
+ Use **U64** when:
67
+
68
+ - The brain may exceed 4.29 B entities.
69
+ - You're starting fresh and want headroom — the U64 wire format has
70
+ ~negligible overhead at small scale and is forward-compatible with
71
+ every cor 3.x release.
72
+ - You want to use the BigInt napi sibling methods directly (e.g. when
73
+ a downstream system already speaks `bigint` and you want to skip
74
+ the safe-integer guard).
75
+
76
+ The two modes are **not interchangeable on the same files**. You
77
+ cannot escalate a U32 brain to U64 in place — the on-disk header is
78
+ authoritative and any mismatched config is rejected as a hard error
79
+ at open time. Migrating an existing U32 brain to U64 is a re-export
80
+ + re-ingest into a new brain.
81
+
82
+ ## No sizing config — the id mapper auto-grows
83
+
84
+ You never size the id-mapper files. Both back the brain's UUID ↔ int
85
+ mapping with mmap-backed sparse files that **grow themselves at
86
+ compaction** as the brain fills:
87
+
88
+ - **`int_to_uuid.bin`** is direct-indexed (`slot = header + int × 24`).
89
+ When the highest assigned int approaches the file's slot capacity,
90
+ the next compaction rebuilds the base at a geometrically larger size
91
+ (×2, with headroom). No fixed entity ceiling.
92
+ - **`uuid_to_int.mkv`** is an extendible hash. Its directory
93
+ auto-doubles up to the architectural maximum depth (32), and
94
+ compaction enlarges the bucket region on demand — with a
95
+ retry-on-overflow loop so even highly-skewed key sets never hit a
96
+ premature cap.
97
+
98
+ Growth rides the crash-safe shadow-page compaction swap (a new base is
99
+ built, the head pointer is swapped atomically, the old base is
100
+ unlinked), so there is no remap-while-readers-active hazard and a
101
+ crash mid-grow recovers cleanly. The files are **sparse**: the
102
+ declared size costs no real disk until the slots are actually written,
103
+ and the modest defaults keep tiny / multi-tenant brains small.
104
+
105
+ **Practical ceiling:** a single brain scales to ~51 B entities
106
+ (`max_global_depth=32` at the 16-entry bucket capacity) with **zero
107
+ configuration**. Past that — i.e. beyond the documented single-box
108
+ content ceiling — cor raises a clear "shard the brain" error
109
+ rather than silently degrading; horizontal scaling across brains is
110
+ the architecture at that point (see [why-cor](./why-cor.md)).
111
+
112
+ ## What changes on the wire
113
+
114
+ - **`int_to_uuid.bin`** (binary mapper): v1 header for U32, v2 header
115
+ for U64. The header carries the IdSpace; the file is self-
116
+ describing.
117
+ - **`.cidx` column-store segments**: flags byte's `FLAG_U64_IDS` bit
118
+ (0x01) is set; the entity-id column is `u64` little-endian. The
119
+ cross-language fixture in
120
+ `src/native/cidxU64WireFormatFixture.test.ts` locks the byte layout
121
+ via SHA-256.
122
+ - **Metadata chunk JSON envelope**: U64 chunks get a v2 envelope with
123
+ `"version": 2` and `"idSpace": "u64"` fields. U32 chunks emit the
124
+ v1-compatible envelope (no extra fields) for backwards compatibility.
125
+ - **`PostingList`** (in-memory representation): the U32 brain uses
126
+ `croaring::Bitmap` (Roaring32); the U64 brain uses `croaring::Treemap`
127
+ (Roaring64). Both expose the same algebra (`union_assign`,
128
+ `intersect_assign`, `difference_assign`, `iter_u64`,
129
+ `cardinality`), so every query path is variant-agnostic.
130
+
131
+ ## What changes in the API
132
+
133
+ Nothing, for the common case. Every wrapper method that returned a
134
+ `number` continues to return a `number`. Two refinements:
135
+
136
+ 1. **`getAllIntIds()` throws in U64 mode.** A materialised `number[]`
137
+ at billion scale would risk OOM, and U64 brains exist precisely
138
+ because the entity count is large. Use the streaming BigInt
139
+ iterator on the underlying native binding.
140
+ 2. **`EntityIdSpaceExceeded`** is thrown when a U64 brain's number-
141
+ typed method (e.g. `getOrAssign(uuid): number`) would return an
142
+ int above `Number.MAX_SAFE_INTEGER` (2⁵³ - 1 ≈ 9.007 PB of
143
+ entities). At that point you must switch to the BigInt sibling
144
+ methods for the full u64 range:
145
+
146
+ ```typescript
147
+ const big: bigint = mapper.getOrAssignBig(uuid)
148
+ const uuidBack: string | undefined = mapper.getUuidBig(big)
149
+ ```
150
+
151
+ The BigInt siblings work in **both** modes. Use them in any code
152
+ path that crosses the U32 → U64 escalation point so it doesn't
153
+ need to branch on `getIdSpace()`.
154
+
155
+ ## BigInt sibling surface (cor 3.0)
156
+
157
+ | Number-typed | BigInt sibling | Returns |
158
+ |:---|:---|:---|
159
+ | `getOrAssign(uuid: string): number` | `getOrAssignBig(uuid: string): bigint` | The allocated int |
160
+ | `getInt(uuid: string): number \| undefined` | `getIntBig(uuid: string): bigint \| undefined` | The int for `uuid`, or undefined |
161
+ | `getUuid(int: number): string \| undefined` | `getUuidBig(int: bigint): string \| undefined` | The UUID for `int` |
162
+ | `get size(): number` | `sizeBig(): bigint` | Live (non-tombstone) entry count |
163
+ | — | `nextIntBig(): bigint` | Largest int ever assigned + 1 |
164
+
165
+ The BigInt methods are mode-independent: they work in U32 brains too,
166
+ and return the same values widened to `bigint`. Mixing
167
+ `getOrAssign(uuid)` and `getOrAssignBig(uuid)` for the same UUID
168
+ returns the same underlying int.
169
+
170
+ ## Brainy 8.0 lockstep — `EntityIdSpaceExceeded` (JS fallback)
171
+
172
+ Brainy 8.0 ships the JS-fallback half of the contract. Without
173
+ cor installed, brainy uses an in-RAM `EntityIdMapper` that also
174
+ caps at u32::MAX — but the JS fallback can't widen to u64 (the
175
+ bitmap layer is `Roaring32` end-to-end on the JS side). So brainy
176
+ 8.0's mapper throws `EntityIdSpaceExceeded` (in
177
+ `@soulcraft/brainy/internals`) at the ceiling and points the caller
178
+ at cor's `idSpace: 'u64'` mode as the migration path.
179
+
180
+ The two errors are siblings at different layers:
181
+
182
+ - **brainy's** `EntityIdSpaceExceeded` (`U32_ENTITY_ID_MAX` = 2³² − 1):
183
+ the JS-only path hit the bitmap-width ceiling — install cor.
184
+ - **cor's** `EntityIdSpaceExceeded` (`Number.MAX_SAFE_INTEGER`
185
+ = 2⁵³ − 1): a U64 brain's number-typed method overflowed JS's
186
+ safe-integer range — switch to BigInt siblings.
187
+
188
+ ## Wire-format parity gates
189
+
190
+ Two hash-locked fixtures lock the format across reader
191
+ implementations:
192
+
193
+ - **`.cidx` U64 segments** —
194
+ `src/native/cidxU64WireFormatFixture.test.ts`. Three SHA-256
195
+ hashes (numeric / float / string segments) lock the byte layout.
196
+ Brainy 8.0's reader targets the same fixtures.
197
+ - **`PostingList` cross-language** — round-tripped via the napi
198
+ `parityWriteU64NumericSegment` / `parityReadSegmentEntityIdsBig`
199
+ pair. Same write path produces same hash across 100 stress
200
+ iterations (see `src/native/cor30Stress.test.ts`).
201
+
202
+ If a hash drifts, do **not** silently update the constant — root-
203
+ cause the byte diff first. A non-deterministic writer or a
204
+ half-applied format change is exactly the kind of bug those fixtures
205
+ exist to catch.
206
+
207
+ ## See also
208
+
209
+ - [Performance](./performance.md) — measured roaring-bitmap ops + SIMD
210
+ distance speedups.
211
+ - [Scaling](./scaling.md) — multi-tenant density + the 1 B operational
212
+ ceiling.
213
+ - [DiskANN](./diskann.md) — the search engine that consumes these
214
+ entity ids at billion scale.