@push.rocks/smartdb 5.2.0 → 5.2.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.
package/.smartconfig.json CHANGED
@@ -77,7 +77,22 @@
77
77
  },
78
78
  "locked": true,
79
79
  "static": true,
80
- "remapLocalPaths": true
80
+ "remapLocalPaths": true,
81
+ "matrix": {
82
+ "builders": {
83
+ "linux_amd64": {
84
+ "transport": "local"
85
+ },
86
+ "macos_arm64": {
87
+ "transport": "ssh",
88
+ "destinationEnv": "SMARTDB_RELEASE_MACOS_SSH",
89
+ "temporaryRootEnv": "SMARTDB_RELEASE_MACOS_TEMP_ROOT"
90
+ }
91
+ },
92
+ "smokeTestArgs": [
93
+ "--version"
94
+ ]
95
+ }
81
96
  },
82
97
  "@ship.zone/szci": {
83
98
  "npmGlobalTools": []
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "c5c0a72cc3e9001b008c73ecfbc05dbcde559796eeb81ddd9b57a858f98d4515",
3
+ "binarySha256": "b585eaae112a1eec36542e1e80410744695543cef34191d03de002ac215c8bd8",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartdb",
6
- "projectVersion": "5.2.0",
7
- "gitCommit": "d61180c4086a9f771765d104260f620d76b1cda0",
6
+ "projectVersion": "5.2.2",
7
+ "gitCommit": "08f85430fb5272560782a39019414511a0cfedba",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-08-18T22:39:04.935Z",
10
- "tsrustVersion": "1.9.1",
9
+ "builtAt": "2026-08-21T06:39:21.040Z",
10
+ "tsrustVersion": "1.10.0",
11
11
  "binary": "rustdb",
12
12
  "target": "linux_amd64"
13
13
  }
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "5f3878be4da5dd17eb4c269fdb3f63185d496bd84c1066ce54f47ea3b8f45321",
3
+ "binarySha256": "a40dedd961feb9bcc0cb13f45e280ef0d8d2d819697d612023c9a3990fb51cc4",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartdb",
6
- "projectVersion": "5.2.0",
7
- "gitCommit": "d61180c4086a9f771765d104260f620d76b1cda0",
6
+ "projectVersion": "5.2.2",
7
+ "gitCommit": "08f85430fb5272560782a39019414511a0cfedba",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-08-18T22:39:24.164Z",
10
- "tsrustVersion": "1.9.1",
9
+ "builtAt": "2026-08-21T06:39:59.105Z",
10
+ "tsrustVersion": "1.10.0",
11
11
  "binary": "rustdb",
12
12
  "target": "linux_arm64"
13
13
  }
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "1cf98dfc94ff75ab0fad7927c5a59cec87e9623bcc8e36908e7811bd9632064c",
3
+ "binarySha256": "125f06fb8af9bdc5194ba9772344730aec39a3bd7f1b83c8419a26391649bf2a",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartdb",
6
- "projectVersion": "5.2.0",
7
- "gitCommit": "d61180c4086a9f771765d104260f620d76b1cda0",
6
+ "projectVersion": "5.2.2",
7
+ "gitCommit": "08f85430fb5272560782a39019414511a0cfedba",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-08-18T22:40:01.824Z",
10
- "tsrustVersion": "1.9.1",
9
+ "builtAt": "2026-08-21T06:41:11.089Z",
10
+ "tsrustVersion": "1.10.0",
11
11
  "binary": "rustdb",
12
12
  "target": "macos_amd64"
13
13
  }
Binary file
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "3784a0e1d125a25a714086b098018a34cef6056677da3f087992a7e76cd9d87f",
3
+ "binarySha256": "2c7bc50ca344c883d1039886e509be8d945bc85bc16f6dbbadae7d39084ee9cb",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartdb",
6
- "projectVersion": "5.2.0",
7
- "gitCommit": "d61180c4086a9f771765d104260f620d76b1cda0",
6
+ "projectVersion": "5.2.2",
7
+ "gitCommit": "08f85430fb5272560782a39019414511a0cfedba",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-08-18T22:41:12.434Z",
10
- "tsrustVersion": "1.9.1",
9
+ "builtAt": "2026-08-21T06:41:51.892Z",
10
+ "tsrustVersion": "1.10.0",
11
11
  "binary": "rustdb",
12
12
  "target": "macos_arm64"
13
13
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdb',
6
- version: '5.2.0',
6
+ version: '5.2.2',
7
7
  description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxxQkFBcUI7SUFDM0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHFIQUFxSDtDQUNuSSxDQUFBIn0=
@@ -11,7 +11,6 @@ const FILE_MAGIC = Buffer.from('SMARTDB\0', 'ascii');
11
11
  const FORMAT_VERSION = 1;
12
12
  /** File type tags */
13
13
  const FILE_TYPE_DATA = 1;
14
- const FILE_TYPE_HINT = 3;
15
14
  /** File header total size */
16
15
  const FILE_HEADER_SIZE = 64;
17
16
  /** Per-record magic */
@@ -54,23 +53,6 @@ function encodeDataRecord(timestamp, key, value) {
54
53
  buf.writeUInt32LE(crc, 18);
55
54
  return buf;
56
55
  }
57
- function encodeHintEntry(key, offset, recordLen, valueLen, timestamp) {
58
- const keyBuf = Buffer.from(key, 'utf-8');
59
- const buf = Buffer.alloc(4 + keyBuf.length + 8 + 4 + 4 + 8);
60
- let pos = 0;
61
- buf.writeUInt32LE(keyBuf.length, pos);
62
- pos += 4;
63
- keyBuf.copy(buf, pos);
64
- pos += keyBuf.length;
65
- buf.writeBigUInt64LE(offset, pos);
66
- pos += 8;
67
- buf.writeUInt32LE(recordLen, pos);
68
- pos += 4;
69
- buf.writeUInt32LE(valueLen, pos);
70
- pos += 4;
71
- buf.writeBigUInt64LE(timestamp, pos);
72
- return buf;
73
- }
74
56
  // ---------------------------------------------------------------------------
75
57
  // CRC32 (matching crc32fast in Rust)
76
58
  // ---------------------------------------------------------------------------
@@ -167,7 +149,7 @@ const syncFile = async (filePathArg, signalArg) => {
167
149
  }, signalArg);
168
150
  };
