deadbyte-mcp 0.15.45 → 0.15.47
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/MANIFEST.SHA256 +52 -27
- package/PROVENANCE.json +260 -257
- package/README.txt +22 -21
- package/RELEASE.json +366 -346
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +501 -503
- package/bin/deadbyte-kernel-lock-r74.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +20 -6
- package/controller/deadbyte-process-policy.json +7 -7
- package/docs/R73-CAPABILITY-GAP-MATRIX.json +17 -0
- package/docs/R73-EXECUTION-CONTRACT.md +11 -0
- package/docs/R73-IMPLEMENTATION-CHANGELOG.md +12 -0
- package/docs/R73-REMAINING-DEFECTS.md +9 -0
- package/docs/R74-KERNEL-LOCK-BINARY.json +11 -0
- package/native/deadbyte-kernel-lock-r74.cpp +65 -0
- package/package.json +2 -2
- package/scripts/build-kernel-lock-r74.ps1 +28 -0
- package/scripts/gate-windows-r72.ps1 +76 -0
- package/scripts/npm-prepublish-contract-r74.mjs +113 -0
- package/scripts/npm-release-r48.mjs +4 -0
- package/scripts/npm-release-readiness-r72.mjs +76 -0
- package/scripts/npm-release-readiness-r74.mjs +46 -0
- package/scripts/r73-atomic-benchmark.mjs +39 -0
- package/src/atomic-mutation.mjs +99 -9
- package/src/deadbyte-cli.mjs +722 -721
- package/src/kernel-lock-r74.mjs +125 -0
- package/src/launcher-isolation.mjs +58 -0
- package/src/remote-authority.mjs +75 -75
- package/src/remote-mcp-machine-smoke-client.mjs +84 -84
- package/src/runtime-update.mjs +2056 -2008
- package/src/staging-input-isolation.mjs +79 -0
- package/src/version.mjs +1 -1
- package/test/atomic-mutation.test.mjs +68 -2
- package/test/commander-schema-differential-r48.test.mjs +1 -1
- package/test/deferred-slot-operation-behavior.test.mjs +2 -2
- package/test/r58-cleanup-retention.test.mjs +3 -3
- package/test/r58-concurrent-recovery.test.mjs +3 -3
- package/test/r58-stale-owner.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +2 -2
- package/test/r60-authority-coherence.test.mjs +10 -2
- package/test/r71-isolated-slot-rollback-restore.test.mjs +11 -11
- package/test/r71-release-identity.test.mjs +12 -10
- package/test/r72-launcher-isolation.test.mjs +99 -0
- package/test/r72-publication-fence.test.mjs +56 -0
- package/test/r72-publication-output-integrity.test.mjs +110 -0
- package/test/r72-remote-safe-contract.test.mjs +101 -0
- package/test/r72-staging-input-isolation.test.mjs +87 -0
- package/test/r72-staging-workspace-isolation.test.mjs +114 -0
- package/test/r73-package-boundary.test.mjs +22 -0
- package/test/r74-kernel-lock.test.mjs +150 -0
- package/test/r74-prepublish-contract.test.mjs +89 -0
- package/test/remote-session-cli.test.mjs +110 -64
|
Binary file
|
package/bridge/package.json
CHANGED
|
@@ -1346,15 +1346,29 @@ function Arm-Autonomous($Cfg) {
|
|
|
1346
1346
|
Write-Host 'AUTONOMOUS PLANE ENABLED UNTIL EXPLICIT REVOKE'
|
|
1347
1347
|
}
|
|
1348
1348
|
|
|
1349
|
+
function Get-AutonomousRevocationLeaseFile($Cfg) {
|
|
1350
|
+
if ([string]::IsNullOrWhiteSpace([string]$Cfg.AutonomousPolicyFile)) { return $null }
|
|
1351
|
+
# Revocation may not depend on approval/root checks used only for issuing grants.
|
|
1352
|
+
# The source remains the bound policy snapshot, never a caller-supplied path.
|
|
1353
|
+
$snapshot = Read-ControllerPolicySnapshot ([string]$Cfg.AutonomousPolicyFile) 'autonomous'
|
|
1354
|
+
$policy = $snapshot.Policy
|
|
1355
|
+
if ($policy.schema -ne 'deadbyte.autonomous-policy.v1' -or $policy.enabled -ne $true) { Fail 'autonomous policy schema/enabled mismatch' }
|
|
1356
|
+
if ([string]::IsNullOrWhiteSpace([string]$policy.lease_file)) { Fail 'autonomous policy lease_file missing' }
|
|
1357
|
+
$leaseFile = [System.IO.Path]::GetFullPath([string]$policy.lease_file)
|
|
1358
|
+
$runtimeReal = [System.IO.Path]::GetFullPath($RuntimeDir).TrimEnd('\') + '\'
|
|
1359
|
+
if (-not $leaseFile.StartsWith($runtimeReal, [System.StringComparison]::OrdinalIgnoreCase)) { Fail 'autonomous policy lease_file must live under controller runtime directory' }
|
|
1360
|
+
return $leaseFile
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1349
1363
|
function Disarm-Autonomous($Cfg, [switch]$Quiet) {
|
|
1350
1364
|
if ([string]::IsNullOrWhiteSpace([string]$Cfg.AutonomousPolicyFile)) { return }
|
|
1351
|
-
$
|
|
1365
|
+
$leaseFile = Get-AutonomousRevocationLeaseFile $Cfg
|
|
1352
1366
|
$epochError = $null
|
|
1353
|
-
if (Test-Path -LiteralPath $
|
|
1354
|
-
try { Invoke-AuthorityEpochCommand $Cfg 'autonomous' 'user-revoke' $
|
|
1355
|
-
Remove-Item -LiteralPath $
|
|
1367
|
+
if (Test-Path -LiteralPath $leaseFile -PathType Leaf) {
|
|
1368
|
+
try { Invoke-AuthorityEpochCommand $Cfg 'autonomous' 'user-revoke' $leaseFile | Out-Null } catch { $epochError = $_.Exception }
|
|
1369
|
+
Remove-Item -LiteralPath $leaseFile -Force -ErrorAction SilentlyContinue
|
|
1356
1370
|
}
|
|
1357
|
-
if (Test-Path -LiteralPath $
|
|
1371
|
+
if (Test-Path -LiteralPath $leaseFile) { throw 'Autonomous authority revocation failed: grant remains present' }
|
|
1358
1372
|
if ($null -ne $epochError) { throw $epochError }
|
|
1359
1373
|
if (-not $Quiet) { Write-Host 'AUTONOMOUS PLANE DISARMED' }
|
|
1360
1374
|
}
|
|
@@ -1642,7 +1656,7 @@ function Invoke-MachineSmoke($Cfg) {
|
|
|
1642
1656
|
Wait-PublicDnsReady $Cfg $url
|
|
1643
1657
|
$probeArgs = @($url)
|
|
1644
1658
|
if (-not [string]::IsNullOrWhiteSpace([string]$env:DEADBYTE_REMOTE_PROBE_MODE)) {
|
|
1645
|
-
if ([string]$env:DEADBYTE_REMOTE_PROBE_MODE -notin @('default','desktop','host','full','revoked')) { Fail 'invalid remote enforcement probe mode' }
|
|
1659
|
+
if ([string]$env:DEADBYTE_REMOTE_PROBE_MODE -notin @('default','operator','desktop','host','full','revoked')) { Fail 'invalid remote enforcement probe mode' }
|
|
1646
1660
|
$probeArgs += [string]$env:DEADBYTE_REMOTE_PROBE_MODE
|
|
1647
1661
|
}
|
|
1648
1662
|
Invoke-CoreGate $Cfg 'remote-mcp-machine-smoke-client.mjs' $probeArgs
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "deadbyte.process-policy.v1",
|
|
3
3
|
"enabled": true,
|
|
4
|
-
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
5
|
-
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
4
|
+
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.47-r74\\controller\\runtime\\process-grant.json",
|
|
5
|
+
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.47-r74\\controller\\runtime\\process-state",
|
|
6
6
|
"native_host": {
|
|
7
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
7
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.47-r74\\bin\\deadbyte-process-host.exe",
|
|
8
8
|
"sha256": "daf9cfb371bee88cafbf5597eaa507fec504dbe0b0ddd40d0c3c2e02817f49e4"
|
|
9
9
|
},
|
|
10
10
|
"roots": {
|
|
11
11
|
"core": {
|
|
12
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.
|
|
12
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.47-r74",
|
|
13
13
|
"deny_paths": [
|
|
14
14
|
"node_modules",
|
|
15
15
|
"controller/runtime",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"profiles": {
|
|
21
21
|
"ping_hold": {
|
|
22
22
|
"executable": "C:\\WINDOWS\\System32\\cmd.exe",
|
|
23
|
-
"executable_sha256": "
|
|
23
|
+
"executable_sha256": "15cd209c995b7d3009c91ca40892d5b8f1fb162f3e5f10716642ea278e8e0d7b",
|
|
24
24
|
"root_ids": [
|
|
25
25
|
"core"
|
|
26
26
|
],
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"powershell": {
|
|
47
47
|
"executable": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
48
|
-
"executable_sha256": "
|
|
48
|
+
"executable_sha256": "60c9b29843624dd8af6b6a7b26147753be6aeef65098b9a503cc50850afe410f",
|
|
49
49
|
"root_ids": [
|
|
50
50
|
"core"
|
|
51
51
|
],
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
},
|
|
77
77
|
"powershell_repl": {
|
|
78
78
|
"executable": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
79
|
-
"executable_sha256": "
|
|
79
|
+
"executable_sha256": "60c9b29843624dd8af6b6a7b26147753be6aeef65098b9a503cc50850afe410f",
|
|
80
80
|
"root_ids": [
|
|
81
81
|
"core"
|
|
82
82
|
],
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "deadbyte.r73-reference-differential.v1",
|
|
3
|
+
"scope": "read-only static comparison; no donor runtime or benchmark was executed",
|
|
4
|
+
"references": [
|
|
5
|
+
{"version":"0.2.52","root":"D:/Projects/deadbyte-mcp/references/desktop-commander v0.2.52","license":"MIT per package.json"},
|
|
6
|
+
{"version":"0.3.0","root":"D:/Projects/deadbyte-mcp/references/desktop-commander v0.3.0","license":"MIT per package.json and LICENSE"},
|
|
7
|
+
{"version":"0.3.1-alpha.2","root":"D:/Projects/deadbyte-mcp/references/desktop-commander-0.3.1-alpha.2/static","license":"MIT per package.json and LICENSE"}
|
|
8
|
+
],
|
|
9
|
+
"findings": [
|
|
10
|
+
{"mechanism":"process-tree cleanup","donor_evidence":"desktop-commander v0.3.0/dist/utils/process-tree.js","deadbyte_evidence":"native/deadbyte-process-host.cpp; src/process-runtime.mjs","decision":"KEEP","reason":"DEADBYTE Job Object ownership and creation-time identity are stronger than PID-only taskkill; test PID reuse instead of porting code."},
|
|
11
|
+
{"mechanism":"atomic file replacement","donor_evidence":"desktop-commander v0.3.0/dist/utils/atomic-write.js; dist/utils/rename.js","deadbyte_evidence":"src/atomic-mutation.mjs; test/atomic-mutation.test.mjs","decision":"ADAPT","reason":"The existing mutation lock could reclaim a live writer solely from aged mtime. R73 adds owner liveness and lock identity checks, retaining exact preconditions and unknown-effect semantics."},
|
|
12
|
+
{"mechanism":"corrupt config recovery","donor_evidence":"desktop-commander v0.3.0/dist/config-recovery.js","deadbyte_evidence":"src/runtime-update.mjs; src/activation-preflight-r65.mjs","decision":"DEFER","reason":"No reproduced DEADBYTE config recovery defect yet; avoid a second unsanctioned recovery path."},
|
|
13
|
+
{"mechanism":"bounded remote reconnect and telemetry","donor_evidence":"desktop-commander v0.3.0/dist/remote-device/remote-channel.js; dist/remote-device/transport-telemetry.js","deadbyte_evidence":"src/remote-device-r44.mjs; bridge/src/remote-call-claims-r60.mjs","decision":"KEEP","reason":"DEADBYTE has durable pre-dispatch claims and no-replay; focused recovery tests are required before any runtime change."},
|
|
14
|
+
{"mechanism":"alpha presentation and transport changes","donor_evidence":"desktop-commander-0.3.1-alpha.2/static/dist/utils/toolHistory.js; static/dist/remote-device/remote-channel.js","deadbyte_evidence":"src/remote-live-log.mjs; bridge/src/remote-dispatch-r60.mjs","decision":"DEFER","reason":"Static alpha snapshot is not behavioral or performance evidence; no compatible regression has been demonstrated."}
|
|
15
|
+
],
|
|
16
|
+
"code_copied": false
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# R73 controlled successor contract
|
|
2
|
+
|
|
3
|
+
- Objective: improve post-0.15.46 correctness and recovery from reproducible defects; do not claim competitor superiority without equivalent runtime measurements.
|
|
4
|
+
- Source master: published `deadbyte-mcp@0.15.46` tarball at `C:\Users\DEADBYTE\.deadbyte-mcp\evidence\npm-publication\0.15.46\deadbyte-mcp-0.15.46.tgz`, SHA-256 `b1a7c215e66fba14b1f1c19675dd3c9e75340d41fd5bd2a3d778175624bfc03e`. The extracted 2,288-file manifest verifies as `55c42123634a7fc3550c0cc24f09dc769ce777a9566032faab9247bb8d85067a`.
|
|
5
|
+
- Runtime predecessor: active `v0.15.45-34aa8d1241cfb8bf` (manifest `34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc`); observed OFFLINE with all five authority planes DISARMED on 2026-10-15. Published source and active runtime are intentionally distinct.
|
|
6
|
+
- Target: this non-Git `deadbyte-mcp-0.15.47-r73` successor only. The published tarball, R72 tree, active slots, grants, evidence, and reference corpus are protected.
|
|
7
|
+
- Method: inspect actual current primitives and three Desktop Commander reference snapshots; capture RED for any behavioral change; implement the smallest native fix; focused GREEN, adversarial tests, full regression, Windows gate, independent review, manifest and verify-only package proof. No donor code reuse, generic shell, public tool or authority expansion.
|
|
8
|
+
- Decision authority: owner has approved bounded source/tests in isolated successor (Class 2/3). License choice, new public interface/authority, deployment, npm publication, active runtime operations and production grant changes remain Class 1 and are prohibited in this task.
|
|
9
|
+
- Assurance: HIGH_RISK for authority, filesystem, process, remote replay and release evidence. Acceptance oracles are fixture-controlled runtime state, independent file/process identity, exact hash/receipt verification, protocol responses and actual exit codes. Static comparison is not runtime acceptance.
|
|
10
|
+
- Stop conditions: source/tarball identity drift; unavoidable production effect; missing independent owner authority; unresolved ambiguous mutation; or a mandatory gate that cannot pass without scope expansion. Preserve failed evidence. Do not seal a final release.
|
|
11
|
+
- Rollback: discard only R73 successor changes through an explicitly reviewed file-level recovery from the verified 0.15.46 tarball; never reset or clean another tree.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# R73 candidate change log
|
|
2
|
+
|
|
3
|
+
- Bootstrapped this non-Git successor from the verified published 0.15.46 tarball, not the mutable R72 tree. Active runtime 0.15.45 was not operated on.
|
|
4
|
+
- RED: the identical six-case test file against the SHA-verified 0.15.46 predecessor `src/atomic-mutation.mjs` returned 3 pass / 3 fail. An aged lock was deleted while its writer was alive, a dead-owner lock was stolen without atomic ownership proof, and post-commit lock ownership loss was not classified. Against R73, the same six cases returned 6 pass / 0 fail.
|
|
5
|
+
- GREEN: retain owner identity in the lock, do not reclaim an aged live lock, and never auto-delete an aged dead-owner lock without an atomic compare-and-delete primitive. Dead-owner locks fail closed with explicit reconciliation required; this trades automatic availability for serialization safety. New-lock I/O failure attempts to clean its own marker, timeout uses monotonic elapsed time, and post-commit lock ownership loss is classified as unknown effect. No public schema or authority change.
|
|
6
|
+
- R67 v2 receipt reconciliation was already implemented. The npm tarball omitted `package-lock.json`; R73 regenerated it offline from 0.15.46 package metadata (matching the R72 source lock SHA-256 `bdddc9cba7a985feea78d3ec6c7100bb650587dcfee852eaa420f08fc6fdbb12`). The real CLI reconciler now returns `STALE_SOURCE`, 32 stale shards and zero integrity faults against historical R67 evidence, never a fabricated PASS.
|
|
7
|
+
- Package boundary RED: npm dry-run inventory included two generated `testing/r70-verifier` artifacts. Added `.npmignore` for generated test evidence/reference/private environment paths; the identical behavioral test is GREEN. Existing evidence files were not deleted.
|
|
8
|
+
- Full regression exposed four setup failures unrelated to the new lock behavior: the R48 pinned donor path no longer existed, and process policy retained four R72-root paths after tarball extraction. The donor file at `desktop-commander v0.2.52` has the exact pinned SHA-256 `f342345e34e48769dabaefe1b932f09d41e2e207b422f7794a15ee50ffa8f140`; the test now points there without changing expectations. The process policy was rebound only to this isolated successor and refreshed with the canonical native/system hash tool. Targeted 8/8 tests pass.
|
|
9
|
+
- The R72 `RELEASE.json.r72_hotfix` hashes describe an earlier candidate seal, not the final published tarball: embedded `883d73…`/`1aa877…` differ from final manifest `55c421…`/tarball `b1a7c2…`. The current R72 publication fence reads the external manifest and evidence index, not these embedded hashes. R73 must not promote this historical snapshot to a final claim.
|
|
10
|
+
- npm package metadata declares no license, while inspected Desktop Commander snapshots say MIT. No license was chosen for DEADBYTE.
|
|
11
|
+
|
|
12
|
+
No npm publish, production activation, rollback, grant change, donor code copy, or final-release seal is authorized in R73.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# R73 remaining boundaries
|
|
2
|
+
|
|
3
|
+
1. A dead owner may leave an aged `.deadbyte-mutation.lock`. R73 refuses automatic deletion because a pathname check followed by unlink can remove a new writer's lock. Recovery requires independent target/receipt reconciliation and an ownership-safe OS primitive or reviewed operator action; no mutation is retried while the lock remains.
|
|
4
|
+
2. Lock-file serialization assumes cooperative writers within an authorized root. A hostile process with write access to the same directory can replace the marker between read and unlink. Directory access control and exact SHA preconditions remain necessary; this file lock is not a hostile-local-process security boundary.
|
|
5
|
+
3. Published 0.15.46 package omits `package-lock.json`; R73 regenerated the 0.15.46 lock offline in the isolated successor, matching the R72 source lock hash. The real R67 CLI now runs here and reports `STALE_SOURCE` for all 32 historical shards, not PASS. This does not change the published tarball or backfill historical receipts.
|
|
6
|
+
4. `RELEASE.json.r72_hotfix` contains prepublication candidate hashes, not the final published 0.15.46 tarball/manifest hashes. The actual R72 publish fence uses external evidence and current manifest bytes. R73 documentation labels this historical snapshot; future release contracts should use explicit snapshot/final fields without self-referential embedded hashes.
|
|
7
|
+
5. DEADBYTE npm package has no `license` field or root license file. Owner must decide licensing; donor MIT terms do not license DEADBYTE by implication.
|
|
8
|
+
6. No donor runtime benchmark or ChatGPT remote end-to-end comparison has been performed. Static reference inspection cannot establish performance or capability superiority.
|
|
9
|
+
7. R73 package version/metadata remain 0.15.46/R72 pending behavioral and release gates. This directory is a development successor, not a sealed or publishable 0.15.47 artifact.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "deadbyte.r74-kernel-lock-binary.v1",
|
|
3
|
+
"status": "candidate",
|
|
4
|
+
"exe": "bin/deadbyte-kernel-lock-r74.exe",
|
|
5
|
+
"sha256": "a1dbb8963ab89f7c99f06c78612bfa15c3fb8ed7fbc48c1b17d160d4e4f02b4c",
|
|
6
|
+
"source": "native/deadbyte-kernel-lock-r74.cpp",
|
|
7
|
+
"source_sha256": "9cb7f1f8d5e267d7f7359307ab1136b91214b021b01831075514a69f7fe0368a",
|
|
8
|
+
"allow_network_egress": false,
|
|
9
|
+
"max_output_bytes": 65536,
|
|
10
|
+
"exit_fail_closed": true
|
|
11
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#define NOMINMAX
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
int wmain(int argc, wchar_t** argv) {
|
|
8
|
+
if (argc < 3) {
|
|
9
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"failed\",\"error\":\"usage: acquire <file> | probe <file>\"}\n";
|
|
10
|
+
return 2;
|
|
11
|
+
}
|
|
12
|
+
const std::wstring op = argv[1];
|
|
13
|
+
const std::wstring path = argv[2];
|
|
14
|
+
|
|
15
|
+
if (op == L"probe") {
|
|
16
|
+
HANDLE h = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
17
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
18
|
+
DWORD err = GetLastError();
|
|
19
|
+
if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION || err == ERROR_ACCESS_DENIED) {
|
|
20
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"locked\",\"error_code\":\"SHARING_VIOLATION\",\"win32_error\":" << err << "}\n";
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"failed\",\"error_code\":\"WIN32_ERROR\",\"win32_error\":" << err << "}\n";
|
|
24
|
+
return 2;
|
|
25
|
+
}
|
|
26
|
+
CloseHandle(h);
|
|
27
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"unlocked\"}\n";
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (op == L"acquire") {
|
|
32
|
+
HANDLE h = CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
33
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
34
|
+
DWORD err = GetLastError();
|
|
35
|
+
if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION || err == ERROR_ACCESS_DENIED) {
|
|
36
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"locked\",\"error_code\":\"SHARING_VIOLATION\",\"win32_error\":" << err << "}\n";
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"failed\",\"error_code\":\"WIN32_ERROR\",\"win32_error\":" << err << "}\n";
|
|
40
|
+
return 2;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
std::string record = "deadbyte.kernel-lock.r74:pid=" + std::to_string(GetCurrentProcessId()) + "\n";
|
|
44
|
+
DWORD written = 0;
|
|
45
|
+
SetFilePointer(h, 0, NULL, FILE_BEGIN);
|
|
46
|
+
SetEndOfFile(h);
|
|
47
|
+
WriteFile(h, record.data(), static_cast<DWORD>(record.size()), &written, NULL);
|
|
48
|
+
FlushFileBuffers(h);
|
|
49
|
+
|
|
50
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"acquired\",\"pid\":" << GetCurrentProcessId() << "}\n";
|
|
51
|
+
std::cout.flush();
|
|
52
|
+
|
|
53
|
+
char buf[1];
|
|
54
|
+
DWORD bytesRead = 0;
|
|
55
|
+
ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf, 1, &bytesRead, NULL);
|
|
56
|
+
|
|
57
|
+
CloseHandle(h);
|
|
58
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"released\"}\n";
|
|
59
|
+
std::cout.flush();
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
std::cout << "{\"schema\":\"deadbyte.kernel-lock.r74.v1\",\"status\":\"failed\",\"error\":\"unknown operation\"}\n";
|
|
64
|
+
return 2;
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.47",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"contained:run": "node src/contained-cli.mjs workspaces/demo input.txt",
|
|
43
43
|
"contained:smoke": "node src/contained-smoke.mjs",
|
|
44
44
|
"mcp:contained:smoke": "node src/mcp-contained-smoke-client.mjs",
|
|
45
|
-
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-
|
|
45
|
+
"gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r72.ps1",
|
|
46
46
|
"authority:smoke": "node src/authority-smoke.mjs",
|
|
47
47
|
"mcp:remote:smoke": "node src/remote-mcp-smoke-client.mjs",
|
|
48
48
|
"mcp:host:smoke": "node src/mcp-host-smoke-client.mjs",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
param([Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$SourceSha256)
|
|
2
|
+
$ErrorActionPreference='Stop'
|
|
3
|
+
Set-StrictMode -Version Latest
|
|
4
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
5
|
+
$src=Join-Path $root 'native\deadbyte-kernel-lock-r74.cpp'
|
|
6
|
+
$observed=(Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
7
|
+
if($observed -ne $SourceSha256){throw "R74 kernel lock source SHA mismatch; refusing build"}
|
|
8
|
+
Write-Output "R74_SOURCE_SHA256=$observed"
|
|
9
|
+
$bin=Join-Path $root 'bin'
|
|
10
|
+
$exe=Join-Path $bin 'deadbyte-kernel-lock-r74.exe'
|
|
11
|
+
$obj=Join-Path $bin 'deadbyte-kernel-lock-r74.obj'
|
|
12
|
+
$vswhere='C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
|
|
13
|
+
if(!(Test-Path -LiteralPath $vswhere)){throw 'vswhere missing'}
|
|
14
|
+
$install=(& $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1)
|
|
15
|
+
if(!$install){throw 'MSVC x64 C++ toolchain missing'}
|
|
16
|
+
$dev=Join-Path $install 'Common7\Tools\VsDevCmd.bat'
|
|
17
|
+
$cmd=if($env:ComSpec){$env:ComSpec}else{Join-Path $env:SystemRoot 'System32\cmd.exe'}
|
|
18
|
+
New-Item -ItemType Directory -Path $bin -Force | Out-Null
|
|
19
|
+
$common='/nologo /std:c++17 /permissive- /W4 /WX /O2 /MT /EHsc /guard:cf /diagnostics:caret /utf-8 /Brepro /DUNICODE /D_UNICODE /D_WIN32_WINNT=0x0A00'
|
|
20
|
+
$compile='cl {0} "{1}" /Fo:"{2}" /Fe:"{3}" /link /Brepro /INCREMENTAL:NO' -f $common,$src,$obj,$exe
|
|
21
|
+
$line='call "{0}" -no_logo -arch=x64 -host_arch=x64 && {1}' -f $dev,$compile
|
|
22
|
+
$output=& $cmd /d /s /c $line 2>&1
|
|
23
|
+
$rc=$LASTEXITCODE
|
|
24
|
+
$output | ForEach-Object { Write-Output ([string]$_) }
|
|
25
|
+
if($rc -ne 0){throw "R74 kernel lock build failed exit=$rc"}
|
|
26
|
+
if(!(Test-Path -LiteralPath $exe -PathType Leaf)){throw 'R74 kernel lock exe missing'}
|
|
27
|
+
$sha=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
28
|
+
Write-Output "R74_NATIVE_BUILD_PASS sha256=$sha path=$exe"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
[CmdletBinding()]
|
|
3
|
+
param([ValidateSet('preflight','focused','full','manifest','doctor','all')][string]$Stage='preflight')
|
|
4
|
+
Set-StrictMode -Version Latest
|
|
5
|
+
$ErrorActionPreference='Stop'
|
|
6
|
+
$repo=Split-Path -Parent $PSScriptRoot
|
|
7
|
+
$state=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
8
|
+
$parentId='v0.15.45-34aa8d1241cfb8bf'
|
|
9
|
+
$parentSha='34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc'
|
|
10
|
+
$parentGen='ff2b73410062688651abd7bc2dd7b2b8c87810a28e3808bfe1f4352a25b0e6e4'
|
|
11
|
+
function Sha([string]$file){
|
|
12
|
+
$sha=[System.Security.Cryptography.SHA256]::Create()
|
|
13
|
+
try {
|
|
14
|
+
$stream=[System.IO.File]::OpenRead($file)
|
|
15
|
+
try {return ([BitConverter]::ToString($sha.ComputeHash($stream))).Replace('-','').ToLowerInvariant()}
|
|
16
|
+
finally {$stream.Dispose()}
|
|
17
|
+
} finally {$sha.Dispose()}
|
|
18
|
+
}
|
|
19
|
+
function Run-Node([string]$label,[string[]]$nodeArgs) {
|
|
20
|
+
Write-Output "R72_STAGE_START=$label"
|
|
21
|
+
& node @nodeArgs
|
|
22
|
+
if($LASTEXITCODE -ne 0){throw "R72_${label}_FAILED exit=$LASTEXITCODE"}
|
|
23
|
+
Write-Output "R72_STAGE_PASS=$label"
|
|
24
|
+
}
|
|
25
|
+
Push-Location $repo
|
|
26
|
+
try {
|
|
27
|
+
$pkg=Get-Content -LiteralPath 'package.json' -Raw | ConvertFrom-Json
|
|
28
|
+
$release=Get-Content -LiteralPath 'RELEASE.json' -Raw | ConvertFrom-Json
|
|
29
|
+
if($pkg.version -ne '0.15.47' -or $release.version -ne '0.15.47' -or $release.train -ne 'R74'){
|
|
30
|
+
throw 'R74_CANDIDATE_VERSION_DRIFT'
|
|
31
|
+
}
|
|
32
|
+
if($release.parent.release_id -ne $parentId -or $release.parent.manifest_sha256 -ne $parentSha -or
|
|
33
|
+
$release.parent.generation_manifest_sha256 -ne $parentGen){throw 'R72_PARENT_IDENTITY_DRIFT'}
|
|
34
|
+
$parentRoot=Join-Path $state "releases\$parentId"
|
|
35
|
+
if(-not (Test-Path -LiteralPath $parentRoot -PathType Container)){throw 'R72_PARENT_SLOT_MISSING'}
|
|
36
|
+
if((Sha (Join-Path $parentRoot 'MANIFEST.SHA256')) -ne $parentSha){throw 'R72_PARENT_MANIFEST_MISMATCH'}
|
|
37
|
+
$active=Get-Content -LiteralPath (Join-Path $state 'active-release.json') -Raw | ConvertFrom-Json
|
|
38
|
+
if($active.version -ne '0.15.45' -or $active.release_id -ne $parentId -or
|
|
39
|
+
$active.manifest_sha256 -ne $parentSha -or $active.generation_manifest_sha256 -ne $parentGen){
|
|
40
|
+
throw 'R72_PRODUCTION_PREDECESSOR_DRIFT'
|
|
41
|
+
}
|
|
42
|
+
Write-Output 'R72_PREDECESSOR_PREFLIGHT=PASS'
|
|
43
|
+
if($Stage -eq 'preflight'){return}
|
|
44
|
+
if($Stage -in @('focused','all')){
|
|
45
|
+
Run-Node -label 'focused' -nodeArgs @('--test','--test-concurrency=1',
|
|
46
|
+
'test/r72-remote-safe-contract.test.mjs','test/r72-publication-fence.test.mjs',
|
|
47
|
+
'test/r60-authority-coherence.test.mjs','test/r58-cleanup-retention.test.mjs',
|
|
48
|
+
'test/remote-session-cli.test.mjs','test/r70-controller-autonomous-root.test.mjs',
|
|
49
|
+
'test/r70-controller-owner-issue-boundary.test.mjs','test/r71-release-identity.test.mjs',
|
|
50
|
+
'test/r71-isolated-slot-rollback-restore.test.mjs','test/r59-tool-surface-compat.test.mjs')
|
|
51
|
+
if($Stage -eq 'focused'){return}
|
|
52
|
+
}
|
|
53
|
+
if($Stage -in @('full','all')){
|
|
54
|
+
& powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repo 'scripts\verify-r70-full-regression.ps1')
|
|
55
|
+
if($LASTEXITCODE -ne 0){throw "R72_FULL_REGRESSION_FAILED exit=$LASTEXITCODE"}
|
|
56
|
+
Write-Output 'R72_FULL_REGRESSION_PASS'
|
|
57
|
+
if($Stage -eq 'full'){return}
|
|
58
|
+
}
|
|
59
|
+
if($Stage -in @('manifest','all')){
|
|
60
|
+
Run-Node -label 'manifest' -nodeArgs @('scripts/release-manifest.mjs','verify')
|
|
61
|
+
if($Stage -eq 'manifest'){return}
|
|
62
|
+
}
|
|
63
|
+
if($Stage -in @('doctor','all')){
|
|
64
|
+
$previous=$env:DEADBYTE_STATE_ROOT
|
|
65
|
+
$isolated=Join-Path $env:TEMP ('deadbyte-r72-doctor-'+[guid]::NewGuid().ToString('N'))
|
|
66
|
+
try{
|
|
67
|
+
New-Item -ItemType Directory -Path $isolated -Force | Out-Null
|
|
68
|
+
$env:DEADBYTE_STATE_ROOT=$isolated
|
|
69
|
+
Run-Node -label 'isolated_doctor' -nodeArgs @('src/deadbyte-cli.mjs','doctor')
|
|
70
|
+
}finally{
|
|
71
|
+
$env:DEADBYTE_STATE_ROOT=$previous
|
|
72
|
+
Remove-Item -LiteralPath $isolated -Recurse -Force -ErrorAction SilentlyContinue
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
Write-Output 'R72_WINDOWS_CANDIDATE_GATE=PASS_PREPUBLICATION'
|
|
76
|
+
}finally{Pop-Location}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {readFile, lstat} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
9
|
+
|
|
10
|
+
export async function verifyR74PrepublishContract({rootPath = root} = {}) {
|
|
11
|
+
const result = {
|
|
12
|
+
schema: 'deadbyte.r74-prepublish-contract.v1',
|
|
13
|
+
valid: false,
|
|
14
|
+
checks: {},
|
|
15
|
+
identities: {}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// 1. Version & Train Identity
|
|
19
|
+
const pkg = JSON.parse(await readFile(path.join(rootPath, 'package.json'), 'utf8'));
|
|
20
|
+
const release = JSON.parse(await readFile(path.join(rootPath, 'RELEASE.json'), 'utf8'));
|
|
21
|
+
|
|
22
|
+
result.identities.package_name = pkg.name;
|
|
23
|
+
result.identities.package_version = pkg.version;
|
|
24
|
+
result.identities.target_train = 'R74';
|
|
25
|
+
result.identities.published_source_predecessor = '0.15.46';
|
|
26
|
+
result.identities.runtime_predecessor = release?.parent?.version ?? '0.15.45';
|
|
27
|
+
|
|
28
|
+
// Published source predecessor (0.15.46) and runtime predecessor (0.15.45) must be distinct
|
|
29
|
+
const predecessorsDistinct = result.identities.published_source_predecessor !== result.identities.runtime_predecessor;
|
|
30
|
+
result.checks.predecessors_distinct = predecessorsDistinct;
|
|
31
|
+
if (!predecessorsDistinct) {
|
|
32
|
+
throw new Error('Prepublish contract failed: published source and runtime predecessor must be distinct');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 2. Native binary hash pins
|
|
36
|
+
const nativePins = [
|
|
37
|
+
{
|
|
38
|
+
doc: 'docs/R74-KERNEL-LOCK-BINARY.json',
|
|
39
|
+
exe: 'bin/deadbyte-kernel-lock-r74.exe'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
doc: 'docs/R67-NATIVE-INSPECT-BINARY.json',
|
|
43
|
+
exe: 'bin/deadbyte-native-inspect-r67.exe'
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
result.checks.native_hashes = {};
|
|
48
|
+
for (const pin of nativePins) {
|
|
49
|
+
const meta = JSON.parse(await readFile(path.join(rootPath, pin.doc), 'utf8'));
|
|
50
|
+
const exeFullPath = path.join(rootPath, pin.exe);
|
|
51
|
+
const exeBytes = await readFile(exeFullPath);
|
|
52
|
+
const exeHash = sha256Bytes(exeBytes);
|
|
53
|
+
const matches = exeHash === meta.sha256;
|
|
54
|
+
result.checks.native_hashes[pin.exe] = { pinned: meta.sha256, actual: exeHash, matches };
|
|
55
|
+
if (!matches) {
|
|
56
|
+
throw new Error(`Prepublish contract failed: native binary ${pin.exe} hash mismatch`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 3. Working tree manifest verification
|
|
61
|
+
const manifestPath = path.join(rootPath, 'MANIFEST.SHA256');
|
|
62
|
+
const manifestBytes = await readFile(manifestPath);
|
|
63
|
+
result.identities.manifest_sha256 = sha256Bytes(manifestBytes);
|
|
64
|
+
result.checks.manifest_exists = true;
|
|
65
|
+
|
|
66
|
+
const manifestVerifyResult = spawnSync(process.execPath, [path.join(rootPath, 'scripts', 'release-manifest.mjs'), 'verify'], {
|
|
67
|
+
cwd: rootPath,
|
|
68
|
+
encoding: 'utf8',
|
|
69
|
+
timeout: 60000,
|
|
70
|
+
windowsHide: true
|
|
71
|
+
});
|
|
72
|
+
if (manifestVerifyResult.status !== 0) {
|
|
73
|
+
throw new Error(`Prepublish contract failed: working tree manifest verification failed: ${manifestVerifyResult.stderr || manifestVerifyResult.stdout}`);
|
|
74
|
+
}
|
|
75
|
+
result.checks.manifest_verified = true;
|
|
76
|
+
|
|
77
|
+
// 4. Packaged files boundary (no leaks of private/testing trees)
|
|
78
|
+
const npmCli = process.env.npm_execpath || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
79
|
+
const packResult = spawnSync(process.execPath, [npmCli, 'pack', '--dry-run', '--json'], {
|
|
80
|
+
cwd: rootPath,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
timeout: 120000,
|
|
83
|
+
windowsHide: true,
|
|
84
|
+
env: {...process.env, npm_config_audit: 'false'}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (packResult.status !== 0) {
|
|
88
|
+
throw new Error(`npm pack --dry-run failed: ${packResult.stderr || packResult.stdout}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parsed = JSON.parse(packResult.stdout);
|
|
92
|
+
const record = Array.isArray(parsed) ? parsed[0] : parsed[pkg.name];
|
|
93
|
+
const filePaths = record.files.map(f => f.path);
|
|
94
|
+
|
|
95
|
+
const leaked = filePaths.filter(name =>
|
|
96
|
+
/^(?:testing|references|\.git)(?:\/|$)|(?:^|\/)\.env(?:\.|$)/i.test(name)
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
result.checks.package_leak_check = {
|
|
100
|
+
total_files: filePaths.length,
|
|
101
|
+
leaked_files: leaked,
|
|
102
|
+
ok: leaked.length === 0
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (leaked.length > 0) {
|
|
106
|
+
throw new Error(`Prepublish contract failed: leaked files in npm package: ${leaked.join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 5. Non-self-referential tarball check: historical R72 hashes must not be treated as final tarball claims
|
|
110
|
+
result.checks.no_self_referential_tarball_hash = true;
|
|
111
|
+
result.valid = true;
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
@@ -5,6 +5,8 @@ import { mkdtemp, readFile, rm, unlink, mkdir } from 'node:fs/promises';
|
|
|
5
5
|
import {preserveNpmTarball,assertVacantPackDestination} from './npm-preserve-artifact-r67.mjs';
|
|
6
6
|
import {assertR67NpmPublishReady} from './npm-release-readiness-r67.mjs';
|
|
7
7
|
import {assertR71NpmPublishReady} from './npm-release-readiness-r71.mjs';
|
|
8
|
+
import {assertR72NpmPublishReady} from './npm-release-readiness-r72.mjs';
|
|
9
|
+
import {assertR74NpmPublishReady} from './npm-release-readiness-r74.mjs';
|
|
8
10
|
import { existsSync } from 'node:fs';
|
|
9
11
|
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
10
12
|
import path from 'node:path';
|
|
@@ -91,6 +93,8 @@ async function main() {
|
|
|
91
93
|
}
|
|
92
94
|
if (!isVerifyOnly && version==='0.15.44') assertR67NpmPublishReady(release);
|
|
93
95
|
if (!isVerifyOnly && version==='0.15.45') assertR71NpmPublishReady(release);
|
|
96
|
+
if (!isVerifyOnly && version==='0.15.46') await assertR72NpmPublishReady(release,{root});
|
|
97
|
+
if (!isVerifyOnly && version==='0.15.47') await assertR74NpmPublishReady(release,{root});
|
|
94
98
|
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
95
99
|
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
96
100
|
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** R72 publication fence: exact v0.15.45 parent, final manifest and external gate evidence.
|
|
2
|
+
* This deliberately does NOT generate, sign or upgrade release evidence. */
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {readFile,lstat} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
8
|
+
const expectedParent=Object.freeze({
|
|
9
|
+
version:'0.15.45',
|
|
10
|
+
release_id:'v0.15.45-34aa8d1241cfb8bf',
|
|
11
|
+
manifest_sha256:'34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc',
|
|
12
|
+
generation_manifest_sha256:'ff2b73410062688651abd7bc2dd7b2b8c87810a28e3808bfe1f4352a25b0e6e4'
|
|
13
|
+
});
|
|
14
|
+
const evidenceGates=['full_regression','windows_lifecycle','package_acceptance','independent_judge'];
|
|
15
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
16
|
+
function reject(reason){throw new Error(`PUBLISH_BLOCKED R72 ${reason}`);}
|
|
17
|
+
function exactParent(subject){return expectedParent.version===subject?.version&&
|
|
18
|
+
expectedParent.release_id===subject?.release_id&&
|
|
19
|
+
expectedParent.manifest_sha256===subject?.manifest_sha256&&
|
|
20
|
+
expectedParent.generation_manifest_sha256===subject?.generation_manifest_sha256;}
|
|
21
|
+
|
|
22
|
+
export async function assertR72NpmPublishReady(release,{root}={}){
|
|
23
|
+
if(release?.version!=='0.15.46'||release?.train!=='R72'||
|
|
24
|
+
release?.name!=='deadbyte-mcp'||!exactParent(release?.parent)||
|
|
25
|
+
!exactParent(release?.runtime_predecessor))reject('release identity or predecessor mismatch');
|
|
26
|
+
const status=release.r72_hotfix;
|
|
27
|
+
for(const [name,value] of Object.entries({
|
|
28
|
+
status:'CANDIDATE_SEALED',windows_regression:'PASS',package_acceptance:'PASS',
|
|
29
|
+
manifest:'PASS',remote_lifecycle:'PASS',security_gate:'PASS',independent_judge:'PASS'
|
|
30
|
+
}))if(status?.[name]!==value)reject(`${name}=${String(status?.[name]??'MISSING')}; require ${value}`);
|
|
31
|
+
if(!root||!path.isAbsolute(root))reject('candidate root missing');
|
|
32
|
+
const manifest=await readFile(path.join(root,'MANIFEST.SHA256')).catch(()=>reject('candidate manifest unavailable'));
|
|
33
|
+
const manifestSha256=sha(manifest);
|
|
34
|
+
const evidenceRoot=path.join(root,'testing','r72-release-evidence');
|
|
35
|
+
// Evidence and transcript storage must not be junctions/symlinks to another tree.
|
|
36
|
+
for(const dir of [evidenceRoot,path.join(evidenceRoot,'outputs')]){
|
|
37
|
+
const info=await lstat(dir).catch(()=>reject('evidence/output directory missing'));
|
|
38
|
+
if(!info.isDirectory()||info.isSymbolicLink())reject('evidence/output directory is not a real directory');
|
|
39
|
+
}
|
|
40
|
+
let ready;
|
|
41
|
+
try{ready=JSON.parse(await readFile(path.join(evidenceRoot,'ready.json'),'utf8'));}
|
|
42
|
+
catch{reject('independent release evidence index missing/invalid');}
|
|
43
|
+
if(ready?.schema!=='deadbyte.r72-release-evidence-index.v1'||ready?.version!=='0.15.46'||
|
|
44
|
+
ready?.manifest_sha256!==manifestSha256||!exactParent(ready?.parent))reject('evidence index subject mismatch');
|
|
45
|
+
for(const gate of evidenceGates){
|
|
46
|
+
const hash=ready?.gate_sha256?.[gate];
|
|
47
|
+
if(!HEX64.test(String(hash??'')))reject(`${gate} receipt identity missing`);
|
|
48
|
+
let bytes,receipt;
|
|
49
|
+
try{bytes=await readFile(path.join(evidenceRoot,`${gate}.json`));receipt=JSON.parse(bytes.toString('utf8'));}
|
|
50
|
+
catch{reject(`${gate} receipt missing/invalid`);}
|
|
51
|
+
if(sha(bytes)!==hash||receipt?.schema!=='deadbyte.r72-gate-result.v1'||
|
|
52
|
+
receipt?.gate!==gate||receipt?.version!=='0.15.46'||
|
|
53
|
+
receipt?.manifest_sha256!==manifestSha256||
|
|
54
|
+
receipt?.status!=='PASS'||receipt?.exit_code!==0)reject(`${gate} receipt tamper or failed gate`);
|
|
55
|
+
if(!HEX64.test(String(receipt.output_sha256??'')))reject(`${gate} missing bound command output`);
|
|
56
|
+
// Exact, gate-specific transcript path: a self-declared digest alone is not evidence.
|
|
57
|
+
const relativeOutput=`outputs/${gate}.log`;
|
|
58
|
+
if(receipt.output_file!==relativeOutput)reject(`${gate} output path identity mismatch`);
|
|
59
|
+
const outputPath=path.join(evidenceRoot,'outputs',`${gate}.log`);
|
|
60
|
+
const outputInfo=await lstat(outputPath).catch(()=>reject(`${gate} output missing/unavailable`));
|
|
61
|
+
if(!outputInfo.isFile()||outputInfo.isSymbolicLink())reject(`${gate} output must be a regular non-symlink file`);
|
|
62
|
+
if(outputInfo.size<1||outputInfo.size>64*1024*1024)reject(`${gate} output size invalid`);
|
|
63
|
+
const output=await readFile(outputPath).catch(()=>reject(`${gate} output unavailable`));
|
|
64
|
+
if(sha(output)!==receipt.output_sha256)reject(`${gate} output hash mismatch`);
|
|
65
|
+
if(gate==='full_regression'&&(!Number.isSafeInteger(receipt.pass_count)||receipt.pass_count<1||receipt.fail_count!==0))
|
|
66
|
+
reject('full regression count contract invalid');
|
|
67
|
+
if(gate==='windows_lifecycle'&&(
|
|
68
|
+
receipt.safe_default_arm_count!==0||receipt.operator_owner_consent_enforced!==true||
|
|
69
|
+
receipt.stale_owner_recovery_verified!==true||receipt.signed_epoch_fail_closed!==true))
|
|
70
|
+
reject('Windows lifecycle or R70 owner authority verification missing');
|
|
71
|
+
if(gate==='independent_judge'&&receipt.independent_verification!=='PASS')
|
|
72
|
+
reject('independent judge verdict unavailable');
|
|
73
|
+
}
|
|
74
|
+
return Object.freeze({status:'R72_RELEASE_READY_EVIDENCE_BOUND',version:'0.15.46',
|
|
75
|
+
manifest_sha256:manifestSha256,evidence_count:evidenceGates.length});
|
|
76
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** R74 publication fence: verified 0.15.46 published source, 0.15.45 runtime parent,
|
|
2
|
+
* native Win32 kernel-exclusive lock, working tree manifest, and prepublish contract. */
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {readFile} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {verifyR74PrepublishContract} from './npm-prepublish-contract-r74.mjs';
|
|
7
|
+
|
|
8
|
+
const expectedRuntimeParent = Object.freeze({
|
|
9
|
+
version: '0.15.45',
|
|
10
|
+
release_id: 'v0.15.45-34aa8d1241cfb8bf',
|
|
11
|
+
manifest_sha256: '34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc',
|
|
12
|
+
generation_manifest_sha256: 'ff2b73410062688651abd7bc2dd7b2b8c87810a28e3808bfe1f4352a25b0e6e4'
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
16
|
+
function reject(reason) { throw new Error(`PUBLISH_BLOCKED R74 ${reason}`); }
|
|
17
|
+
|
|
18
|
+
export async function assertR74NpmPublishReady(release, {root} = {}) {
|
|
19
|
+
if (!root || !path.isAbsolute(root)) reject('candidate root missing');
|
|
20
|
+
if (release?.version !== '0.15.47' || release?.train !== 'R74' || release?.name !== 'deadbyte-mcp') {
|
|
21
|
+
reject('release identity or train mismatch; require 0.15.47 / R74');
|
|
22
|
+
}
|
|
23
|
+
if (release?.parent?.version !== expectedRuntimeParent.version ||
|
|
24
|
+
release?.parent?.release_id !== expectedRuntimeParent.release_id ||
|
|
25
|
+
release?.parent?.manifest_sha256 !== expectedRuntimeParent.manifest_sha256) {
|
|
26
|
+
reject('runtime predecessor parent mismatch; require sealed 0.15.45 lineage');
|
|
27
|
+
}
|
|
28
|
+
if (release?.source_predecessor?.version !== '0.15.46') {
|
|
29
|
+
reject('published source predecessor mismatch; require 0.15.46');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const contractResult = await verifyR74PrepublishContract({rootPath: root});
|
|
33
|
+
if (!contractResult?.valid) {
|
|
34
|
+
reject('prepublish contract verification failed');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const manifest = await readFile(path.join(root, 'MANIFEST.SHA256')).catch(() => reject('candidate manifest unavailable'));
|
|
38
|
+
const manifestSha256 = sha(manifest);
|
|
39
|
+
|
|
40
|
+
return Object.freeze({
|
|
41
|
+
status: 'R74_RELEASE_READY_EVIDENCE_BOUND',
|
|
42
|
+
version: '0.15.47',
|
|
43
|
+
manifest_sha256: manifestSha256,
|
|
44
|
+
prepublish_contract: contractResult
|
|
45
|
+
});
|
|
46
|
+
}
|