@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,28 @@
1
+ /**
2
+ * @soulcraft/cor — Native Rust acceleration for Brainy
3
+ *
4
+ * When installed alongside @soulcraft/brainy, this package automatically
5
+ * provides native Rust implementations for:
6
+ * - SIMD distance calculations (cosine, euclidean, manhattan, dot product)
7
+ * - Metadata index (full query/mutation engine in Rust)
8
+ * - Graph adjacency index (4 LSM-trees + verb tracking)
9
+ * - Entity ID mapper (UUID ↔ integer bidirectional mapping)
10
+ * - Roaring bitmaps (CRoaring bindings)
11
+ * - Embedding engine (Candle ML — CPU, CUDA, Metal)
12
+ * - Msgpack encoding/decoding
13
+ *
14
+ * Storage adapter: cor uses brainy's `FileSystemStorage` directly
15
+ * (since brainy 7.25.0 it natively provides the binary-blob primitive
16
+ * and multi-process locking cor used to layer on top). Cor no
17
+ * longer ships a storage adapter — the boundary is now razor-sharp:
18
+ * **brainy = data + storage**, **cor = compute accelerators**.
19
+ *
20
+ * Usage: `npm install @soulcraft/cor` — auto-detected, zero config.
21
+ */
22
+ export { default } from './plugin.js';
23
+ export { loadNativeModule, isNativeAvailable } from './native/index.js';
24
+ export type { NativeBindings } from './native/types.js';
25
+ export { NativeEmbeddingEngine, nativeEmbeddingEngine, cosineSimilarity } from './native/NativeEmbeddingEngine.js';
26
+ export { RoaringBitmap32, RoaringBitmap32Iterator, roaringLibraryInitialize, roaringLibraryIsReady, SerializationFormat, DeserializationFormat } from './native/NativeRoaringBitmap32.js';
27
+ export { NativeAggregationProvider } from './aggregation/NativeAggregationEngine.js';
28
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @soulcraft/cor — Native Rust acceleration for Brainy
3
+ *
4
+ * When installed alongside @soulcraft/brainy, this package automatically
5
+ * provides native Rust implementations for:
6
+ * - SIMD distance calculations (cosine, euclidean, manhattan, dot product)
7
+ * - Metadata index (full query/mutation engine in Rust)
8
+ * - Graph adjacency index (4 LSM-trees + verb tracking)
9
+ * - Entity ID mapper (UUID ↔ integer bidirectional mapping)
10
+ * - Roaring bitmaps (CRoaring bindings)
11
+ * - Embedding engine (Candle ML — CPU, CUDA, Metal)
12
+ * - Msgpack encoding/decoding
13
+ *
14
+ * Storage adapter: cor uses brainy's `FileSystemStorage` directly
15
+ * (since brainy 7.25.0 it natively provides the binary-blob primitive
16
+ * and multi-process locking cor used to layer on top). Cor no
17
+ * longer ships a storage adapter — the boundary is now razor-sharp:
18
+ * **brainy = data + storage**, **cor = compute accelerators**.
19
+ *
20
+ * Usage: `npm install @soulcraft/cor` — auto-detected, zero config.
21
+ */
22
+ // Default export: BrainyPlugin (auto-detected by brainy init())
23
+ export { default } from './plugin.js';
24
+ export { loadNativeModule, isNativeAvailable } from './native/index.js';
25
+ // Re-export all native wrapper classes
26
+ export { NativeEmbeddingEngine, nativeEmbeddingEngine, cosineSimilarity } from './native/NativeEmbeddingEngine.js';
27
+ export { RoaringBitmap32, RoaringBitmap32Iterator, roaringLibraryInitialize, roaringLibraryIsReady, SerializationFormat, DeserializationFormat } from './native/NativeRoaringBitmap32.js';
28
+ export { NativeAggregationProvider } from './aggregation/NativeAggregationEngine.js';
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @module legacyLayoutGuard
3
+ * @description Refuses to start when a cor 3.0 process is pointed
4
+ * at a directory that still holds cortex 2.x on-disk artifacts.
5
+ *
6
+ * The failure mode this prevents is the most dangerous in production:
7
+ * an operator deploys brainy 8.0 + cor 3.0 to a host whose data
8
+ * directory hasn't been migrated yet, the service starts,
9
+ * `brain.init()` fires, and either (a) crashes mid-init with a
10
+ * confusing low-level error from a shadow-page store, or (b) — worse —
11
+ * cortex's auto-engagement creates a new 3.0 file alongside the 2.x
12
+ * remnants, producing a half-migrated state that's harder to roll
13
+ * back than a clean stop.
14
+ *
15
+ * The guard runs at the top of `corPlugin.activate()`, before any
16
+ * provider registers. It scans the storage root for known cortex 2.x
17
+ * markers, and if any are found, throws a single error with the exact
18
+ * migration command the operator needs to run. **Throwing here is the
19
+ * point** — a service that won't start is recoverable; a service that
20
+ * starts on the wrong format isn't.
21
+ *
22
+ * The guard is no-op when:
23
+ * - The storage adapter isn't filesystem-backed (cortex 2.x markers
24
+ * are inherently filesystem-rooted; cloud adapters can't have
25
+ * them).
26
+ * - The storage root is empty (fresh install — no migration needed).
27
+ * - Only cor 3.0 markers are present (the migration completed,
28
+ * or this brain was created fresh on 3.0).
29
+ *
30
+ * The guard fires when:
31
+ * - `_id_mapper/uuid_to_int.mkv` is a FILE (cortex 2.x). In cortex
32
+ * 3.0 this is a DIRECTORY containing the shadow-page head pointer
33
+ * + base + delta.
34
+ * - `_id_mapper/int_to_uuid.bin` is a FILE (cortex 2.x). Same
35
+ * story — 3.0 makes this a directory.
36
+ * - `_metadata/` contains the cortex 2.x JSON chunk envelope shape
37
+ * without the 3.0 LSM shard subdirectory.
38
+ * - HNSW provider artifacts exist alongside no DiskANN directory.
39
+ *
40
+ * Bypass: setting `COR_LEGACY_LAYOUT_GUARD=skip` (legacy `CORTEX_LEGACY_LAYOUT_GUARD` still honored) in the environment
41
+ * disables the guard. This exists for the migration script itself
42
+ * (which needs to be able to read the legacy layout to take its
43
+ * snapshot) and for emergency debugging. Production code should never
44
+ * set this.
45
+ */
46
+ /**
47
+ * Storage shape we can inspect. Cortex plugin's
48
+ * `BrainyPluginContext.storage` exposes `getBinaryBlobPath` on
49
+ * filesystem-backed adapters; we use that to discover the root.
50
+ */
51
+ interface StorageWithBlobPath {
52
+ getBinaryBlobPath?: (key: string) => string | null;
53
+ }
54
+ /**
55
+ * Public guard. Call at the top of `corPlugin.activate()` with
56
+ * the brainy storage adapter. Throws if cortex 2.x markers are
57
+ * present.
58
+ */
59
+ export declare function assertNoLegacyLayout(storage: StorageWithBlobPath | null | undefined): void;
60
+ /**
61
+ * @description Run {@link assertNoLegacyLayout} exactly once per storage
62
+ * adapter, from inside cor's storage-receiving provider factories.
63
+ *
64
+ * This is the load-bearing entry point under brainy 8.0. The plugin
65
+ * `activate(context)` hook receives a context that does **not** expose
66
+ * the brain's storage — it carries only `registerProvider` + `version` —
67
+ * so a guard call there scans `undefined` and silently passes, defeating
68
+ * the safety net. The storage adapter is only handed to the *provider
69
+ * factories* (`metadataIndex`, `graphIndex`, `entityIdMapper`), which
70
+ * brainy calls during `brain.init()`, before any of those native engines
71
+ * open files under the brain root. Calling this at the top of each such
72
+ * factory makes the cortex-2.x guard fire at the right moment, against
73
+ * the real storage, on whichever factory brainy constructs first.
74
+ *
75
+ * A loud "service won't start" failure is recoverable; letting cor 3.0
76
+ * auto-engagement run alongside 2.x remnants produces a half-migrated
77
+ * state that's harder to undo. Bypass via env `COR_LEGACY_LAYOUT_GUARD=skip`
78
+ * (legacy `CORTEX_LEGACY_LAYOUT_GUARD` still honored) for the migration
79
+ * script itself.
80
+ *
81
+ * @param storage - The brainy storage adapter passed to a provider
82
+ * factory. Non-object / nullish values no-op (defensive).
83
+ * @throws If cortex 2.x on-disk markers are found under the storage root
84
+ * (see {@link assertNoLegacyLayout}).
85
+ * @example
86
+ * context.registerProvider('graphIndex', (storage) => {
87
+ * guardLegacyLayoutOnce(storage)
88
+ * return new NativeGraphAdjacencyIndex(storage)
89
+ * })
90
+ */
91
+ export declare function guardLegacyLayoutOnce(storage: StorageWithBlobPath | null | undefined): void;
92
+ export {};
93
+ //# sourceMappingURL=legacyLayoutGuard.d.ts.map
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @module legacyLayoutGuard
3
+ * @description Refuses to start when a cor 3.0 process is pointed
4
+ * at a directory that still holds cortex 2.x on-disk artifacts.
5
+ *
6
+ * The failure mode this prevents is the most dangerous in production:
7
+ * an operator deploys brainy 8.0 + cor 3.0 to a host whose data
8
+ * directory hasn't been migrated yet, the service starts,
9
+ * `brain.init()` fires, and either (a) crashes mid-init with a
10
+ * confusing low-level error from a shadow-page store, or (b) — worse —
11
+ * cortex's auto-engagement creates a new 3.0 file alongside the 2.x
12
+ * remnants, producing a half-migrated state that's harder to roll
13
+ * back than a clean stop.
14
+ *
15
+ * The guard runs at the top of `corPlugin.activate()`, before any
16
+ * provider registers. It scans the storage root for known cortex 2.x
17
+ * markers, and if any are found, throws a single error with the exact
18
+ * migration command the operator needs to run. **Throwing here is the
19
+ * point** — a service that won't start is recoverable; a service that
20
+ * starts on the wrong format isn't.
21
+ *
22
+ * The guard is no-op when:
23
+ * - The storage adapter isn't filesystem-backed (cortex 2.x markers
24
+ * are inherently filesystem-rooted; cloud adapters can't have
25
+ * them).
26
+ * - The storage root is empty (fresh install — no migration needed).
27
+ * - Only cor 3.0 markers are present (the migration completed,
28
+ * or this brain was created fresh on 3.0).
29
+ *
30
+ * The guard fires when:
31
+ * - `_id_mapper/uuid_to_int.mkv` is a FILE (cortex 2.x). In cortex
32
+ * 3.0 this is a DIRECTORY containing the shadow-page head pointer
33
+ * + base + delta.
34
+ * - `_id_mapper/int_to_uuid.bin` is a FILE (cortex 2.x). Same
35
+ * story — 3.0 makes this a directory.
36
+ * - `_metadata/` contains the cortex 2.x JSON chunk envelope shape
37
+ * without the 3.0 LSM shard subdirectory.
38
+ * - HNSW provider artifacts exist alongside no DiskANN directory.
39
+ *
40
+ * Bypass: setting `COR_LEGACY_LAYOUT_GUARD=skip` (legacy `CORTEX_LEGACY_LAYOUT_GUARD` still honored) in the environment
41
+ * disables the guard. This exists for the migration script itself
42
+ * (which needs to be able to read the legacy layout to take its
43
+ * snapshot) and for emergency debugging. Production code should never
44
+ * set this.
45
+ */
46
+ import { existsSync, statSync, readdirSync } from 'node:fs';
47
+ import { join } from 'node:path';
48
+ const CORTEX_SUBDIRS = ['_id_mapper', '_metadata', '_diskann', '_graph_adjacency'];
49
+ /**
50
+ * Heuristic: cortex 2.x flat-file layout markers under `_id_mapper`.
51
+ * Cor 3.0 makes both of these directories instead.
52
+ */
53
+ const LEGACY_ID_MAPPER_FILES = [
54
+ '_id_mapper/uuid_to_int.mkv',
55
+ '_id_mapper/int_to_uuid.bin',
56
+ ];
57
+ /**
58
+ * Discover the storage root directory from the brainy
59
+ * `StorageAdapter`. Returns null for non-filesystem adapters (no
60
+ * legacy markers possible).
61
+ */
62
+ function resolveStorageRoot(storage) {
63
+ if (!storage || typeof storage.getBinaryBlobPath !== 'function')
64
+ return null;
65
+ // Ask for a sentinel path; the returned path's parent is the
66
+ // storage root for our purposes.
67
+ const probe = storage.getBinaryBlobPath('__cortex_legacy_layout_guard_probe__');
68
+ if (!probe)
69
+ return null;
70
+ // The probe path doesn't exist; we want its containing directory.
71
+ // brainy's FileSystemStorage returns something like
72
+ // `${rootDirectory}/__cortex_legacy_layout_guard_probe__` or
73
+ // `${rootDirectory}/binaryBlobs/__cortex_legacy_layout_guard_probe__`.
74
+ // Walk up to the directory that contains `_id_mapper` or is the
75
+ // brain root.
76
+ let dir = probe;
77
+ // Strip the probe filename component.
78
+ dir = dir.replace(/\/[^/]+$/, '');
79
+ // If this is `binaryBlobs/`, go one more up.
80
+ if (dir.endsWith('/binaryBlobs')) {
81
+ dir = dir.slice(0, -'/binaryBlobs'.length);
82
+ }
83
+ if (!existsSync(dir))
84
+ return null;
85
+ return dir;
86
+ }
87
+ function findLegacyMarkers(root) {
88
+ const markers = [];
89
+ // (1) cortex 2.x flat-file id mapper layout.
90
+ for (const rel of LEGACY_ID_MAPPER_FILES) {
91
+ const p = join(root, rel);
92
+ if (!existsSync(p))
93
+ continue;
94
+ const stat = statSync(p);
95
+ if (stat.isFile()) {
96
+ markers.push({
97
+ path: p,
98
+ reason: `${rel} is a file (cortex 2.x format). ` +
99
+ `Cor 3.0 expects this to be a directory (shadow-page LSM store).`,
100
+ });
101
+ }
102
+ }
103
+ // (2) cortex 2.x metadata index JSON chunk files at the metadata
104
+ // root, with no 3.0 LSM shard subdirectory.
105
+ const metadataDir = join(root, '_metadata');
106
+ if (existsSync(metadataDir) && statSync(metadataDir).isDirectory()) {
107
+ const entries = readdirSync(metadataDir);
108
+ const hasLsmShard = entries.some((e) => e === 'memtable' || e.startsWith('lsm-'));
109
+ const hasJsonChunks = entries.some((e) => e.endsWith('.json') || e.endsWith('.cidx'));
110
+ if (hasJsonChunks && !hasLsmShard) {
111
+ markers.push({
112
+ path: metadataDir,
113
+ reason: `${metadataDir} contains cortex 2.x JSON chunk files (.json/.cidx) but no 3.0 LSM shard layout. ` +
114
+ `Cor 3.0's metadata index is LSM-tree-backed.`,
115
+ });
116
+ }
117
+ }
118
+ // (3) HNSW provider artifacts in `_diskann/` or root without DiskANN
119
+ // directory. The cortex 2.x HNSW provider wrote a `.hnsw` file at
120
+ // the root or under a `_hnsw/` subdirectory. Cor 3.0 writes
121
+ // `_diskann/` instead.
122
+ const candidateHnsw = readdirSync(root, { withFileTypes: true })
123
+ .filter((e) => e.isFile())
124
+ .filter((e) => e.name.endsWith('.hnsw'));
125
+ if (candidateHnsw.length > 0 && !existsSync(join(root, '_diskann'))) {
126
+ markers.push({
127
+ path: join(root, candidateHnsw[0].name),
128
+ reason: `${root} contains cortex 2.x HNSW provider files (.hnsw) with no 3.0 DiskANN directory. ` +
129
+ `Cor 3.0 uses Adaptive DiskANN as the default vector index.`,
130
+ });
131
+ }
132
+ return markers;
133
+ }
134
+ /**
135
+ * Public guard. Call at the top of `corPlugin.activate()` with
136
+ * the brainy storage adapter. Throws if cortex 2.x markers are
137
+ * present.
138
+ */
139
+ export function assertNoLegacyLayout(storage) {
140
+ // Dual-read during the cortex→cor rename: COR_ preferred, legacy CORTEX_ honored.
141
+ if ((process.env.COR_LEGACY_LAYOUT_GUARD ?? process.env.CORTEX_LEGACY_LAYOUT_GUARD) === 'skip') {
142
+ return;
143
+ }
144
+ const root = resolveStorageRoot(storage);
145
+ if (!root)
146
+ return; // non-filesystem adapter or unresolvable; nothing to check
147
+ if (!existsSync(root))
148
+ return; // fresh install
149
+ const markers = findLegacyMarkers(root);
150
+ if (markers.length === 0)
151
+ return;
152
+ // Build the error message. The point is to be SO clear that an
153
+ // operator can act on it without reading docs.
154
+ const lines = [
155
+ '',
156
+ '╔══════════════════════════════════════════════════════════════════════════╗',
157
+ '║ Cor 3.0 cannot start: this storage directory holds cortex 2.x data. ║',
158
+ '╚══════════════════════════════════════════════════════════════════════════╝',
159
+ '',
160
+ `Storage root: ${root}`,
161
+ '',
162
+ 'Cortex 2.x markers found:',
163
+ ...markers.map((m) => ` • ${m.reason}`),
164
+ '',
165
+ 'Cor 3.0 broke the on-disk format intentionally to ship snapshot-safe',
166
+ 'shadow-page LSM stores + Adaptive DiskANN. Reading 2.x files in 3.0 is not',
167
+ 'supported by design.',
168
+ '',
169
+ '🚨 Do NOT delete this directory. Your data is intact in the 2.x format.',
170
+ '',
171
+ 'To migrate (zero data loss, ~10 minutes for Venue-scale brains):',
172
+ '',
173
+ ' node node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs \\',
174
+ ` --brain-dir ${root} \\`,
175
+ ` --backup-dir ${root}/../backups/pre-3.0`,
176
+ '',
177
+ 'Full migration guide: docs/migration-3.0.md',
178
+ 'Venue-specific worked example in docs/migration-3.0.md § Worked example — Venue',
179
+ '',
180
+ 'To rollback instead: downgrade @soulcraft/brainy + @soulcraft/cortex to',
181
+ 'your previous versions (e.g. brainy@7.31.6 cortex@2.7.0) — no data',
182
+ 'changes have happened yet because cor 3.0 refused to start.',
183
+ '',
184
+ ];
185
+ throw new Error(lines.join('\n'));
186
+ }
187
+ /**
188
+ * Per-storage memo so the (filesystem-scanning) guard runs at most once
189
+ * per brain. brainy invokes each storage-receiving provider factory
190
+ * independently during `brain.init()`; without this, the same root would
191
+ * be re-scanned for every factory. A `WeakSet` keyed on the storage
192
+ * object also means distinct brains (distinct storage adapters) are each
193
+ * guarded independently, and the memo is collected with the brain.
194
+ */
195
+ const guardedStorages = new WeakSet();
196
+ /**
197
+ * @description Run {@link assertNoLegacyLayout} exactly once per storage
198
+ * adapter, from inside cor's storage-receiving provider factories.
199
+ *
200
+ * This is the load-bearing entry point under brainy 8.0. The plugin
201
+ * `activate(context)` hook receives a context that does **not** expose
202
+ * the brain's storage — it carries only `registerProvider` + `version` —
203
+ * so a guard call there scans `undefined` and silently passes, defeating
204
+ * the safety net. The storage adapter is only handed to the *provider
205
+ * factories* (`metadataIndex`, `graphIndex`, `entityIdMapper`), which
206
+ * brainy calls during `brain.init()`, before any of those native engines
207
+ * open files under the brain root. Calling this at the top of each such
208
+ * factory makes the cortex-2.x guard fire at the right moment, against
209
+ * the real storage, on whichever factory brainy constructs first.
210
+ *
211
+ * A loud "service won't start" failure is recoverable; letting cor 3.0
212
+ * auto-engagement run alongside 2.x remnants produces a half-migrated
213
+ * state that's harder to undo. Bypass via env `COR_LEGACY_LAYOUT_GUARD=skip`
214
+ * (legacy `CORTEX_LEGACY_LAYOUT_GUARD` still honored) for the migration
215
+ * script itself.
216
+ *
217
+ * @param storage - The brainy storage adapter passed to a provider
218
+ * factory. Non-object / nullish values no-op (defensive).
219
+ * @throws If cortex 2.x on-disk markers are found under the storage root
220
+ * (see {@link assertNoLegacyLayout}).
221
+ * @example
222
+ * context.registerProvider('graphIndex', (storage) => {
223
+ * guardLegacyLayoutOnce(storage)
224
+ * return new NativeGraphAdjacencyIndex(storage)
225
+ * })
226
+ */
227
+ export function guardLegacyLayoutOnce(storage) {
228
+ if (!storage || typeof storage !== 'object')
229
+ return;
230
+ if (guardedStorages.has(storage))
231
+ return;
232
+ assertNoLegacyLayout(storage);
233
+ // Only memoize a CLEAN result: a storage that threw is never marked,
234
+ // so a legacy layout keeps failing loudly on every entry point.
235
+ guardedStorages.add(storage);
236
+ }
237
+ //# sourceMappingURL=legacyLayoutGuard.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * License constants (v2)
3
+ *
4
+ * Crypto keys are in Rust. This file keeps paths and URLs only.
5
+ */
6
+ /** Default heartbeat check interval (24h) — server may override via nextCheckSeconds */
7
+ export declare const DEFAULT_CHECK_INTERVAL_MS: number;
8
+ /** Grace period in days after subscription lapse detected */
9
+ export declare const GRACE_PERIOD_DAYS = 14;
10
+ /** Online validation timeout in milliseconds */
11
+ export declare const ONLINE_VALIDATION_TIMEOUT_MS = 10000;
12
+ /** Portal heartbeat validation API URL */
13
+ export declare const API_URL = "https://soulcraft.com/api/license/validate";
14
+ /** Short code exchange API URL */
15
+ export declare const EXCHANGE_URL = "https://soulcraft.com/api/license/exchange";
16
+ /** Pricing URL for license purchase */
17
+ export declare const PRICING_URL = "https://soulcraft.com/pricing?focus=pro";
18
+ /**
19
+ * Pattern matching activation short codes (CO-XXXX-XXXX). The legacy `CX-`
20
+ * prefix is also accepted during the cortex→cor rename so codes already issued
21
+ * by Portal keep working; Portal issues `CO-` going forward.
22
+ */
23
+ export declare const SHORT_CODE_PATTERN: RegExp;
24
+ /** Project-local config file name */
25
+ export declare const CONFIG_FILE = ".soulcraft.json";
26
+ /** Project-local cache directory (gitignored) */
27
+ export declare const CACHE_DIR = ".soulcraft";
28
+ /** Heartbeat cache file */
29
+ export declare const HEARTBEAT_FILE = "heartbeat.json";
30
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * License constants (v2)
3
+ *
4
+ * Crypto keys are in Rust. This file keeps paths and URLs only.
5
+ */
6
+ /** Default heartbeat check interval (24h) — server may override via nextCheckSeconds */
7
+ export const DEFAULT_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
8
+ /** Grace period in days after subscription lapse detected */
9
+ export const GRACE_PERIOD_DAYS = 14;
10
+ /** Online validation timeout in milliseconds */
11
+ export const ONLINE_VALIDATION_TIMEOUT_MS = 10_000;
12
+ /** Portal heartbeat validation API URL */
13
+ export const API_URL = 'https://soulcraft.com/api/license/validate';
14
+ /** Short code exchange API URL */
15
+ export const EXCHANGE_URL = 'https://soulcraft.com/api/license/exchange';
16
+ /** Pricing URL for license purchase */
17
+ export const PRICING_URL = 'https://soulcraft.com/pricing?focus=pro';
18
+ /**
19
+ * Pattern matching activation short codes (CO-XXXX-XXXX). The legacy `CX-`
20
+ * prefix is also accepted during the cortex→cor rename so codes already issued
21
+ * by Portal keep working; Portal issues `CO-` going forward.
22
+ */
23
+ export const SHORT_CODE_PATTERN = /^(?:CO|CX)-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
24
+ /** Project-local config file name */
25
+ export const CONFIG_FILE = '.soulcraft.json';
26
+ /** Project-local cache directory (gitignored) */
27
+ export const CACHE_DIR = '.soulcraft';
28
+ /** Heartbeat cache file */
29
+ export const HEARTBEAT_FILE = 'heartbeat.json';
30
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Online license heartbeat (v2)
3
+ *
4
+ * Background check against Portal API for:
5
+ * - Status-based subscription state (active, past_due, canceled, etc.)
6
+ * - Key renewal (subscription renewed, fresh JWT cached in memory)
7
+ * - Server-driven check intervals via nextCheckSeconds
8
+ *
9
+ * No signature verification here — that's in Rust.
10
+ * Default 24h heartbeat interval, 14-day grace period.
11
+ */
12
+ interface HeartbeatCache {
13
+ lastCheck: string;
14
+ nextCheckAt?: string;
15
+ status?: string;
16
+ gracePeriodStart?: string;
17
+ }
18
+ /**
19
+ * Read the heartbeat cache from disk. Returns null if not found or unreadable.
20
+ */
21
+ export declare function readHeartbeatCache(): Promise<HeartbeatCache | null>;
22
+ /**
23
+ * Check if a grace period start timestamp is expired (> GRACE_PERIOD_DAYS old).
24
+ */
25
+ export declare function isGracePeriodExpired(gracePeriodStart: string): boolean;
26
+ /**
27
+ * Perform background heartbeat check.
28
+ *
29
+ * Call this after successful Rust validation. Non-blocking (fire and forget).
30
+ * Returns a warning string if the user should be notified.
31
+ * Returns degrade: true if the license is suspended or the grace period has expired.
32
+ *
33
+ * @param token - License key token
34
+ * @param options.forceCheck - Skip interval check, always call API (use at process startup)
35
+ */
36
+ export declare function performHeartbeat(token: string, options?: {
37
+ forceCheck?: boolean;
38
+ }): Promise<{
39
+ warning?: string;
40
+ degrade?: boolean;
41
+ }>;
42
+ export {};
43
+ //# sourceMappingURL=onlineValidator.d.ts.map