169
151
  const assertPublishedMigrationComplete = async (collectionDirectoryArg) => {
170
- for (const fileName of ['data.rdb', 'keydir.hint', 'indexes.json']) {
152
+ for (const fileName of ['data.rdb', 'indexes.json']) {
171
153
  let fileStat;
172
154
  try {
173
155
  fileStat = await fs.lstat(path.join(collectionDirectoryArg, fileName));
@@ -183,6 +165,9 @@ const assertPublishedMigrationComplete = async (collectionDirectoryArg) => {
183
165
  }
184
166
  }
185
167
  };
168
+ // ---------------------------------------------------------------------------
169
+ // Migration: v0 (JSON) → v1 (Bitcask binary)
170
+ // ---------------------------------------------------------------------------
186
171
  /**
187
172
  * Migrate a storage directory from v0 (JSON-per-collection) to v1 (Bitcask binary).
188
173
  *
@@ -247,8 +232,6 @@ export async function migrateV0ToV1(storagePath, signalArg) {
247
232
  // File header
248
233
  const headerBuf = writeFileHeader(FILE_TYPE_DATA);
249
234
  await writeBuffer(dataFileHandle, headerBuf, signalArg);
250
- let currentOffset = BigInt(FILE_HEADER_SIZE);
251
- const keydir = new Map();
252
235
  const ts = BigInt(Date.now());
253
236
  for (const doc of docs) {
254
237
  signalArg?.throwIfAborted();
@@ -274,27 +257,9 @@ export async function migrateV0ToV1(storagePath, signalArg) {
274
257
  const valueBuf = Buffer.from(bsonBytes);
275
258
  const record = encodeDataRecord(ts, keyBuf, valueBuf);
276
259
  await writeBuffer(dataFileHandle, record, signalArg);
277
- keydir.set(idHex, {
278
- offset: currentOffset,
279
- recordLen: record.length,
280
- valueLen: valueBuf.length,
281
- timestamp: ts,
282
- });
283
- currentOffset += BigInt(record.length);
284
260
  }
285
261
  await dataFileHandle.sync();
286
262
  signalArg?.throwIfAborted();
287
- // Write keydir.hint
288
- const hintPath = path.join(stagingDir, 'keydir.hint');
289
- await withFileHandle(hintPath, 'wx', async (hintFileHandle) => {
290
- await writeBuffer(hintFileHandle, writeFileHeader(FILE_TYPE_HINT), signalArg);
291
- for (const [key, entry] of keydir) {
292
- signalArg?.throwIfAborted();
293
- await writeBuffer(hintFileHandle, encodeHintEntry(key, entry.offset, entry.recordLen, entry.valueLen, entry.timestamp), signalArg);
294
- }
295
- await hintFileHandle.sync();
296
- signalArg?.throwIfAborted();
297
- }, signalArg);
298
263
  }, signalArg);
299
264
  // Copy indexes.json if it exists
300
265
  if (await pathExists(indexJsonPath)) {
@@ -341,4 +306,4 @@ export async function migrateV0ToV1(storagePath, signalArg) {
341
306
  }
342
307
  return deletableFiles;
343
308
  }
344
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidjBfdG9fdjEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy90c19taWdyYXRpb24vbWlncmF0b3JzL3YwX3RvX3YxLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFDdEMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUU1Qiw4RUFBOEU7QUFDOUUsdURBQXVEO0FBQ3ZELDhFQUE4RTtBQUU5RSxvQ0FBb0M7QUFDcEMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDckQsNkJBQTZCO0FBQzdCLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUN6QixxQkFBcUI7QUFDckIsTUFBTSxjQUFjLEdBQUcsQ0FBQyxDQUFDO0FBQ3pCLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUN6Qiw2QkFBNkI7QUFDN0IsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7QUFDNUIsdUJBQXVCO0FBQ3ZCLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQztBQUM1Qiw2QkFBNkI7QUFDN0IsTUFBTSxrQkFBa0IsR0FBRyxFQUFFLENBQUMsQ0FBQyxvQkFBb0I7QUFFbkQsOEVBQThFO0FBQzlFLDBCQUEwQjtBQUMxQiw4RUFBOEU7QUFFOUUsU0FBUyxlQUFlLENBQUMsUUFBZ0I7SUFDdkMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUM5QyxVQUFVLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN4QixHQUFHLENBQUMsYUFBYSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUMs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
309
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidjBfdG9fdjEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy90c19taWdyYXRpb24vbWlncmF0b3JzL3YwX3RvX3YxLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDdkMsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFDdEMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUU1Qiw4RUFBOEU7QUFDOUUsdURBQXVEO0FBQ3ZELDhFQUE4RTtBQUU5RSxvQ0FBb0M7QUFDcEMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDckQsNkJBQTZCO0FBQzdCLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUN6QixxQkFBcUI7QUFDckIsTUFBTSxjQUFjLEdBQUcsQ0FBQyxDQUFDO0FBQ3pCLDZCQUE2QjtBQUM3QixNQUFNLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztBQUM1Qix1QkFBdUI7QUFDdkIsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDO0FBQzVCLDZCQUE2QjtBQUM3QixNQUFNLGtCQUFrQixHQUFHLEVBQUUsQ0FBQyxDQUFDLG9CQUFvQjtBQUVuRCw4RUFBOEU7QUFDOUUsMEJBQTBCO0FBQzFCLDhFQUE4RTtBQUU5RSxTQUFTLGVBQWUsQ0FBQyxRQUFnQjtJQUN2QyxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzlDLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3hCLEdBQUcsQ0FBQyxhQUFhLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3JDLEdBQUcsQ0FBQyxVQUFVLENBQUMs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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdb",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "private": false,
5
5
  "description": "A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.",
6
6
  "exports": {
@@ -16,8 +16,7 @@
16
16
  "@design.estate/dees-element": "^2.2.4",
17
17
  "@git.zone/tsbuild": "^4.4.2",
18
18
  "@git.zone/tsbundle": "^2.11.4",
19
- "@git.zone/tsrun": "^2.0.6",
20
- "@git.zone/tsrust": "^1.9.1",
19
+ "@git.zone/tsrust": "^1.10.0",
21
20
  "@git.zone/tstest": "^4.0.0",
22
21
  "@types/node": "26.2.0",
23
22
  "mongodb": "^7.5.0"
@@ -31,7 +30,6 @@
31
30
  ],
32
31
  "files": [
33
32
  "ts/**/*",
34
- "scripts/**/*",
35
33
  "ts_web/**/*",
36
34
  "dist/**/*",
37
35
  "dist_*/**/*",
@@ -75,7 +73,7 @@
75
73
  "test:rust": "(cd rust && cargo test)",
76
74
  "build": "(tsbundle) && (tsbuild tsfolders) && (tsrust)",
77
75
  "buildDocs": "tsdoc",
78
- "release:check-builders": "tsrun scripts/release-artifacts.ts check",
79
- "release:build-artifacts": "tsrun scripts/release-artifacts.ts build"
76
+ "release:check-builders": "tsrust matrix check",
77
+ "release:build-artifacts": "(tsbundle) && (tsbuild tsfolders) && (tsrust matrix build)"
80
78
  }
81
79
  }
package/readme.hints.md CHANGED
@@ -1,11 +1,19 @@
1
1
  # smartdb hints
2
2
 
3
+ ## Authoritative File Recovery
4
+
5
+ - `data.rdb` is the sole runtime source for rebuilding KeyDir. The v1 `keydir.hint` format cannot prove completeness or data-generation identity, so existing regular hint files are tolerated for compatibility and diagnostics but their contents are never loaded as runtime state. New shutdowns, compactions, and v0-to-v1 migrations do not publish hints.
6
+ - `KeyDir::build_from_data_file()` streams every payload through CRC32 in fixed-size chunks before allocating its key and never allocates the declared value length. It may classify only a final prefix shorter than `RECORD_HEADER_SIZE` with matching record-magic bytes as an incomplete tail. A full header with a short payload and every checksum, magic, length, or UTF-8 key failure are corruption and must leave `data.rdb` unchanged.
7
+ - Compaction durably removes any legacy hint and syncs the collection directory before replacing `data.rdb`. This ordering forces SmartDB 5.2.1 and earlier to rebuild after downgrade even when the compacted file happens to have the hint's old recorded size.
8
+ - Startup maintenance may skip a publication-held database. Its first later storage access still performs the authoritative scan and fails unchanged on corruption; never infer validation of held data from listener startup alone.
9
+ - Every cancellation-unsafe maintenance, fencing, auth, or storage worker must own a clone of the retained storage-root capability through its final file/lock cleanup. Provider installation is a finite joined startup sweep; periodic maintenance starts only after listener setup has no remaining fallible step. `close()` joins maintenance, `RustDb::drop()` requests cooperative shutdown, and Unix socket cleanup is bound to the exact device/inode created by `bind()`.
10
+
3
11
  ## Stopped Storage-Root Relocation
4
12
 
5
13
  - `LocalSmartDb.relocateStoppedStorageRoot()` is the only supported path-changing operation. It uses a dedicated serialized mutating sidecar runner; do not route it through the disposable offline-inspection helper. A bridge whose termination is unconfirmed remains in the static cleanup gate, and no later relocation may spawn until cleanup succeeds.
6
14
  - Rust owns the descriptor-relative operation in `rust/crates/rustdb/src/storage_root_relocation.rs`. It never constructs `RustDb`. The fixed journal phases are `preparing`, `prepared`, `exchanged`, `providerPublished`, `sentinelPublished`, and `cleaned`; retries reconcile the exact source-root/destination-absent-or-receipt topology or source-receipt/destination-root topology before continuing.
7
15
  - The destination receipt is published with an exclusive rename, then exchanged with the source root using an atomic swap (`RENAME_NOREPLACE`/`RENAME_EXCHANGE` on Linux and `RENAME_EXCL`/`RENAME_SWAP` on macOS). Provider and sentinel replacement also uses checked exchanges, retaining each previous object in the workspace until the replacement is durable and verified. The original root inode ends at the destination and the original receipt inode remains at the source indefinitely. Parent, root, internal, lock, provider, and sentinel identities are revalidated around publication.
8
- - Eligibility is intentionally narrow: Linux on the existing supported local-filesystem set or macOS on qualified local APFS, exact current owner/provider/sentinel metadata, no auth path digest, and no resource receipt, allocation, retired lock, durable database/transaction workspace, database marker, symlink, unknown entry, or unsafe ownership/mode/link profile. macOS additionally rejects case, Unicode, mount, and ancestry aliases by retained physical identity. Missing metadata is rejected and never initialized by relocation.
16
+ - Eligibility is intentionally narrow: Linux on the existing supported local-filesystem set or macOS on qualified local APFS, exact current owner/provider/sentinel metadata, no auth path digest, and no active resource receipt, allocation, database-mutation workspace, unrelated marker, symlink, unknown entry, or unsafe ownership/mode/link profile. A transaction workspace is eligible only when `rustdb-storage` validates its exact `cleaned` internal journal and matching database transaction marker; workspace debris, unfinished phases, mismatches, and allocation bindings remain unsupported. Empty retired legacy locks are eligible only with the canonical resource-digest/inode name parsed by `resource_fencing`, within its existing bound, and tied to one of those terminal transaction resources. macOS additionally rejects case, Unicode, mount, and ancestry aliases by retained physical identity. Missing metadata is rejected and never initialized by relocation.
9
17
  - Both TypeScript startup paths check for `.__rustdb_internal/storage-root-relocation` before `StorageMigrator`. Rust checks immediately after securely opening an existing root and repeats the descriptor-relative check after acquiring the owner lock, before storage initialization. This closes the interval where a relocation can create its workspace and release the lock after startup's first inspection. Occupancy or inspection ambiguity is a startup error; only an exact relocation retry performs recovery.
10
18
  - Completed roots retain the SmartDB 5.0.1 provider/sentinel JSON shape, changing only their storage-root path digest. Interrupted Linux state requires 5.1.0 or newer; interrupted macOS state requires a macOS-capable release. Exact replay lasts only while the destination remains current; a later relocation supersedes the old source/destination tuple.
11
19
 
@@ -44,4 +52,5 @@
44
52
  - `pnpm run test:rust` runs the Rust unit tests; plain `cargo test` in `rust/` works as well.
45
53
  - Verify linkage manually with `ldd dist_rust/<binary>_linux_amd64` → "statically linked".
46
54
  - Do NOT verify feature presence in release binaries with `strings`/`grep`: on x86_64 release builds LLVM embeds short string literals (≤16 bytes, e.g. BSON doc keys like `volatileOnly`) as instruction immediates, so they are invisible to byte scans while the aarch64 binary shows them in `.rodata`. This produces false "stale binary" alarms. Verify behavior by running the binary (the wire tests spawn the host-arch binary from `dist_rust/`), or disassemble: `objdump -d <bin> | grep <hex-of-literal-LE>`.
47
- - Release builds use tsrust 1.9 host partitions and strict sidecar assembly. The enabled gitzone test preflight compiles and executes a static Linux arm64 probe through `binfmt` and checks macOS Rosetta before versioning. The post-version build transfers exact HEAD by Git bundle, executes all four target binaries, and assembles separate Linux and macOS inputs. Remote cleanup is limited to the unique sentinel-owned child below `SMARTDB_RELEASE_MACOS_TEMP_ROOT`.
55
+ - Release builds use the tsrust 1.10 native matrix. The enabled gitzone test preflight checks the exact project-local tool, cross-execution, worker identities, toolchains, and remote root before versioning. The post-version build bundles TypeScript, transfers exact HEAD into isolated local and SSH workers, installs the frozen lockfile, executes all four target binaries, retrieves bounded artifact sets, and transactionally publishes the complete provenance matrix. Remote cleanup is limited to the unique owner-marked child below `SMARTDB_RELEASE_MACOS_TEMP_ROOT` and gates publication.
56
+ - Matrix workers use synthetic homes and therefore do not inherit operator-global pnpm policy. Keep the workspace release-age exception pinned to the exact audited `@git.zone/tsrust` version needed by an immediate upstream-to-downstream release; do not disable the age gate or widen the exception to a scope.
package/readme.md CHANGED
@@ -398,7 +398,7 @@ const released = await server.commitDatabasePublication({
398
398
  });
399
399
  ```
400
400
 
401
- The held barrier survives restart and blocks wire commands, transactions, startup recovery, compaction, index restoration, and close-time hint writes for that database while unrelated databases remain available. Commit is exact and idempotent. A successful commit removes the raw capability from durable state and retains only protected verification material. Provider/root identity, durable fenced-mode markers, and bounded startup validation make copied, missing, corrupt, or legacy-active publication state fail closed. A higher fencing token compacts resolved older receipts, so long-lived coordinators do not exhaust receipt capacity.
401
+ The held barrier survives restart and blocks wire commands, transactions, startup recovery, compaction, and index restoration for that database while unrelated databases remain available. Commit is exact and idempotent. A successful commit removes the raw capability from durable state and retains only protected verification material. Provider/root identity, durable fenced-mode markers, and bounded startup validation make copied, missing, corrupt, or legacy-active publication state fail closed. A higher fencing token compacts resolved older receipts, so long-lived coordinators do not exhaust receipt capacity.
402
402
 
403
403
  Durable database publication holds and allocation fencing remain Linux-only. The macOS support described under stopped storage-root relocation does not enable these runtime mutation features.
404
404
 
@@ -586,7 +586,7 @@ interface ILocalSmartDbStoppedStorageRootRelocationReceipt {
586
586
 
587
587
  On success, the destination directory is the original storage-root inode and the source path is an exact mode-0600 regular-file receipt. SmartDB does not remove that source receipt. Data, WAL, hint, and index bytes are not copied or rewritten. `providerRootId`, root device/inode, and every provider/sentinel field are preserved; only `storageRootSha256` changes to the destination's canonical path digest. This leaves a completed destination compatible with SmartDB 5.0.1.
588
588
 
589
- Relocation deliberately supports only the plain, unbound `LocalSmartDb` topology. The existing owner lock, `provider-identity.json`, and resource-fencing sentinel must all be present; the operation never creates missing ownership metadata. A provider identity containing `authUsersPathSha256` is unsupported. Any durable fence receipt or capability, allocation state, retired fence lock, database mutation/transaction journal, allocation/publication marker, symlink, unknown entry, unsafe mode/owner/link count, exhausted scan bound, active owner, or ambiguous identity fails closed.
589
+ Relocation deliberately supports only the plain, unbound `LocalSmartDb` topology. The existing owner lock, `provider-identity.json`, and resource-fencing sentinel must all be present; the operation never creates missing ownership metadata. A provider identity containing `authUsersPathSha256` is unsupported. One narrow terminal transaction topology is also eligible: each retained internal transaction journal must be in the exact `cleaned` phase, contain no workspace debris or allocation binding, and match the exact transaction-publication marker in its database. Canonically named, empty retired legacy fence locks are accepted only when their resource digest belongs to one of those terminal transactions and their encoded inode matches the retained file. These terminal artifacts move unchanged with the root and continue the existing transaction token sequence after restart. Any unfinished or mismatched transaction, durable database-mutation journal, active fence receipt or capability, allocation state, unrelated or malformed retired lock, allocation/general publication marker, symlink, unknown entry, unsafe mode/owner/link count, exhausted scan bound, active owner, or ambiguous identity fails closed.
590
590
 
591
591
  The operation journals phases `preparing`, `prepared`, `exchanged`, `providerPublished`, `sentinelPublished`, and `cleaned`. If a call returns `RECOVERY_REQUIRED`, stop all startup attempts and retry the exact same normalized source path, destination path, and `relocationId` with the same SmartDB version that began the relocation or a newer version. Startup rejects any relocation-workspace occupancy and does not mutate or recover it. Do not delete, rename, edit, or replace either path between attempts. Linux relocation state created by SmartDB 5.1.0 remains recoverable by 5.1.0 or newer; macOS recovery requires a macOS-capable release. A completed root remains 5.0.1-compatible.
592
592
 
@@ -682,7 +682,7 @@ const namespaces = await LocalSmartDb.inspectOfflinePhysicalNamespaces({
682
682
 
683
683
  The result is exactly `{ schemaVersion: 1, databases: Array<{ name, collections }> }`. Databases and each database's collections are sorted by physical name, empty database directories are retained, and no documents, index definitions, record identifiers, or values cross the management boundary.
684
684
 
685
- The accepted root must contain the current `.__rustdb_internal/storage-owner.lock`; legacy roots without that lock are rejected. The sidecar takes the shared owner lock, so an active engine causes the call to fail. Every reported collection must have the canonical complete profile of regular `data.rdb`, `wal.rdb`, and `indexes.json` files. A regular `keydir.hint` is optional and is treated only as an allowed cache file. Missing canonical files, unexpected collection entries, malformed index metadata, invalid data or WAL records, symlinks, cross-filesystem traversal, resource-limit exhaustion, and changes observed during inspection fail the complete operation. No partial result is returned and the storage tree is not mutated.
685
+ The accepted root must contain the current `.__rustdb_internal/storage-owner.lock`; legacy roots without that lock are rejected. The sidecar takes the shared owner lock, so an active engine causes the call to fail. Every reported collection must have the canonical complete profile of regular `data.rdb`, `wal.rdb`, and `indexes.json` files. A regular `keydir.hint` is optional and is treated only as an allowed legacy cache file; runtime recovery ignores its contents. Missing canonical files, unexpected collection entries, malformed index metadata, invalid data or WAL records, symlinks, cross-filesystem traversal, resource-limit exhaustion, and changes observed during inspection fail the complete operation. No partial result is returned and the storage tree is not mutated.
686
686
 
687
687
  Inspection performs two full bounded passes and returns only if their namespace results and retained filesystem observations agree. The entry, database, collection, index, file-byte, and record limits are enforced independently on each pass, so total validation work is at most twice the corresponding caller cap. The result-size cap applies to the single serialized result. This is a contract for the current owner-locked canonical SmartDB file-storage profile, not a claim of compatibility with arbitrary MongoDB layouts, legacy SmartDB roots, or unspecified future storage formats.
688
688
 
@@ -1015,7 +1015,7 @@ Cross-compiled for `linux_amd64`, `linux_arm64`, `macos_amd64`, and `macos_arm64
1015
1015
 
1016
1016
  ### Multi-host release artifacts
1017
1017
 
1018
- Normal `tsrust` builds select only the current host's partition from `targetsByHost`. Releases coordinate both partitions after the version commit exists, then publish only after every exact-commit binary has executed and passed strict provenance assembly.
1018
+ Normal `tsrust` builds select only the current host's partition from `targetsByHost`. Releases use `tsrust matrix` to coordinate both partitions after the version commit exists, then publish only after every exact-commit binary has executed and passed strict provenance assembly.
1019
1019
 
1020
1020
  The release coordinator must be Linux amd64 with `aarch64-linux-gnu-gcc` and a registered Linux arm64 `binfmt` interpreter. Set these environment variables to an authorized Apple Silicon builder with Rosetta and a dedicated temporary root:
1021
1021
 
@@ -1024,14 +1024,14 @@ export SMARTDB_RELEASE_MACOS_SSH="release-user@mac-builder.example"
1024
1024
  export SMARTDB_RELEASE_MACOS_TEMP_ROOT="/absolute/dedicated/smartdb-release"
1025
1025
  ```
1026
1026
 
1027
- `gitzone release` first runs `pnpm run release:check-builders`, before creating a version commit or tag. It compiles and executes a static arm64 probe and rejects missing cross-toolchain or `binfmt` execution, an unsuitable macOS host, missing Rosetta, missing tools, or an unwritable remote temporary root. The release build then:
1027
+ `gitzone release` first runs `pnpm run release:check-builders`, before creating a version commit or tag. It verifies the project-local tsrust version, compiles and executes the required architecture probes, and rejects missing cross-toolchains or emulation, an unsuitable macOS host, missing tools, or an unsafe remote temporary root. The release build then:
1028
1028
 
1029
- 1. Builds and executes the Linux amd64 and arm64 binaries.
1030
- 2. Transfers a Git bundle of the unpushed release commit into a unique sentinel-owned macOS workspace.
1031
- 3. Builds both Darwin targets from the frozen lockfile, verifies their Cargo bytes and signatures, and executes both architectures.
1032
- 4. Copies each host output into separate `.nogit` inputs and runs strict `tsrust assemble` into `dist_rust/`.
1029
+ 1. Builds the TypeScript package and debug UI bundle from the version commit.
1030
+ 2. Streams an exact Git bundle into isolated local Linux and remote macOS worker workspaces and installs each frozen lockfile.
1031
+ 3. Builds and executes the Linux amd64, Linux arm64, macOS arm64, and macOS amd64 binaries on their assigned workers.
1032
+ 4. Retrieves bounded artifact sets and transactionally publishes the complete provenance-checked matrix into `dist_rust/`.
1033
1033
 
1034
- No host address or path is stored in the repository. A failed run retains its unique diagnostic directories and prints their paths. Successful cleanup can remove only the generated child whose path and random ownership sentinel both match.
1034
+ No host address or path is stored in the repository. A failed run retains its unique local diagnostics and owner-marked remote diagnostics, then prints their paths. Remote cleanup is an owner-validated publication gate, and an incomplete matrix never replaces the prior `dist_rust/`.
1035
1035
 
1036
1036
  ### Storage Engine Reliability 🔒
1037
1037
 
@@ -1040,8 +1040,8 @@ The Bitcask-style file storage engine includes several reliability features:
1040
1040
  - **Write-ahead log (WAL)** — every write is logged before being applied, with crash recovery on restart; once committed entries grow past 16 MiB the WAL is checkpointed and truncated at runtime, so `wal.rdb` does not grow unbounded
1041
1041
  - **CRC32 checksums** — every record is integrity-checked on read
1042
1042
  - **Automatic compaction** — dead records are reclaimed when they exceed 50% of file size, runs on startup and after every write
1043
- - **Hint file staleness detection** — the hint file records the data file size at write time; if data.rdb changed since (e.g. crash after a delete), the engine falls back to a full scan to ensure tombstones are not lost
1044
- - **Torn-tail repair** — startup scans `data.rdb` to the last valid record, truncates invalid trailing bytes, and preserves all verified records after interrupted writes
1043
+ - **Authoritative streamed recovery** — collection initialization rebuilds KeyDir from the complete `data.rdb` record stream, verifies each CRC without allocating the declared value size, and never trusts legacy hint files as runtime state; publication-held databases defer this work until first access after release
1044
+ - **Conservative torn-header repair** — collection initialization truncates only a final sequence shorter than the fixed record header whose available record-magic bytes match; checksum, magic, length, UTF-8 key, and full-header short-payload failures stop initialization without changing `data.rdb`
1045
1045
  - **Stale socket cleanup** — orphaned `/tmp/smartdb-*.sock` files from crashed instances are automatically cleaned up on startup
1046
1046
 
1047
1047
  ### Data Integrity CLI 🔍
@@ -1058,14 +1058,14 @@ The Rust binary includes an offline integrity checker:
1058
1058
  # Database: mydb
1059
1059
  # Collection: users
1060
1060
  # Header: OK
1061
- # Records: 1,234 (1,200 live, 34 tombstones)
1062
- # Data size: 2.1 MB
1061
+ # Records: 1234 (1200 live, 34 tombstones)
1062
+ # Data size: 2097152 bytes
1063
1063
  # Duplicates: 0
1064
1064
  # CRC errors: 0
1065
- # Hint file: OK
1065
+ # Hint file: legacy cache (ignored)
1066
1066
  ```
1067
1067
 
1068
- Checks file headers, record CRC32 checksums, duplicate `_id` entries, and hint file consistency. Exit code 1 if any errors are found.
1068
+ Checks file headers, record CRC32 checksums, and duplicate `_id` entries. Existing hint files are reported as ignored legacy caches and do not determine data integrity. Exit code 1 if any authoritative-data errors are found.
1069
1069
 
1070
1070
  ---
1071
1071
 
package/readme.plan.md CHANGED
@@ -3,11 +3,22 @@
3
3
  Status: canonical direction, approved 2026-08-17. Reread before starting engine or
4
4
  managed-service work.
5
5
 
6
- Baseline: `@push.rocks/smartdb` 5.0.1. SmartDB is a Rust database engine with a
6
+ Baseline: `@push.rocks/smartdb` 5.2.1. SmartDB is a Rust database engine with a
7
7
  TypeScript lifecycle facade, a MongoDB wire-protocol command surface, file and
8
8
  memory storage, authentication, transactions, resource fencing, and official
9
9
  MongoDB Node.js driver integration.
10
10
 
11
+ Current execution status:
12
+
13
+ - the legal and provenance gate has not passed
14
+ - Phase 0 and the Phase 1 gate have not passed
15
+ - 5.x maintenance now treats `data.rdb` as authoritative, ignores unverifiable
16
+ legacy hint contents, streams CRC validation, and repairs only an incomplete
17
+ final record-header prefix
18
+ - strict runtime WAL recovery, ambiguous point-write reconciliation, crash-safe
19
+ ordinary DDL, debug-server security, and general crash/soak qualification remain
20
+ separate required slices
21
+
11
22
  ## Product objective
12
23
 
13
24
  Build SmartDB into a commercially operated, MongoDB-driver-compatible managed
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdb',
6
- version: '5.2.0',
6
+ version: '5.2.2',
7
7
  description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
8
  }
@@ -13,7 +13,6 @@ const FILE_MAGIC = Buffer.from('SMARTDB\0', 'ascii');
13
13
  const FORMAT_VERSION = 1;
14
14
  /** File type tags */
15
15
  const FILE_TYPE_DATA = 1;
16
- const FILE_TYPE_HINT = 3;
17
16
  /** File header total size */
18
17
  const FILE_HEADER_SIZE = 64;
19
18
  /** Per-record magic */
@@ -63,19 +62,6 @@ function encodeDataRecord(timestamp: bigint, key: Buffer, value: Buffer): Buffer
63
62
  return buf;
64
63
  }
65
64
 
66
- function encodeHintEntry(key: string, offset: bigint, recordLen: number, valueLen: number, timestamp: bigint): Buffer {
67
- const keyBuf = Buffer.from(key, 'utf-8');
68
- const buf = Buffer.alloc(4 + keyBuf.length + 8 + 4 + 4 + 8);
69
- let pos = 0;
70
- buf.writeUInt32LE(keyBuf.length, pos); pos += 4;
71
- keyBuf.copy(buf, pos); pos += keyBuf.length;
72
- buf.writeBigUInt64LE(offset, pos); pos += 8;
73
- buf.writeUInt32LE(recordLen, pos); pos += 4;
74
- buf.writeUInt32LE(valueLen, pos); pos += 4;
75
- buf.writeBigUInt64LE(timestamp, pos);
76
- return buf;
77
- }
78
-
79
65
  // ---------------------------------------------------------------------------
80
66
  // CRC32 (matching crc32fast in Rust)
81
67
  // ---------------------------------------------------------------------------
@@ -214,7 +200,7 @@ const syncFile = async (filePathArg: string, signalArg?: AbortSignal): Promise<v
214
200
  const assertPublishedMigrationComplete = async (
215
201
  collectionDirectoryArg: string,
216
202
  ): Promise<void> => {
217
- for (const fileName of ['data.rdb', 'keydir.hint', 'indexes.json']) {
203
+ for (const fileName of ['data.rdb', 'indexes.json']) {
218
204
  let fileStat: Awaited<ReturnType<typeof fs.lstat>>;
219
205
  try {
220
206
  fileStat = await fs.lstat(path.join(collectionDirectoryArg, fileName));
@@ -239,13 +225,6 @@ const assertPublishedMigrationComplete = async (
239
225
  // Migration: v0 (JSON) → v1 (Bitcask binary)
240
226
  // ---------------------------------------------------------------------------
241
227
 
242
- interface IKeyDirEntry {
243
- offset: bigint;
244
- recordLen: number;
245
- valueLen: number;
246
- timestamp: bigint;
247
- }
248
-
249
228
  /**
250
229
  * Migrate a storage directory from v0 (JSON-per-collection) to v1 (Bitcask binary).
251
230
  *
@@ -320,8 +299,6 @@ export async function migrateV0ToV1(
320
299
  const headerBuf = writeFileHeader(FILE_TYPE_DATA);
321
300
  await writeBuffer(dataFileHandle, headerBuf, signalArg);
322
301
 
323
- let currentOffset = BigInt(FILE_HEADER_SIZE);
324
- const keydir: Map<string, IKeyDirEntry> = new Map();
325
302
  const ts = BigInt(Date.now());
326
303
 
327
304
  for (const doc of docs) {
@@ -348,40 +325,10 @@ export async function migrateV0ToV1(
348
325
  const record = encodeDataRecord(ts, keyBuf, valueBuf);
349
326
  await writeBuffer(dataFileHandle, record, signalArg);
350
327
 
351
- keydir.set(idHex, {
352
- offset: currentOffset,
353
- recordLen: record.length,
354
- valueLen: valueBuf.length,
355
- timestamp: ts,
356
- });
357
-
358
- currentOffset += BigInt(record.length);
359
328
  }
360
329
 
361
330
  await dataFileHandle.sync();
362
331
  signalArg?.throwIfAborted();
363
-
364
- // Write keydir.hint
365
- const hintPath = path.join(stagingDir, 'keydir.hint');
366
- await withFileHandle(hintPath, 'wx', async (hintFileHandle) => {
367
- await writeBuffer(hintFileHandle, writeFileHeader(FILE_TYPE_HINT), signalArg);
368
- for (const [key, entry] of keydir) {
369
- signalArg?.throwIfAborted();
370
- await writeBuffer(
371
- hintFileHandle,
372
- encodeHintEntry(
373
- key,
374
- entry.offset,
375
- entry.recordLen,
376
- entry.valueLen,
377
- entry.timestamp,
378
- ),
379
- signalArg,
380
- );
381
- }
382
- await hintFileHandle.sync();
383
- signalArg?.throwIfAborted();
384
- }, signalArg);
385
332
  }, signalArg);
386
333
 
387
334
  // Copy indexes.json if it exists
@@ -1,626 +0,0 @@
1
- import * as childProcess from 'node:child_process';
2
- import * as crypto from 'node:crypto';
3
- import * as fs from 'node:fs';
4
- import * as path from 'node:path';
5
-
6
- type TReleaseArtifactMode = 'check' | 'build';
7
-
8
- interface IReleaseArtifactEnvironment {
9
- macosSsh: string;
10
- macosTempRoot: string;
11
- }
12
-
13
- const provenanceSuffix = '.tsrust-build.json';
14
- const defaultCommandTimeoutMs = 60 * 60 * 1000;
15
- const capabilityCommandTimeoutMs = 5 * 60 * 1000;
16
- const forceKillDelayMs = 10 * 1000;
17
- const maximumCapturedOutputBytes = 16 * 1024 * 1024;
18
- const sshConnectionArgs = [
19
- '-o',
20
- 'ServerAliveInterval=15',
21
- '-o',
22
- 'ServerAliveCountMax=2',
23
- ];
24
-
25
- type TForwardedSignal = 'SIGINT' | 'SIGTERM' | 'SIGHUP';
26
-
27
- const shellQuote = (valueArg: string): string => `'${valueArg.replace(/'/g, `'"'"'`)}'`;
28
-
29
- const executeCommand = (
30
- commandArg: string,
31
- argsArg: string[],
32
- optionsArg: {
33
- cwd?: string;
34
- capture?: boolean;
35
- timeoutMs?: number;
36
- heartbeatStdin?: boolean;
37
- } = {},
38
- ): Promise<string> =>
39
- new Promise((resolveArg, rejectArg) => {
40
- const capture = optionsArg.capture === true;
41
- const child = childProcess.spawn(commandArg, argsArg, {
42
- cwd: optionsArg.cwd || process.cwd(),
43
- env: process.env,
44
- stdio: capture
45
- ? ['ignore', 'pipe', 'pipe']
46
- : optionsArg.heartbeatStdin
47
- ? ['pipe', 'inherit', 'inherit']
48
- : 'inherit',
49
- detached: true,
50
- });
51
- let stdout = '';
52
- let stderr = '';
53
- let capturedOutputBytes = 0;
54
- let timedOut = false;
55
- let outputLimitExceeded = false;
56
- let forwardedSignal: TForwardedSignal | undefined;
57
- let forwardedSignalCount = 0;
58
- let settled = false;
59
- let forceKillTimer: NodeJS.Timeout | undefined;
60
- let heartbeatTimer: NodeJS.Timeout | undefined;
61
-
62
- const killProcessGroup = (signalArg: NodeJS.Signals): void => {
63
- if (!child.pid) return;
64
- try {
65
- process.kill(-child.pid, signalArg);
66
- } catch {
67
- try {
68
- child.kill(signalArg);
69
- } catch {
70
- // The process may already have exited.
71
- }
72
- }
73
- };
74
-
75
- const terminateProcessGroup = (signalArg: NodeJS.Signals): void => {
76
- killProcessGroup(signalArg);
77
- if (!forceKillTimer) {
78
- forceKillTimer = setTimeout(
79
- () => killProcessGroup('SIGKILL'),
80
- forceKillDelayMs,
81
- );
82
- }
83
- };
84
-
85
- const processGroupExists = (): boolean => {
86
- if (!child.pid) return false;
87
- try {
88
- process.kill(-child.pid, 0);
89
- return true;
90
- } catch (error) {
91
- if ((error as NodeJS.ErrnoException).code === 'ESRCH') return false;
92
- throw error;
93
- }
94
- };
95
-
96
- const waitForProcessGroupExit = async (timeoutMsArg: number): Promise<boolean> => {
97
- const deadline = Date.now() + timeoutMsArg;
98
- while (processGroupExists() && Date.now() < deadline) {
99
- await new Promise((resolveArg) => setTimeout(resolveArg, 100));
100
- }
101
- return !processGroupExists();
102
- };
103
-
104
- const ensureProcessGroupStopped = async (): Promise<void> => {
105
- if (!processGroupExists()) return;
106
- terminateProcessGroup('SIGTERM');
107
- if (!(await waitForProcessGroupExit(forceKillDelayMs + 1000))) {
108
- killProcessGroup('SIGKILL');
109
- if (!(await waitForProcessGroupExit(2000))) {
110
- throw new Error(`${commandArg} left a live descendant process group`);
111
- }
112
- }
113
- };
114
-
115
- const signalHandlers = new Map<TForwardedSignal, () => void>();
116
- for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as TForwardedSignal[]) {
117
- const handler = (): void => {
118
- forwardedSignal = signal;
119
- forwardedSignalCount += 1;
120
- if (forwardedSignalCount === 1) terminateProcessGroup(signal);
121
- else killProcessGroup('SIGKILL');
122
- };
123
- signalHandlers.set(signal, handler);
124
- process.on(signal, handler);
125
- }
126
-
127
- const timeout = setTimeout(() => {
128
- timedOut = true;
129
- terminateProcessGroup('SIGTERM');
130
- }, optionsArg.timeoutMs || defaultCommandTimeoutMs);
131
-
132
- if (optionsArg.heartbeatStdin) {
133
- child.stdin?.on('error', () => {
134
- // Process close handles a lost stdin channel.
135
- });
136
- const sendHeartbeat = (): void => {
137
- if (!child.stdin?.destroyed) child.stdin?.write('heartbeat\n');
138
- };
139
- sendHeartbeat();
140
- heartbeatTimer = setInterval(sendHeartbeat, 1000);
141
- }
142
-
143
- const cleanup = (): void => {
144
- clearTimeout(timeout);
145
- if (forceKillTimer) clearTimeout(forceKillTimer);
146
- if (heartbeatTimer) clearInterval(heartbeatTimer);
147
- child.stdin?.end();
148
- for (const [signal, handler] of signalHandlers) {
149
- process.off(signal, handler);
150
- }
151
- };
152
-
153
- const captureChunk = (targetArg: 'stdout' | 'stderr', chunkArg: Buffer): void => {
154
- capturedOutputBytes += chunkArg.length;
155
- if (capturedOutputBytes > maximumCapturedOutputBytes) {
156
- outputLimitExceeded = true;
157
- terminateProcessGroup('SIGTERM');
158
- return;
159
- }
160
- if (targetArg === 'stdout') stdout += chunkArg.toString('utf8');
161
- else stderr += chunkArg.toString('utf8');
162
- };
163
- child.stdout?.on('data', (chunkArg: Buffer) => captureChunk('stdout', chunkArg));
164
- child.stderr?.on('data', (chunkArg: Buffer) => captureChunk('stderr', chunkArg));
165
-
166
- child.once('error', (errorArg) => {
167
- if (settled) return;
168
- settled = true;
169
- cleanup();
170
- rejectArg(errorArg);
171
- });
172
- child.once('close', (codeArg, signalArg) => {
173
- if (settled) return;
174
- settled = true;
175
- void (async () => {
176
- try {
177
- await ensureProcessGroupStopped();
178
- cleanup();
179
- if (codeArg !== 0 && capture) {
180
- if (stdout) process.stdout.write(stdout);
181
- if (stderr) process.stderr.write(stderr);
182
- }
183
- if (timedOut) {
184
- rejectArg(new Error(`${commandArg} exceeded its command timeout`));
185
- } else if (outputLimitExceeded) {
186
- rejectArg(new Error(`${commandArg} exceeded the captured output limit`));
187
- } else if (forwardedSignal) {
188
- rejectArg(new Error(`${commandArg} was interrupted by ${forwardedSignal}`));
189
- } else if (codeArg !== 0) {
190
- rejectArg(
191
- new Error(
192
- `${commandArg} exited with ${signalArg ? `signal ${signalArg}` : `status ${codeArg}`}`,
193
- ),
194
- );
195
- } else {
196
- resolveArg(stdout.trim());
197
- }
198
- } catch (error) {
199
- cleanup();
200
- rejectArg(error);
201
- }
202
- })();
203
- });
204
- });
205
-
206
- const runCommand = async (
207
- commandArg: string,
208
- argsArg: string[],
209
- cwdArg: string = process.cwd(),
210
- timeoutMsArg: number = defaultCommandTimeoutMs,
211
- heartbeatStdinArg: boolean = false,
212
- ): Promise<void> => {
213
- await executeCommand(commandArg, argsArg, {
214
- cwd: cwdArg,
215
- timeoutMs: timeoutMsArg,
216
- heartbeatStdin: heartbeatStdinArg,
217
- });
218
- };
219
-
220
- const captureCommand = async (
221
- commandArg: string,
222
- argsArg: string[],
223
- cwdArg: string = process.cwd(),
224
- ): Promise<string> => {
225
- return executeCommand(commandArg, argsArg, {
226
- cwd: cwdArg,
227
- capture: true,
228
- timeoutMs: capabilityCommandTimeoutMs,
229
- });
230
- };
231
-
232
- const loadEnvironment = (): IReleaseArtifactEnvironment => {
233
- const macosSsh = process.env.SMARTDB_RELEASE_MACOS_SSH?.trim() || '';
234
- const macosTempRoot = (process.env.SMARTDB_RELEASE_MACOS_TEMP_ROOT?.trim() || '').replace(
235
- /\/+$/,
236
- '',
237
- );
238
-
239
- if (!/^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$/.test(macosSsh)) {
240
- throw new Error(
241
- 'SMARTDB_RELEASE_MACOS_SSH must use the user@host form without SSH options',
242
- );
243
- }
244
- if (
245
- !/^\/[A-Za-z0-9._/-]+$/.test(macosTempRoot) ||
246
- path.posix.normalize(macosTempRoot) !== macosTempRoot ||
247
- macosTempRoot.split('/').filter(Boolean).length < 2
248
- ) {
249
- throw new Error(
250
- 'SMARTDB_RELEASE_MACOS_TEMP_ROOT must be a non-root absolute path without spaces',
251
- );
252
- }
253
- return { macosSsh, macosTempRoot };
254
- };
255
-
256
- const runRemoteLoginScript = async (
257
- environmentArg: IReleaseArtifactEnvironment,
258
- scriptArg: string,
259
- timeoutMsArg: number = defaultCommandTimeoutMs,
260
- ): Promise<void> => {
261
- const ownedRemoteScript = [
262
- 'set -u',
263
- 'exec 3<&0',
264
- 'child_pid=',
265
- 'watchdog_pid=',
266
- 'kill_tree() {',
267
- ' local parent_pid children child_pid_to_kill',
268
- ' parent_pid="$1"',
269
- ' kill -STOP "$parent_pid" 2>/dev/null || return 0',
270
- ' children="$(/usr/bin/pgrep -P "$parent_pid" 2>/dev/null || true)"',
271
- ' for child_pid_to_kill in $children; do kill_tree "$child_pid_to_kill"; done',
272
- ' kill -KILL "$parent_pid" 2>/dev/null || true',
273
- '}',
274
- 'terminate_owned() { test -z "$child_pid" || kill_tree "$child_pid"; test -z "$watchdog_pid" || kill -KILL "$watchdog_pid" 2>/dev/null || true; }',
275
- 'watch_channel() { while IFS= read -r -t 45 ignored_line; do :; done; test -z "$child_pid" || kill_tree "$child_pid"; }',
276
- 'trap terminate_owned EXIT HUP INT TERM',
277
- `/bin/zsh -lc ${shellQuote(scriptArg)} </dev/null &`,
278
- 'child_pid=$!',
279
- 'watch_channel <&3 &',
280
- 'watchdog_pid=$!',
281
- 'wait "$child_pid"',
282
- 'exit_status=$?',
283
- 'child_pid=',
284
- 'kill -KILL "$watchdog_pid" 2>/dev/null || true',
285
- 'wait "$watchdog_pid" 2>/dev/null || true',
286
- 'watchdog_pid=',
287
- 'exec 3<&-',
288
- 'trap - EXIT HUP INT TERM',
289
- 'exit "$exit_status"',
290
- ].join('\n');
291
- await runCommand(
292
- 'ssh',
293
- [
294
- ...sshConnectionArgs,
295
- environmentArg.macosSsh,
296
- `zsh -lic ${shellQuote(`/bin/sh -c ${shellQuote(ownedRemoteScript)}`)}`,
297
- ],
298
- process.cwd(),
299
- timeoutMsArg,
300
- true,
301
- );
302
- };
303
-
304
- const checkBuilders = async (
305
- environmentArg: IReleaseArtifactEnvironment,
306
- ): Promise<void> => {
307
- if (process.platform !== 'linux' || process.arch !== 'x64') {
308
- throw new Error('SmartDB release artifact assembly requires a Linux amd64 coordinator');
309
- }
310
-
311
- await runCommand('git', ['--version'], process.cwd(), capabilityCommandTimeoutMs);
312
- await runCommand('pnpm', ['--version'], process.cwd(), capabilityCommandTimeoutMs);
313
- await captureCommand('/bin/sh', ['-c', 'command -v scp >/dev/null']);
314
- const linuxProbeParent = path.join(process.cwd(), '.nogit', 'smartdb-release-probe');
315
- fs.mkdirSync(linuxProbeParent, { recursive: true, mode: 0o700 });
316
- const linuxProbeDirectory = fs.mkdtempSync(path.join(linuxProbeParent, 'arm64-'));
317
- try {
318
- const sourcePath = path.join(linuxProbeDirectory, 'probe.c');
319
- const binaryPath = path.join(linuxProbeDirectory, 'probe');
320
- fs.writeFileSync(sourcePath, 'int main(void) { return 0; }\n');
321
- await runCommand(
322
- 'aarch64-linux-gnu-gcc',
323
- ['-static', '-O2', sourcePath, '-o', binaryPath],
324
- process.cwd(),
325
- capabilityCommandTimeoutMs,
326
- );
327
- await runCommand(binaryPath, [], process.cwd(), capabilityCommandTimeoutMs);
328
- } finally {
329
- fs.rmSync(linuxProbeDirectory, { recursive: true, force: true });
330
- }
331
-
332
- const probeToken = crypto.randomUUID();
333
- const probePath = `${environmentArg.macosTempRoot}/smartdb-release-check.${probeToken}`;
334
- const remoteCheckScript = [
335
- 'set -eu',
336
- 'test "$(uname -s)" = "Darwin"',
337
- 'test "$(uname -m)" = "arm64"',
338
- 'command -v git >/dev/null',
339
- 'command -v pnpm >/dev/null',
340
- 'command -v codesign >/dev/null',
341
- 'command -v cmp >/dev/null',
342
- `root=${shellQuote(environmentArg.macosTempRoot)}`,
343
- `probe=${shellQuote(probePath)}`,
344
- `test "$probe" = "$root/smartdb-release-check.${probeToken}"`,
345
- 'mkdir -p "$root"',
346
- 'test "$(cd "$root" && pwd -P)" = "$root"',
347
- 'created=0',
348
- `cleanup_probe() { if [ "$created" = 1 ]; then test ! -f "$probe/.smartdb-release-owner" || test "$(cat "$probe/.smartdb-release-owner")" = ${shellQuote(probeToken)} || return; rm -f "$probe/.smartdb-release-owner"; rmdir "$probe" 2>/dev/null || true; fi; }`,
349
- 'trap cleanup_probe EXIT HUP INT TERM',
350
- 'mkdir -m 700 "$probe"',
351
- 'created=1',
352
- `printf '%s\\n' ${shellQuote(probeToken)} > "$probe/.smartdb-release-owner"`,
353
- 'rm -f "$probe/.smartdb-release-owner"',
354
- 'rmdir "$probe"',
355
- 'trap - EXIT HUP INT TERM',
356
- '/usr/bin/arch -x86_64 /usr/bin/true',
357
- ].join('\n');
358
- await runRemoteLoginScript(
359
- environmentArg,
360
- remoteCheckScript,
361
- capabilityCommandTimeoutMs,
362
- );
363
- console.log('All SmartDB release builders can execute their assigned architectures.');
364
- };
365
-
366
- const assertArtifactSet = (directoryArg: string, targetsArg: string[]): void => {
367
- const expectedNames = targetsArg.flatMap((targetArg) => [
368
- `rustdb_${targetArg}`,
369
- `rustdb_${targetArg}${provenanceSuffix}`,
370
- ]);
371
- const actualNames = fs.readdirSync(directoryArg).sort();
372
- if (JSON.stringify(actualNames) !== JSON.stringify(expectedNames.sort())) {
373
- throw new Error(
374
- `Unexpected release artifact set in ${directoryArg}: ${actualNames.join(', ')}`,
375
- );
376
- }
377
- for (const target of targetsArg) {
378
- const binaryPath = path.join(directoryArg, `rustdb_${target}`);
379
- const stat = fs.lstatSync(binaryPath);
380
- if (!stat.isFile() || (stat.mode & 0o111) === 0) {
381
- throw new Error(`Release artifact is not an executable regular file: ${binaryPath}`);
382
- }
383
- }
384
- };
385
-
386
- const remoteWorkspacePath = (
387
- environmentArg: IReleaseArtifactEnvironment,
388
- gitCommitArg: string,
389
- ownerTokenArg: string,
390
- ): string =>
391
- `${environmentArg.macosTempRoot}/smartdb-release.${gitCommitArg}.${ownerTokenArg}`;
392
-
393
- const assertOwnedRemoteWorkspacePath = (
394
- environmentArg: IReleaseArtifactEnvironment,
395
- remoteDirectoryArg: string,
396
- gitCommitArg: string,
397
- ownerTokenArg: string,
398
- ): void => {
399
- const expectedDirectory = remoteWorkspacePath(
400
- environmentArg,
401
- gitCommitArg,
402
- ownerTokenArg,
403
- );
404
- if (
405
- remoteDirectoryArg !== expectedDirectory ||
406
- path.posix.dirname(remoteDirectoryArg) !== environmentArg.macosTempRoot ||
407
- path.posix.normalize(remoteDirectoryArg) !== remoteDirectoryArg
408
- ) {
409
- throw new Error(`Refusing unsafe macOS release workspace path: ${remoteDirectoryArg}`);
410
- }
411
- };
412
-
413
- const createRemoteWorkspace = async (
414
- environmentArg: IReleaseArtifactEnvironment,
415
- remoteDirectoryArg: string,
416
- gitCommitArg: string,
417
- ownerTokenArg: string,
418
- ): Promise<void> => {
419
- assertOwnedRemoteWorkspacePath(
420
- environmentArg,
421
- remoteDirectoryArg,
422
- gitCommitArg,
423
- ownerTokenArg,
424
- );
425
- const createScript = [
426
- 'set -eu',
427
- `root=${shellQuote(environmentArg.macosTempRoot)}`,
428
- `directory=${shellQuote(remoteDirectoryArg)}`,
429
- `test "$directory" = "$root/smartdb-release.${gitCommitArg}.${ownerTokenArg}"`,
430
- 'mkdir -p "$root"',
431
- 'test "$(cd "$root" && pwd -P)" = "$root"',
432
- 'created=0',
433
- `cleanup_unowned() { if [ "$created" = 1 ]; then test ! -f "$directory/.smartdb-release-owner" || test "$(cat "$directory/.smartdb-release-owner")" = ${shellQuote(ownerTokenArg)} || return; rm -f "$directory/.smartdb-release-owner"; rmdir "$directory" 2>/dev/null || true; fi; }`,
434
- 'trap cleanup_unowned EXIT HUP INT TERM',
435
- 'mkdir -m 700 "$directory"',
436
- 'created=1',
437
- `printf '%s\\n' ${shellQuote(ownerTokenArg)} > "$directory/.smartdb-release-owner"`,
438
- 'trap - EXIT HUP INT TERM',
439
- ].join('\n');
440
- await runCommand(
441
- 'ssh',
442
- [...sshConnectionArgs, environmentArg.macosSsh, createScript],
443
- process.cwd(),
444
- capabilityCommandTimeoutMs,
445
- );
446
- };
447
-
448
- const cleanupRemoteWorkspace = async (
449
- environmentArg: IReleaseArtifactEnvironment,
450
- remoteDirectoryArg: string,
451
- gitCommitArg: string,
452
- ownerTokenArg: string,
453
- ): Promise<void> => {
454
- assertOwnedRemoteWorkspacePath(
455
- environmentArg,
456
- remoteDirectoryArg,
457
- gitCommitArg,
458
- ownerTokenArg,
459
- );
460
- const cleanupScript = [
461
- 'set -eu',
462
- `root=${shellQuote(environmentArg.macosTempRoot)}`,
463
- `directory=${shellQuote(remoteDirectoryArg)}`,
464
- `test "$directory" = "$root/smartdb-release.${gitCommitArg}.${ownerTokenArg}"`,
465
- 'test "$(cd "$root" && pwd -P)" = "$root"',
466
- 'test -f "$directory/.smartdb-release-owner"',
467
- `test "$(cat "$directory/.smartdb-release-owner")" = ${shellQuote(ownerTokenArg)}`,
468
- 'rm -rf "$directory"',
469
- ].join('\n');
470
- await runCommand('ssh', [...sshConnectionArgs, environmentArg.macosSsh, cleanupScript]);
471
- };
472
-
473
- const buildArtifacts = async (
474
- environmentArg: IReleaseArtifactEnvironment,
475
- ): Promise<void> => {
476
- await checkBuilders(environmentArg);
477
-
478
- const workspace = process.cwd();
479
- const branch = await captureCommand('git', ['branch', '--show-current'], workspace);
480
- const gitCommit = await captureCommand('git', ['rev-parse', 'HEAD'], workspace);
481
- const gitStatus = await captureCommand(
482
- 'git',
483
- ['status', '--porcelain=v1', '--untracked-files=all'],
484
- workspace,
485
- );
486
- const packageJson = JSON.parse(
487
- fs.readFileSync(path.join(workspace, 'package.json'), 'utf8'),
488
- ) as { version?: string };
489
- const expectedTag = `v${packageJson.version || ''}`;
490
- const tagsAtHead = (
491
- await captureCommand('git', ['tag', '--points-at', 'HEAD'], workspace)
492
- ).split(/\r?\n/);
493
- if (branch !== 'main') throw new Error('SmartDB releases must be built from main');
494
- if (gitStatus) throw new Error('SmartDB release artifact build requires a clean worktree');
495
- if (!packageJson.version || !tagsAtHead.includes(expectedTag)) {
496
- throw new Error(`SmartDB release artifact build requires ${expectedTag} at HEAD`);
497
- }
498
-
499
- const releaseParent = path.join(workspace, '.nogit', 'smartdb-release');
500
- fs.mkdirSync(releaseParent, { recursive: true, mode: 0o700 });
501
- const localDirectory = fs.mkdtempSync(
502
- path.join(releaseParent, `${gitCommit.slice(0, 12)}-`),
503
- );
504
- const linuxDirectory = path.join(localDirectory, 'linux');
505
- const macosDirectory = path.join(localDirectory, 'macos');
506
- const bundlePath = path.join(localDirectory, 'source.bundle');
507
- const ownerToken = crypto.randomUUID();
508
- let remoteDirectory: string | undefined;
509
- let completed = false;
510
-
511
- try {
512
- await runCommand('pnpm', ['exec', 'tsbundle'], workspace);
513
- await runCommand('pnpm', ['exec', 'tsbuild', 'tsfolders'], workspace);
514
- await runCommand('pnpm', ['exec', 'tsrust'], workspace);
515
-
516
- fs.cpSync(path.join(workspace, 'dist_rust'), linuxDirectory, {
517
- recursive: true,
518
- preserveTimestamps: true,
519
- });
520
- assertArtifactSet(linuxDirectory, ['linux_amd64', 'linux_arm64']);
521
- await runCommand(path.join(linuxDirectory, 'rustdb_linux_amd64'), ['--version'], workspace);
522
- await runCommand(
523
- path.join(linuxDirectory, 'rustdb_linux_arm64'),
524
- ['--version'],
525
- workspace,
526
- );
527
-
528
- await runCommand('git', ['bundle', 'create', bundlePath, 'HEAD'], workspace);
529
- remoteDirectory = remoteWorkspacePath(environmentArg, gitCommit, ownerToken);
530
- await createRemoteWorkspace(
531
- environmentArg,
532
- remoteDirectory,
533
- gitCommit,
534
- ownerToken,
535
- );
536
- await runCommand('scp', [
537
- '-p',
538
- ...sshConnectionArgs,
539
- bundlePath,
540
- `${environmentArg.macosSsh}:${remoteDirectory}/source.bundle`,
541
- ]);
542
-
543
- const remoteWorkspace = `${remoteDirectory}/workspace`;
544
- const remoteBuildScript = [
545
- 'set -eu',
546
- `bundle=${shellQuote(`${remoteDirectory}/source.bundle`)}`,
547
- `workspace=${shellQuote(remoteWorkspace)}`,
548
- `expected_commit=${shellQuote(gitCommit)}`,
549
- 'git clone --quiet "$bundle" "$workspace"',
550
- 'cd "$workspace"',
551
- 'test "$(git rev-parse HEAD)" = "$expected_commit"',
552
- 'test -z "$(git status --porcelain=v1 --untracked-files=all)"',
553
- 'pnpm install --frozen-lockfile',
554
- 'pnpm exec tsrust',
555
- 'cmp .nogit/tsrust-target/aarch64-apple-darwin/release/rustdb dist_rust/rustdb_macos_arm64',
556
- 'cmp .nogit/tsrust-target/x86_64-apple-darwin/release/rustdb dist_rust/rustdb_macos_amd64',
557
- 'codesign --verify --deep --strict dist_rust/rustdb_macos_arm64',
558
- 'codesign --verify --deep --strict dist_rust/rustdb_macos_amd64',
559
- '/usr/bin/arch -arm64 dist_rust/rustdb_macos_arm64 --version',
560
- '/usr/bin/arch -x86_64 dist_rust/rustdb_macos_amd64 --version',
561
- 'test "$(git rev-parse HEAD)" = "$expected_commit"',
562
- 'test -z "$(git status --porcelain=v1 --untracked-files=all)"',
563
- ].join('\n');
564
- await runRemoteLoginScript(environmentArg, remoteBuildScript);
565
- await runCommand('scp', [
566
- '-pr',
567
- ...sshConnectionArgs,
568
- `${environmentArg.macosSsh}:${remoteWorkspace}/dist_rust`,
569
- macosDirectory,
570
- ]);
571
- assertArtifactSet(macosDirectory, ['macos_amd64', 'macos_arm64']);
572
-
573
- await runCommand(
574
- 'pnpm',
575
- ['exec', 'tsrust', 'assemble', linuxDirectory, macosDirectory],
576
- workspace,
577
- );
578
- assertArtifactSet(path.join(workspace, 'dist_rust'), [
579
- 'linux_amd64',
580
- 'linux_arm64',
581
- 'macos_amd64',
582
- 'macos_arm64',
583
- ]);
584
- for (const target of ['linux_amd64', 'linux_arm64', 'macos_amd64', 'macos_arm64']) {
585
- await runCommand(
586
- 'pnpm',
587
- ['exec', 'tsrust', 'inspect', `dist_rust/rustdb_${target}`],
588
- workspace,
589
- );
590
- }
591
-
592
- await cleanupRemoteWorkspace(
593
- environmentArg,
594
- remoteDirectory,
595
- gitCommit,
596
- ownerToken,
597
- );
598
- remoteDirectory = undefined;
599
- fs.rmSync(localDirectory, { recursive: true });
600
- completed = true;
601
- console.log(`Assembled and qualified SmartDB release artifacts for ${expectedTag}.`);
602
- } finally {
603
- if (!completed) {
604
- console.error(`Retained local release diagnostics at ${localDirectory}`);
605
- if (remoteDirectory) {
606
- console.error(`Retained macOS release diagnostics at ${remoteDirectory}`);
607
- }
608
- }
609
- }
610
- };
611
-
612
- try {
613
- const mode = process.argv.find((argumentArg) =>
614
- ['check', 'build'].includes(argumentArg),
615
- ) as TReleaseArtifactMode | undefined;
616
- if (!mode) throw new Error('Usage: release-artifacts.ts <check|build>');
617
- const environment = loadEnvironment();
618
- if (mode === 'check') {
619
- await checkBuilders(environment);
620
- } else {
621
- await buildArtifacts(environment);
622
- }
623
- } catch (error) {
624
- console.error(error);
625
- process.exitCode = 1;
626
- }