deadbyte-mcp 0.15.42 → 0.15.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/MANIFEST.SHA256 +131 -68
  2. package/PROVENANCE.json +37 -13
  3. package/README.txt +18 -18
  4. package/RELEASE.json +40 -14
  5. package/SBOM.cdx.json +14 -2
  6. package/TEST-MATRIX.json +52 -10
  7. package/bin/appcontainer-stage.obj +0 -0
  8. package/bin/bootstrap-advapi32.obj +0 -0
  9. package/bin/bootstrap-exitcode.obj +0 -0
  10. package/bin/bootstrap-kernel32.obj +0 -0
  11. package/bin/child-control-probe.obj +0 -0
  12. package/bin/child-control-stage.obj +0 -0
  13. package/bin/contained-reverse-worker.obj +0 -0
  14. package/bin/contained-transform-worker.obj +0 -0
  15. package/bin/containment-probe.obj +0 -0
  16. package/bin/deadbyte-contain.obj +0 -0
  17. package/bin/deadbyte-desktop-input-r40.obj +0 -0
  18. package/bin/deadbyte-desktop-observer-r40.obj +0 -0
  19. package/bin/deadbyte-desktop-uia-r40.obj +0 -0
  20. package/bin/deadbyte-exec.obj +0 -0
  21. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  22. package/bin/deadbyte-native-inspect-r67.exe +0 -0
  23. package/bin/deadbyte-native-inspect-r67.obj +0 -0
  24. package/bin/deadbyte-process-host.obj +0 -0
  25. package/bin/deadbyte-tunnel-host.obj +0 -0
  26. package/bridge/package.json +1 -1
  27. package/controller/deadbyte-desktop-policy.json +5 -5
  28. package/controller/deadbyte-process-policy.json +4 -4
  29. package/docs/R67-ALPHA-REFERENCE-DIFFERENTIAL.md +42 -0
  30. package/docs/R67-ALPHA-STATIC-COMPARISON.json +205 -0
  31. package/docs/R67-CANDIDATE-PARENT.json +10 -0
  32. package/docs/R67-EVIDENCE-RECONCILE-CLOSEOUT-20261011.md +64 -0
  33. package/docs/R67-FULL-REGRESSION-UNKNOWN.json +27 -0
  34. package/docs/R67-NATIVE-INSPECT-BINARY.json +11 -0
  35. package/docs/R67-NPM-EXDEV-PRESERVATION-20261011.md +39 -0
  36. package/docs/R67-OBSERVATION-TRUTH.md +82 -0
  37. package/docs/R67-REFERENCE-GAP-MATRIX.json +57 -0
  38. package/docs/R67-REGRESSION-CLOSURE-20261011.md +24 -0
  39. package/native/deadbyte-native-inspect-r67.cpp +252 -0
  40. package/package.json +7 -3
  41. package/proof/CONTAINMENT-BUILD.txt +6 -6
  42. package/scripts/build-native-inspector-r67.ps1 +28 -0
  43. package/scripts/gate-windows-r66-prepublish.ps1 +65 -0
  44. package/scripts/gate-windows-r67.ps1 +56 -0
  45. package/scripts/npm-preserve-artifact-r67.mjs +99 -0
  46. package/scripts/npm-release-r48.mjs +10 -2
  47. package/scripts/npm-release-readiness-r67.mjs +26 -0
  48. package/scripts/r67-acquire-alpha-reference.mjs +95 -0
  49. package/scripts/r67-audit-nonpass-source.mjs +39 -0
  50. package/scripts/r67-compare-desktop-alpha.mjs +48 -0
  51. package/scripts/r67-diagnose-failed-shard.mjs +23 -0
  52. package/scripts/r67-durable-batch.mjs +42 -0
  53. package/scripts/r67-focused-gate.mjs +58 -0
  54. package/scripts/r67-full-regression.mjs +28 -0
  55. package/scripts/r67-independent-observation-verify.mjs +36 -0
  56. package/scripts/r67-independent-runtime-observation-verify.mjs +63 -0
  57. package/scripts/r67-nonpass-adjudicate.mjs +20 -0
  58. package/scripts/r67-publish-boundary-audit.mjs +32 -0
  59. package/scripts/r67-regression-reconcile.mjs +10 -0
  60. package/scripts/r67-regression-shard-run.mjs +70 -0
  61. package/scripts/r67-run-shard-batch.mjs +18 -0
  62. package/scripts/r67-stage-candidate.mjs +30 -0
  63. package/scripts/r67-verify-parent-ping.mjs +22 -0
  64. package/scripts/release-manifest.mjs +1 -0
  65. package/src/activation-continuity-engine-r65.mjs +2 -1
  66. package/src/autonomous-runtime.mjs +1 -1
  67. package/src/deadbyte-cli.mjs +33 -1
  68. package/src/doctor-runtime-r58.mjs +3 -1
  69. package/src/machine-fs-smoke-client.mjs +13 -5
  70. package/src/machine-identity.mjs +59 -2
  71. package/src/machine-mcp-tools.mjs +8 -0
  72. package/src/mcp-server.mjs +11 -2
  73. package/src/native-inspector-r67.mjs +66 -0
  74. package/src/r67-diagnostic-export.mjs +118 -0
  75. package/src/r67-diagnostics-verify.mjs +82 -0
  76. package/src/r67-evidence-reconcile.mjs +128 -0
  77. package/src/r67-native-observation-authority.mjs +45 -0
  78. package/src/r67-observation-receipt.mjs +43 -0
  79. package/src/r67-regression-shards.mjs +176 -0
  80. package/src/remote-live-log.mjs +14 -10
  81. package/src/tool-audit.mjs +1 -1
  82. package/src/version.mjs +1 -1
  83. package/test/agent-runtime.test.mjs +1 -1
  84. package/test/autonomous-loop.test.mjs +11 -6
  85. package/test/autonomous-runtime.test.mjs +1 -1
  86. package/test/coding-plane.test.mjs +1 -1
  87. package/test/full-max-catalog-r46.test.mjs +1 -1
  88. package/test/output-schema.test.mjs +1 -1
  89. package/test/r51-live-log-polling.test.mjs +12 -0
  90. package/test/r53-release-contract.test.mjs +1 -1
  91. package/test/r54-release-contract.test.mjs +1 -1
  92. package/test/r55-release-contract.test.mjs +1 -1
  93. package/test/r56-release-contract.test.mjs +1 -1
  94. package/test/r57-release-contract.test.mjs +1 -1
  95. package/test/r58-bundled-bridge.test.mjs +1 -1
  96. package/test/r58-doctor-runtime.test.mjs +18 -0
  97. package/test/r58-release-contract.test.mjs +2 -2
  98. package/test/r58-windows-gate.test.mjs +1 -1
  99. package/test/r59-tool-surface-compat.test.mjs +1 -1
  100. package/test/r60-release-contract.test.mjs +3 -3
  101. package/test/r61-release-contract.test.mjs +1 -1
  102. package/test/r62-release-contract.test.mjs +1 -1
  103. package/test/r63-release-contract.test.mjs +1 -1
  104. package/test/r64-release-contract.test.mjs +1 -1
  105. package/test/r65-epoch-native-activation-integration.test.mjs +4 -4
  106. package/test/r65-release-contract.test.mjs +2 -1
  107. package/test/r66-release-contract.test.mjs +57 -0
  108. package/test/r67-cli-integration.test.mjs +41 -0
  109. package/test/r67-diagnostics-cli.test.mjs +60 -0
  110. package/test/r67-diagnostics-verify.test.mjs +49 -0
  111. package/test/r67-diagnostics-zip-oracle.test.mjs +38 -0
  112. package/test/r67-diagnostics.test.mjs +60 -0
  113. package/test/r67-evidence-reconcile.test.mjs +186 -0
  114. package/test/r67-isolated-slot-rollback-restore.test.mjs +62 -0
  115. package/test/r67-live-log-secret-canary.test.mjs +44 -0
  116. package/test/r67-machine-fs-smoke-epoch.test.mjs +22 -0
  117. package/test/r67-native-inspector.test.mjs +131 -0
  118. package/test/r67-native-observation-authority.test.mjs +49 -0
  119. package/test/r67-npm-cross-volume.test.mjs +74 -0
  120. package/test/r67-npm-publish-gate.test.mjs +38 -0
  121. package/test/r67-regression-shards.test.mjs +184 -0
  122. package/test/r67-release-contract.test.mjs +44 -0
  123. package/test/r67-runtime-observation-binding.test.mjs +86 -0
  124. package/test/r67-server-observation-wiring.test.mjs +12 -0
  125. package/test/r67-signed-receipt.test.mjs +66 -0
  126. package/test/r67-skip-rationale.test.mjs +31 -0
  127. package/test/r67-tool-annotations.test.mjs +21 -0
  128. package/test/r67-windows-gate-argv.test.mjs +13 -0
  129. package/test/release-version.test.mjs +1 -1
  130. package/test/remote-live-log-r36.test.mjs +10 -0
  131. package/test/remote-live-log-r37.test.mjs +29 -0
  132. package/test/semantic-multilang.test.mjs +1 -1
@@ -0,0 +1,252 @@
1
+ #define WIN32_LEAN_AND_MEAN
2
+ #define NOMINMAX
3
+ #include <windows.h>
4
+ #include <tlhelp32.h>
5
+ #include <bcrypt.h>
6
+ #include <winnt.h>
7
+ #include <cstdint>
8
+ #include <cstring>
9
+ #include <iomanip>
10
+ #include <limits>
11
+ #include <iostream>
12
+ #include <sstream>
13
+ #include <stdexcept>
14
+ #include <string>
15
+ #include <vector>
16
+ #include <algorithm>
17
+
18
+ #pragma comment(lib,"bcrypt.lib")
19
+ struct Handle { HANDLE h=INVALID_HANDLE_VALUE; explicit Handle(HANDLE p):h(p){} ~Handle(){if(h&&h!=INVALID_HANDLE_VALUE)CloseHandle(h);} Handle(const Handle&)=delete; Handle& operator=(const Handle&)=delete; };
20
+ static std::string hex(const uint64_t x){std::ostringstream s;s<<"0x"<<std::hex<<x;return s.str();}
21
+ static std::string escape(const std::string& in){
22
+ std::ostringstream s;s<<'"';
23
+ for(unsigned char c:in){if(c=='"'||c=='\\')s<<'\\'<<char(c);else if(c=='\n')s<<"\\n";else if(c=='\r')s<<"\\r";else if(c=='\t')s<<"\\t";else if(c<32){s<<"\\u00"<<std::hex<<std::setw(2)<<std::setfill('0')<<int(c)<<std::dec;}else s<<char(c);}
24
+ s<<'"';return s.str();
25
+ }
26
+ static std::string utf8(const std::wstring& wide){
27
+ if(wide.empty())return {};
28
+ const int n=WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,wide.data(),int(wide.size()),nullptr,0,nullptr,nullptr);
29
+ if(n<=0)throw std::runtime_error("invalid UTF-16"); std::string s(size_t(n),'\0');
30
+ if(WideCharToMultiByte(CP_UTF8,WC_ERR_INVALID_CHARS,wide.data(),int(wide.size()),s.data(),n,nullptr,nullptr)!=n)throw std::runtime_error("UTF-8 conversion failed");return s;
31
+ }
32
+ static std::string sha256(const uint8_t* p,size_t n){
33
+ BCRYPT_ALG_HANDLE alg=nullptr;BCRYPT_HASH_HANDLE ctx=nullptr;
34
+ if(!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&alg,BCRYPT_SHA256_ALGORITHM,nullptr,0)))throw std::runtime_error("CNG open failed");
35
+ DWORD length=0,read=0;
36
+ if(!BCRYPT_SUCCESS(BCryptGetProperty(alg,BCRYPT_HASH_LENGTH,reinterpret_cast<PUCHAR>(&length),sizeof(length),&read,0))||length!=32){BCryptCloseAlgorithmProvider(alg,0);throw std::runtime_error("CNG SHA length invalid");}
37
+ std::vector<uint8_t> digest(length);
38
+ NTSTATUS rc=BCryptCreateHash(alg,&ctx,nullptr,0,nullptr,0,0);
39
+ if(BCRYPT_SUCCESS(rc))rc=BCryptHashData(ctx,const_cast<PUCHAR>(p),ULONG(n),0);
40
+ if(BCRYPT_SUCCESS(rc))rc=BCryptFinishHash(ctx,digest.data(),length,0);
41
+ if(ctx)BCryptDestroyHash(ctx);BCryptCloseAlgorithmProvider(alg,0);
42
+ if(!BCRYPT_SUCCESS(rc))throw std::runtime_error("CNG hash failed");
43
+ std::ostringstream s;for(uint8_t b:digest)s<<std::hex<<std::setfill('0')<<std::setw(2)<<int(b);return s.str();
44
+ }
45
+ static std::string sha256(const std::string& s){return sha256(reinterpret_cast<const uint8_t*>(s.data()),s.size());}
46
+ static uint64_t fticks(FILETIME t){ULARGE_INTEGER u{};u.LowPart=t.dwLowDateTime;u.HighPart=t.dwHighDateTime;return u.QuadPart;}
47
+ static std::string timestamp(FILETIME t){SYSTEMTIME s{};if(!FileTimeToSystemTime(&t,&s))return "";std::ostringstream o;o<<std::setfill('0')<<std::setw(4)<<s.wYear<<"-"<<std::setw(2)<<s.wMonth<<"-"<<std::setw(2)<<s.wDay<<"T"<<std::setw(2)<<s.wHour<<":"<<std::setw(2)<<s.wMinute<<":"<<std::setw(2)<<s.wSecond<<"."<<std::setw(3)<<s.wMilliseconds<<"Z";return o.str();}
48
+ static std::string image(HANDLE h){
49
+ std::vector<wchar_t> b(32768,L'\0');DWORD size=DWORD(b.size());
50
+ if(!QueryFullProcessImageNameW(h,0,b.data(),&size))return "null";
51
+ return escape(utf8(std::wstring(b.data(),size)));
52
+ }
53
+ static std::string process_item(DWORD pid,DWORD ppid,const std::wstring& name){
54
+ std::ostringstream o;o<<"{\"pid\":"<<pid<<",\"parent_pid\":"<<ppid<<",\"name\":"<<escape(utf8(name));
55
+ Handle proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,pid));
56
+ if(!proc.h){const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();}
57
+ FILETIME c{},e{},k{},u{};
58
+ if(!GetProcessTimes(proc.h,&c,&e,&k,&u)){const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();}
59
+ std::vector<wchar_t> filename(32768,L'\0'); DWORD chars=DWORD(filename.size());
60
+ if(!QueryFullProcessImageNameW(proc.h,0,filename.data(),&chars)){
61
+ const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();
62
+ }
63
+ const std::wstring full(filename.data(),chars);
64
+ const size_t leafIndex=full.find_last_of(L"\\/");
65
+ const std::wstring leaf=full.substr(leafIndex==std::wstring::npos?0:leafIndex+1);
66
+ if(CompareStringOrdinal(leaf.c_str(),-1,name.c_str(),-1,TRUE)!=CSTR_EQUAL){
67
+ o<<",\"state\":\"stale_or_reused\",\"snapshot_binding\":\"NAME_MISMATCH\",\"win32_error\":0}";return o.str();
68
+ }
69
+ const DWORD wait=WaitForSingleObject(proc.h,0);
70
+ if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT){
71
+ const DWORD error=GetLastError();o<<",\"state\":\"unavailable\",\"win32_error\":"<<error<<"}";return o.str();
72
+ }
73
+ o<<",\"state\":\"observed\",\"snapshot_binding\":\"UNVERIFIED\",\"creation_time_utc\":"<<escape(timestamp(c))
74
+ <<",\"creation_filetime\":"<<escape(std::to_string(fticks(c)))
75
+ <<",\"image_path\":"<<escape(utf8(full))
76
+ <<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")<<",\"win32_error\":0}";
77
+ return o.str();
78
+ }
79
+ static std::string snapshot(size_t limit){
80
+ Handle snap(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0));
81
+ if(snap.h==INVALID_HANDLE_VALUE)throw std::runtime_error("CreateToolhelp32Snapshot failed "+std::to_string(GetLastError()));
82
+ PROCESSENTRY32W row{};row.dwSize=sizeof(row);
83
+ if(!Process32FirstW(snap.h,&row))throw std::runtime_error("Process32FirstW failed "+std::to_string(GetLastError()));
84
+ std::ostringstream o;o<<"{\"api\":\"CreateToolhelp32Snapshot+OpenProcess+GetProcessTimes+QueryFullProcessImageNameW\",\"processes\":[";
85
+ size_t count=0;bool more=false;
86
+ do{if(count>=limit){more=true;break;}if(count)o<<",";o<<process_item(row.th32ProcessID,row.th32ParentProcessID,row.szExeFile);++count;}while(Process32NextW(snap.h,&row));
87
+ o<<"],\"count\":"<<count<<",\"truncated\":"<<(more?"true":"false")<<"}";return o.str();
88
+ }
89
+ static std::string identity(DWORD pid){
90
+ if(pid==0)throw std::runtime_error("PID 0 has no process identity");
91
+ Handle proc(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|SYNCHRONIZE,FALSE,pid));
92
+ if(!proc.h)throw std::runtime_error("OpenProcess identity win32="+std::to_string(GetLastError()));
93
+ FILETIME c{},e{},k{},u{};
94
+ if(!GetProcessTimes(proc.h,&c,&e,&k,&u))throw std::runtime_error("GetProcessTimes identity win32="+std::to_string(GetLastError()));
95
+ const DWORD wait=WaitForSingleObject(proc.h,0);
96
+ if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT)throw std::runtime_error("WaitForSingleObject identity win32="+std::to_string(GetLastError()));
97
+ std::ostringstream o;
98
+ o<<"{\"api\":\"OpenProcess+GetProcessTimes+QueryFullProcessImageNameW\",\"pid\":"<<pid
99
+ <<",\"creation_time_utc\":"<<escape(timestamp(c))
100
+ <<",\"creation_filetime\":"<<escape(std::to_string(fticks(c)))
101
+ <<",\"image_path\":"<<image(proc.h)
102
+ <<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")<<"}";
103
+ return o.str();
104
+ }
105
+ static std::string memory_map(DWORD pid,size_t limit,uint64_t expectedCreation=0){
106
+ if(pid==0)throw std::runtime_error("PID 0 is not a process handle");
107
+ Handle proc(OpenProcess(PROCESS_QUERY_INFORMATION|SYNCHRONIZE,FALSE,pid));
108
+ if(!proc.h)throw std::runtime_error("OpenProcess PROCESS_QUERY_INFORMATION win32="+std::to_string(GetLastError()));
109
+ FILETIME c{},e{},k{},u{};
110
+ if(!GetProcessTimes(proc.h,&c,&e,&k,&u))throw std::runtime_error("GetProcessTimes win32="+std::to_string(GetLastError()));
111
+ const uint64_t cid=fticks(c);
112
+ if(expectedCreation!=0&&expectedCreation!=cid)throw std::runtime_error("PROCESS_IDENTITY_MISMATCH: PID is stale or reused");
113
+ std::ostringstream o;o<<"{\"api\":\"VirtualQueryEx\",\"pid\":"<<pid
114
+ <<",\"creation_time_utc\":"<<escape(timestamp(c))
115
+ <<",\"creation_filetime\":"<<escape(std::to_string(cid))
116
+ <<",\"image_path\":"<<image(proc.h)<<",\"regions\":[";
117
+ uint64_t pos=0;size_t count=0;bool end=false;DWORD error=0;
118
+ const uint64_t maxAddr=static_cast<uint64_t>(std::numeric_limits<uintptr_t>::max());
119
+ while(count<limit){
120
+ MEMORY_BASIC_INFORMATION mbi{};
121
+ SIZE_T result=VirtualQueryEx(proc.h,reinterpret_cast<LPCVOID>(static_cast<uintptr_t>(pos)),&mbi,sizeof(mbi));
122
+ if(!result){
123
+ error=GetLastError();
124
+ if(WaitForSingleObject(proc.h,0)==WAIT_OBJECT_0)throw std::runtime_error("PROCESS_EXITED_DURING_QUERY");
125
+ if(error==ERROR_INVALID_PARAMETER&&pos!=0)end=true;
126
+ break;
127
+ }
128
+ const uint64_t base=reinterpret_cast<uint64_t>(mbi.BaseAddress);
129
+ const uint64_t size=uint64_t(mbi.RegionSize);
130
+ if(!size||base>maxAddr-size)throw std::runtime_error("VirtualQueryEx non-progress/overflow");
131
+ if(count)o<<',';
132
+ o<<"{\"base\":"<<escape(hex(base))<<",\"allocation_base\":"<<escape(hex(reinterpret_cast<uint64_t>(mbi.AllocationBase)))
133
+ <<",\"size\":"<<escape(std::to_string(size))<<",\"state\":"<<mbi.State
134
+ <<",\"protect\":"<<mbi.Protect<<",\"type\":"<<mbi.Type<<"}";
135
+ ++count;const uint64_t next=base+size;if(next<=pos)throw std::runtime_error("VirtualQueryEx non-progress");
136
+ pos=next;
137
+ }
138
+ if(error&&!end)throw std::runtime_error("VirtualQueryEx win32="+std::to_string(error));
139
+ o<<"],\"count\":"<<count<<",\"truncated\":"<<((count==limit&&!end)?"true":"false")
140
+ <<",\"terminal_win32_error\":"<<(end?0:error)<<",\"terminal\":\""<<(end?"end":"limit")<<"\"";
141
+ FILETIME c2{},e2{},k2{},u2{};
142
+ if(!GetProcessTimes(proc.h,&c2,&e2,&k2,&u2)||fticks(c2)!=cid)throw std::runtime_error("process identity changed during inspection");
143
+ const DWORD wait=WaitForSingleObject(proc.h,0);
144
+ if(wait!=WAIT_OBJECT_0&&wait!=WAIT_TIMEOUT)throw std::runtime_error("WaitForSingleObject win32="+std::to_string(GetLastError()));
145
+ o<<",\"process_exited\":"<<(wait==WAIT_OBJECT_0?"true":"false")
146
+ <<",\"outcome\":\""<<(wait==WAIT_OBJECT_0?"PROCESS_EXITED":"HANDLE_IDENTITY_CONFIRMED")<<"\"}";return o.str();
147
+ }
148
+ static std::vector<uint8_t> filebytes(const std::wstring& filename){
149
+ Handle h(CreateFileW(filename.c_str(),GENERIC_READ,FILE_SHARE_READ,nullptr,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nullptr));
150
+ if(h.h==INVALID_HANDLE_VALUE)throw std::runtime_error("CreateFileW win32="+std::to_string(GetLastError()));
151
+ LARGE_INTEGER length{};if(!GetFileSizeEx(h.h,&length))throw std::runtime_error("GetFileSizeEx failed");
152
+ if(length.QuadPart<64||length.QuadPart>64*1024*1024)throw std::runtime_error("PE file size outside 64B..64MiB");
153
+ std::vector<uint8_t> data(size_t(length.QuadPart));
154
+ size_t offset=0;
155
+ while(offset<data.size()){DWORD received=0;DWORD chunk=DWORD(std::min<size_t>(1024*1024,data.size()-offset));if(!ReadFile(h.h,data.data()+offset,chunk,&received,nullptr)||!received)throw std::runtime_error("ReadFile failed/short");offset+=received;}
156
+ LARGE_INTEGER final{};if(!GetFileSizeEx(h.h,&final)||final.QuadPart!=length.QuadPart)throw std::runtime_error("PE file size changed during read");
157
+ return data;
158
+ }
159
+ template<typename T>static T read_at(const std::vector<uint8_t>& b,size_t off){
160
+ if(off>b.size()||sizeof(T)>b.size()-off)throw std::runtime_error("PE structure truncated");
161
+ T v{};std::memcpy(&v,b.data()+off,sizeof(T));return v;
162
+ }
163
+ static std::string pe(const std::wstring& filename){
164
+ const auto b=filebytes(filename);
165
+ const auto dos=read_at<IMAGE_DOS_HEADER>(b,0);
166
+ if(dos.e_magic!=IMAGE_DOS_SIGNATURE||dos.e_lfanew<sizeof(IMAGE_DOS_HEADER))throw std::runtime_error("invalid MZ/PE offset");
167
+ const size_t off=size_t(dos.e_lfanew);
168
+ if(read_at<DWORD>(b,off)!=IMAGE_NT_SIGNATURE)throw std::runtime_error("invalid PE signature");
169
+ const auto coff=read_at<IMAGE_FILE_HEADER>(b,off+4);
170
+ if(coff.NumberOfSections==0||coff.NumberOfSections>96)throw std::runtime_error("invalid number of sections");
171
+ const size_t opt=off+4+sizeof(IMAGE_FILE_HEADER);
172
+ if(opt>b.size()||coff.SizeOfOptionalHeader>b.size()-opt)throw std::runtime_error("optional header truncated");
173
+ const WORD magic=read_at<WORD>(b,opt);
174
+ uint64_t base=0;DWORD entry=0;
175
+ if(magic==IMAGE_NT_OPTIONAL_HDR32_MAGIC){
176
+ if(coff.SizeOfOptionalHeader<sizeof(IMAGE_OPTIONAL_HEADER32))throw std::runtime_error("PE32 optional header truncated");
177
+ const auto h=read_at<IMAGE_OPTIONAL_HEADER32>(b,opt);base=h.ImageBase;entry=h.AddressOfEntryPoint;
178
+ }else if(magic==IMAGE_NT_OPTIONAL_HDR64_MAGIC){
179
+ if(coff.SizeOfOptionalHeader<sizeof(IMAGE_OPTIONAL_HEADER64))throw std::runtime_error("PE32+ optional header truncated");
180
+ const auto h=read_at<IMAGE_OPTIONAL_HEADER64>(b,opt);base=h.ImageBase;entry=h.AddressOfEntryPoint;
181
+ }else throw std::runtime_error("unsupported optional header magic");
182
+ const size_t sec=opt+coff.SizeOfOptionalHeader;
183
+ if(sec>b.size()||size_t(coff.NumberOfSections)>((b.size()-sec)/sizeof(IMAGE_SECTION_HEADER)))throw std::runtime_error("section table truncated");
184
+ std::ostringstream o;o<<"{\"api\":\"CreateFileW+ReadFile\",\"format\":"<<escape(magic==IMAGE_NT_OPTIONAL_HDR64_MAGIC?"PE32+":"PE32")
185
+ <<",\"file_path\":"<<escape(utf8(filename))<<",\"file_size\":"<<b.size()
186
+ <<",\"file_sha256\":"<<escape(sha256(b.data(),b.size()))<<",\"machine\":"<<coff.Machine
187
+ <<",\"number_of_sections\":"<<coff.NumberOfSections<<",\"image_base\":"<<escape(hex(base))
188
+ <<",\"entry_rva\":"<<escape(hex(entry))<<",\"sections\":[";
189
+ for(size_t i=0;i<coff.NumberOfSections;++i){
190
+ const auto s=read_at<IMAGE_SECTION_HEADER>(b,sec+i*sizeof(IMAGE_SECTION_HEADER));
191
+ std::string name(reinterpret_cast<const char*>(s.Name),8);const auto zero=name.find('\0');if(zero!=std::string::npos)name.resize(zero);
192
+ if(s.SizeOfRawData&&(size_t(s.PointerToRawData)>b.size()||size_t(s.SizeOfRawData)>b.size()-size_t(s.PointerToRawData)))throw std::runtime_error("section raw data exceeds file");
193
+ if(i)o<<',';
194
+ o<<"{\"name\":"<<escape(name)<<",\"rva\":"<<escape(hex(s.VirtualAddress))
195
+ <<",\"virtual_size\":"<<s.Misc.VirtualSize<<",\"raw_offset\":"<<s.PointerToRawData
196
+ <<",\"raw_size\":"<<s.SizeOfRawData<<",\"characteristics\":"<<s.Characteristics<<"}";
197
+ }
198
+ o<<"]}";return o.str();
199
+ }
200
+ int wmain(int argc,wchar_t** argv){
201
+ try{
202
+ if(argc<2)throw std::runtime_error("usage: process [limit] | map self [limit] | map PID creation_filetime [limit] | pe FILE");
203
+ const std::wstring op=argv[1];std::string data;
204
+ if(op==L"process"){
205
+ size_t limit=64;if(argc>2){auto x=std::stoul(argv[2]);if(x<1||x>256)throw std::runtime_error("limit must be 1..256");limit=x;}
206
+ data=snapshot(limit);
207
+ }else if(op==L"identity"){
208
+ if(argc!=3)throw std::runtime_error("identity requires one PID");
209
+ const std::wstring value=argv[2];
210
+ if(value.empty()||!std::all_of(value.begin(),value.end(),[](wchar_t c){return c>=L'0'&&c<=L'9';}))throw std::runtime_error("invalid PID");
211
+ const auto n=std::stoull(value);if(n<1||n>MAXDWORD)throw std::runtime_error("invalid PID");
212
+ data=identity(DWORD(n));
213
+ }else if(op==L"map"){
214
+ if(argc<3)throw std::runtime_error("map requires self or PID");
215
+ const bool self=std::wstring(argv[2])==L"self";
216
+ DWORD pid=0;size_t limit=128;uint64_t expected=0;
217
+ const auto parseUnsigned=[](const wchar_t* text)->uint64_t{
218
+ const std::wstring s=text;
219
+ if(s.empty()||!std::all_of(s.begin(),s.end(),[](wchar_t c){return c>=L'0'&&c<=L'9';}))
220
+ throw std::runtime_error("invalid unsigned number");
221
+ return std::stoull(s);
222
+ };
223
+ if(self){
224
+ if(argc>4)throw std::runtime_error("map self [limit]");
225
+ pid=GetCurrentProcessId();
226
+ if(argc==4){const auto n=parseUnsigned(argv[3]);if(n<1||n>512)throw std::runtime_error("limit must be 1..512");limit=size_t(n);}
227
+ }else{
228
+ if(argc<4||argc>5)throw std::runtime_error("map PID requires creation_filetime [limit]");
229
+ const auto n=parseUnsigned(argv[2]);if(n<1||n>MAXDWORD)throw std::runtime_error("invalid PID");pid=DWORD(n);
230
+ expected=parseUnsigned(argv[3]);if(expected==0)throw std::runtime_error("creation_filetime must be nonzero");
231
+ if(argc==5){const auto nLimit=parseUnsigned(argv[4]);if(nLimit<1||nLimit>512)throw std::runtime_error("limit must be 1..512");limit=size_t(nLimit);}
232
+ }
233
+ data=memory_map(pid,limit,expected);
234
+ }else if(op==L"pe"){
235
+ if(argc!=3)throw std::runtime_error("pe requires one file path");
236
+ data=pe(argv[2]);
237
+ }else throw std::runtime_error("unknown operation");
238
+ std::cout<<"{\"schema\":\"deadbyte.native-inspect.r67.v1\",\"status\":\"ok\",\"operation\":"<<escape(utf8(op))
239
+ <<",\"data\":"<<data<<",\"data_sha256\":"<<escape(sha256(data))<<"}\n";
240
+ return 0;
241
+ }catch(const std::exception& e){
242
+ const std::string message=e.what();const auto start=message.rfind("win32=");
243
+ unsigned long win32=0;std::string category="INVALID_INPUT";
244
+ if(message.find("PROCESS_IDENTITY_MISMATCH")!=std::string::npos)category="PROCESS_IDENTITY_MISMATCH";
245
+ else if(message.find("PROCESS_EXITED_DURING_QUERY")!=std::string::npos)category="PROCESS_EXITED";
246
+ if(start!=std::string::npos){try{win32=std::stoul(message.substr(start+6));}catch(...){win32=0;}
247
+ category=win32==ERROR_ACCESS_DENIED?"ACCESS_DENIED":(win32==ERROR_INVALID_PARAMETER?"INVALID_PARAMETER":"WIN32_FAILURE");}
248
+ std::cout<<"{\"schema\":\"deadbyte.native-inspect.r67.v1\",\"status\":\"failed\",\"error_code\":"<<escape(category)
249
+ <<",\"win32_error\":"<<win32<<",\"error\":"<<escape(message)<<"}\n";
250
+ return 2;
251
+ }
252
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deadbyte-mcp",
3
- "version": "0.15.42",
3
+ "version": "0.15.44",
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-r65-prepublish.ps1",
45
+ "gate:windows": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r67.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",
@@ -90,7 +90,11 @@
90
90
  "r62:focused:test": "node --test --test-concurrency=1 test/r62-release-contract.test.mjs test/r62-doctor-transactions-reconcile.test.mjs test/r62-doctor-receipt-semantics.test.mjs test/r62-rollback-failure-receipt.test.mjs test/r58-doctor-transactions.test.mjs test/r58-completed-update-receipt.test.mjs test/runtime-self-update.test.mjs test/r56-rollback-launcher-restore.test.mjs",
91
91
  "r63:focused:test": "node --test --test-concurrency=1 test/r63-release-contract.test.mjs test/r63-one-shot-coherence.test.mjs test/r51-runtime-coherence.test.mjs test/remote-session-cli.test.mjs",
92
92
  "r64:focused:test": "node --test --test-concurrency=1 test/r64-release-contract.test.mjs test/r64-machine-fs-large-read.test.mjs test/machine-fs-runtime.test.mjs test/compact-gateway-r36.test.mjs test/r61-control-lane.test.mjs test/r60-bridge-dispatch.test.mjs",
93
- "r65:focused:test": "node scripts/r65-focused.mjs"
93
+ "r65:focused:test": "node scripts/r65-focused.mjs",
94
+ "r66:focused:test": "node --test --test-concurrency=1 test/r66-release-contract.test.mjs test/r58-doctor-runtime.test.mjs test/r58-doctor-foreground.test.mjs test/r58-doctor-installation.test.mjs test/r65-controller-authority-epoch.test.mjs test/r65-authority-observation-epoch.test.mjs",
95
+ "r67:focused:test": "node scripts/r67-focused-gate.mjs",
96
+ "r67:shard:run": "node scripts/r67-regression-shard-run.mjs",
97
+ "r67:shard:verify": "node scripts/r67-regression-reconcile.mjs"
94
98
  },
95
99
  "bundleDependencies": [
96
100
  "@modelcontextprotocol/server",
@@ -39,7 +39,7 @@ deadbyte-process-host.cpp
39
39
  exit_code=0
40
40
  ==== probe-dependencies ====
41
41
 
42
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\containment-probe.exe
42
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\containment-probe.exe
43
43
 
44
44
  File Type: EXECUTABLE IMAGE
45
45
 
@@ -59,7 +59,7 @@ File Type: EXECUTABLE IMAGE
59
59
  exit_code=0
60
60
  ==== contained-launcher-dependencies ====
61
61
 
62
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-exec.exe
62
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-exec.exe
63
63
 
64
64
  File Type: EXECUTABLE IMAGE
65
65
 
@@ -83,7 +83,7 @@ File Type: EXECUTABLE IMAGE
83
83
  exit_code=0
84
84
  ==== contained-worker-dependencies ====
85
85
 
86
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\contained-transform-worker.exe
86
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-transform-worker.exe
87
87
 
88
88
  File Type: EXECUTABLE IMAGE
89
89
 
@@ -104,7 +104,7 @@ File Type: EXECUTABLE IMAGE
104
104
  exit_code=0
105
105
  ==== contained-reverse-worker-dependencies ====
106
106
 
107
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\contained-reverse-worker.exe
107
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\contained-reverse-worker.exe
108
108
 
109
109
  File Type: EXECUTABLE IMAGE
110
110
 
@@ -125,7 +125,7 @@ File Type: EXECUTABLE IMAGE
125
125
  exit_code=0
126
126
  ==== named-tunnel-host-dependencies ====
127
127
 
128
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-tunnel-host.exe
128
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-tunnel-host.exe
129
129
 
130
130
  File Type: EXECUTABLE IMAGE
131
131
 
@@ -144,7 +144,7 @@ File Type: EXECUTABLE IMAGE
144
144
  exit_code=0
145
145
  ==== process-host-dependencies ====
146
146
 
147
- Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.30-stabilization\bin\deadbyte-process-host.exe
147
+ Dump of file D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.44-r67\bin\deadbyte-process-host.exe
148
148
 
149
149
  File Type: EXECUTABLE IMAGE
150
150
 
@@ -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-native-inspect-r67.cpp'
6
+ $observed=(Get-FileHash -LiteralPath $src -Algorithm SHA256).Hash.ToLowerInvariant()
7
+ if($observed -ne $SourceSha256){throw "R67 source SHA mismatch; refusing build"}
8
+ Write-Output "R67_SOURCE_SHA256=$observed"
9
+ $bin=Join-Path $root 'bin'
10
+ $exe=Join-Path $bin 'deadbyte-native-inspect-r67.exe'
11
+ $obj=Join-Path $bin 'deadbyte-native-inspect-r67.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 bcrypt.lib /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 "R67 native build failed exit=$rc"}
26
+ if(!(Test-Path -LiteralPath $exe -PathType Leaf)){throw 'R67 native exe missing'}
27
+ $sha=(Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLowerInvariant()
28
+ Write-Output "R67_NATIVE_BUILD_PASS sha256=$sha path=$exe"
@@ -0,0 +1,65 @@
1
+ param(
2
+ [ValidateSet('all','preflight','focused','full','manifest','doctor')]
3
+ [string]$Stage='all'
4
+ )
5
+
6
+ $ErrorActionPreference='Stop'
7
+ Set-StrictMode -Version 3.0
8
+ $root=Split-Path $PSScriptRoot -Parent
9
+ Set-Location -LiteralPath $root
10
+
11
+ function Invoke-Npm([string]$Name,[string[]]$NpmArgs){
12
+ Write-Host "==== $Name ===="
13
+ & npm.cmd @NpmArgs
14
+ if($LASTEXITCODE -ne 0){ throw "R66 prepublish gate failed at $Name exit=$LASTEXITCODE" }
15
+ }
16
+
17
+ function Get-FileSha256([string]$File){
18
+ $hasher=[Security.Cryptography.SHA256]::Create()
19
+ try{
20
+ $bytes=[IO.File]::ReadAllBytes($File)
21
+ return (($hasher.ComputeHash($bytes) | ForEach-Object { $_.ToString('x2') }) -join '')
22
+ }finally{ $hasher.Dispose() }
23
+ }
24
+
25
+ $sourceRoot='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.42-r65'
26
+ $sourceManifest='1b4754103dd4ca43eb81f8e33cf188b36eee3051cb182396ccc6abe2a6e6c250'
27
+ if(-not (Test-Path -LiteralPath $sourceRoot -PathType Container)){ throw 'R66 source predecessor missing' }
28
+ $observedSourceManifest=Get-FileSha256 (Join-Path $sourceRoot 'MANIFEST.SHA256')
29
+ if($observedSourceManifest -ne $sourceManifest){ throw 'R66 source predecessor manifest identity mismatch' }
30
+ & node (Join-Path $root 'scripts\verify-r65-parent-source.mjs') --root $sourceRoot --expected-manifest-sha256 $sourceManifest
31
+ if($LASTEXITCODE -ne 0){ throw 'R66 source predecessor declared-manifest verification failed' }
32
+
33
+ $stateRoot=Join-Path $env:USERPROFILE '.deadbyte-mcp'
34
+ $pointer=Join-Path $stateRoot 'active-release.json'
35
+ if(-not (Test-Path -LiteralPath $pointer -PathType Leaf)){ throw "R66 runtime predecessor pointer missing: $pointer" }
36
+ $active=Get-Content -LiteralPath $pointer -Raw | ConvertFrom-Json
37
+ if($active.release_id -ne 'v0.15.42-1b4754103dd4ca43'){ throw "R66 runtime predecessor release mismatch: $($active.release_id)" }
38
+ if($active.manifest_sha256 -ne $sourceManifest){ throw 'R66 runtime predecessor manifest mismatch' }
39
+ if($active.generation_manifest_sha256 -ne '62dc68be55ad627f8d872b8be8c1369ecca0edf08fdedc8336c9ccee2d2809fb'){ throw 'R66 runtime predecessor generation mismatch' }
40
+ if($Stage -eq 'preflight'){Write-Host '==== R66 WINDOWS GATE: PASS preflight ====';return}
41
+
42
+ if($Stage -eq 'all' -or $Stage -eq 'focused'){
43
+ Invoke-Npm 'r66-focused' @('run','r66:focused:test')
44
+ if($Stage -eq 'focused'){Write-Host '==== R66 WINDOWS GATE: PASS focused ====';return}
45
+ }
46
+ if($Stage -eq 'all' -or $Stage -eq 'full'){
47
+ Invoke-Npm 'full-regression' @('test')
48
+ if($Stage -eq 'full'){Write-Host '==== R66 WINDOWS GATE: PASS full ====';return}
49
+ }
50
+ if($Stage -eq 'all' -or $Stage -eq 'manifest'){
51
+ Invoke-Npm 'manifest-write' @('run','manifest:write')
52
+ Invoke-Npm 'manifest-verify' @('run','manifest:verify')
53
+ if($Stage -eq 'manifest'){Write-Host '==== R66 WINDOWS GATE: PASS manifest ====';return}
54
+ }
55
+ if($Stage -eq 'all' -or $Stage -eq 'doctor'){
56
+ $doctorRoot='D:\Projects\deadbyte-mcp\testing\0.15.43-r66\doctor-isolated-empty'
57
+ if(Test-Path -LiteralPath (Join-Path $doctorRoot 'active-release.json')){ throw 'R66 isolated doctor root unexpectedly contains a release pointer' }
58
+ $priorStateRoot=$env:DEADBYTE_STATE_ROOT
59
+ try{
60
+ $env:DEADBYTE_STATE_ROOT=$doctorRoot
61
+ Invoke-Npm 'doctor-isolated-empty-state' @('run','doctor')
62
+ }finally{ $env:DEADBYTE_STATE_ROOT=$priorStateRoot }
63
+ if($Stage -eq 'doctor'){Write-Host '==== R66 WINDOWS GATE: PASS doctor ====';return}
64
+ }
65
+ Write-Host '==== R66 PREPUBLISH WINDOWS GATE: PASS ===='
@@ -0,0 +1,56 @@
1
+ param(
2
+ [ValidateSet('preflight','focused','evidence','manifest','doctor','all')]
3
+ [string]$Stage='preflight',
4
+ [string]$OperationId=''
5
+ )
6
+ $ErrorActionPreference='Stop'
7
+ Set-StrictMode -Version Latest
8
+ $root=Split-Path -Parent $PSScriptRoot
9
+ $parent='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.43-r66'
10
+ $expected='4c4a64a4c8e9f5a6e9eecce85fd0007ebcac99121b29c07f1a1fc81d1643cd33'
11
+ $expectedGen='ef3b041e2a87c2e143d4733a98a52d9db80cd16ad7007e112fcb5202e70c77c3'
12
+ $expectedRelease='v0.15.43-4c4a64a4c8e9f5a6'
13
+ if(!(Test-Path -LiteralPath $parent -PathType Container)){throw 'R67 predecessor source missing'}
14
+ $prev=(Get-FileHash -LiteralPath (Join-Path $parent 'MANIFEST.SHA256') -Algorithm SHA256).Hash.ToLowerInvariant()
15
+ if($prev -ne $expected){throw 'R67 predecessor manifest not exact'}
16
+ $stateRoot=Join-Path $env:USERPROFILE '.deadbyte-mcp'
17
+ $pointerPath=Join-Path $stateRoot 'active-release.json'
18
+ if(!(Test-Path -LiteralPath $pointerPath -PathType Leaf)){throw 'R67 active predecessor pointer missing'}
19
+ $pointer=Get-Content -LiteralPath $pointerPath -Raw | ConvertFrom-Json
20
+ if($pointer.release_id -ne $expectedRelease -or $pointer.manifest_sha256 -ne $expected -or
21
+ $pointer.generation_manifest_sha256 -ne $expectedGen){throw 'R67 active predecessor binding mismatch'}
22
+ $pkg=Get-Content -LiteralPath (Join-Path $root 'package.json') -Raw | ConvertFrom-Json
23
+ if($pkg.version -ne '0.15.44'){throw 'R67 package candidate version mismatch'}
24
+ Write-Output 'R67_PREDECESSOR_PREFLIGHT=PASS'
25
+ if($Stage -eq 'preflight'){return}
26
+ function Invoke-Node([string]$Label,[string[]]$NodeArgs){
27
+ Write-Output "R67_STAGE_START=$Label"
28
+ & node @NodeArgs
29
+ if($LASTEXITCODE -ne 0){throw "R67 stage $Label exit=$LASTEXITCODE"}
30
+ Write-Output "R67_STAGE_PASS=$Label"
31
+ }
32
+ if($Stage -eq 'all' -or $Stage -eq 'focused'){
33
+ Invoke-Node 'focused' @('scripts/r67-focused-gate.mjs')
34
+ Invoke-Node 'machine-fs-smoke' @('src/machine-fs-smoke-client.mjs')
35
+ if($Stage -eq 'focused'){return}
36
+ }
37
+ if($Stage -eq 'all' -or $Stage -eq 'evidence'){
38
+ if($OperationId -notmatch '^[a-z0-9][a-z0-9._-]{5,63}$'){throw 'R67 full evidence operation ID required'}
39
+ Invoke-Node 'evidence' @('scripts/r67-regression-reconcile.mjs',$OperationId)
40
+ if($Stage -eq 'evidence'){return}
41
+ }
42
+ if($Stage -eq 'all' -or $Stage -eq 'manifest'){
43
+ Invoke-Node 'manifest-verify' @('scripts/release-manifest.mjs','verify')
44
+ if($Stage -eq 'manifest'){return}
45
+ }
46
+ if($Stage -eq 'all' -or $Stage -eq 'doctor'){
47
+ $prior=$env:DEADBYTE_STATE_ROOT
48
+ $isolated='D:\Projects\deadbyte-mcp\testing\0.15.44-r67\doctor-isolated-empty'
49
+ if(Test-Path -LiteralPath (Join-Path $isolated 'active-release.json')){throw 'isolated doctor root unexpectedly has active release pointer'}
50
+ try{
51
+ $env:DEADBYTE_STATE_ROOT=$isolated
52
+ Invoke-Node 'isolated-doctor' @('src/deadbyte-cli.mjs','doctor')
53
+ }finally{$env:DEADBYTE_STATE_ROOT=$prior}
54
+ if($Stage -eq 'doctor'){return}
55
+ }
56
+ Write-Output 'R67_WINDOWS_GATE=PASS_PREDEPLOY_CANDIDATE'
@@ -0,0 +1,99 @@
1
+ import {createHash,randomBytes} from 'node:crypto';
2
+ import {lstat,readFile,open,link,unlink} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ function sha256(bytes){return createHash('sha256').update(bytes).digest('hex');}
6
+ function integrity(bytes){return 'sha512-'+createHash('sha512').update(bytes).digest('base64');}
7
+ async function regular(pathname,label){
8
+ const stat=await lstat(pathname);
9
+ if(!stat.isFile()||stat.isSymbolicLink())throw new Error(label+' must be a regular non-symlink file');
10
+ return stat;
11
+ }
12
+ function assertSameIdentity(before,after){
13
+ if(before.dev!==after.dev||before.ino!==after.ino||before.size!==after.size)
14
+ throw new Error('source artifact identity changed during preservation; do not remove original');
15
+ }
16
+
17
+ /**
18
+ * Preserve an npm-packed immutable tarball across Windows volumes.
19
+ * Uses an exclusive staging file ON THE DESTINATION VOLUME, syncs complete
20
+ * verified bytes, and publishes with a same-volume, no-clobber hard link.
21
+ * No rename(D: -> C:), no partial destination visibility, no npm publish.
22
+ * Preserving exact bytes is NOT permission to send them to npm.
23
+ */
24
+ export async function preserveNpmTarball({source,destination,expectedIntegrity,expectedShasum}={}){
25
+ if(typeof source!=='string'||typeof destination!=='string'||
26
+ !path.isAbsolute(source)||!path.isAbsolute(destination)||
27
+ path.resolve(source)===path.resolve(destination))throw new Error('absolute distinct source and destination required');
28
+ if(typeof expectedIntegrity!=='string'||!/^sha512-[A-Za-z0-9+/]+=*$/.test(expectedIntegrity))
29
+ throw new Error('npm SHA-512 integrity precondition required');
30
+ if(expectedShasum!==undefined&&!/^[a-f0-9]{40}$/.test(expectedShasum))
31
+ throw new Error('invalid npm packed artifact SHA-1 precondition');
32
+
33
+ const original=await regular(source,'source artifact');
34
+ if(original.size===0||original.size>128*1024*1024)throw new Error('npm artifact size outside permitted budget');
35
+ const bytes=await readFile(source);
36
+ assertSameIdentity(original,await regular(source,'source artifact'));
37
+ if(bytes.length!==original.size||integrity(bytes)!==expectedIntegrity)
38
+ throw new Error('npm packed artifact integrity mismatch; original retained');
39
+ if(expectedShasum!==undefined&&createHash('sha1').update(bytes).digest('hex')!==expectedShasum)
40
+ throw new Error('npm packed artifact shasum mismatch; original retained');
41
+ const hash=sha256(bytes);
42
+ let committed=false;
43
+ try{
44
+ const existing=await regular(destination,'preserved artifact');
45
+ const preserved=await readFile(destination);
46
+ if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
47
+ throw new Error('existing preserved artifact differs; manual conflict reconciliation required');
48
+ committed=true;
49
+ }catch(error){
50
+ if(error?.code!=='ENOENT')throw error;
51
+ }
52
+ let staging=null;
53
+ if(!committed){
54
+ staging=path.join(path.dirname(destination),'.'+path.basename(destination)+'.stage-'+process.pid+'-'+randomBytes(8).toString('hex'));
55
+ try{
56
+ const handle=await open(staging,'wx',0o600);
57
+ try{
58
+ await handle.writeFile(bytes);
59
+ await handle.sync();
60
+ }finally{await handle.close();}
61
+ const staged=await readFile(staging);
62
+ if(staged.length!==bytes.length||sha256(staged)!==hash||integrity(staged)!==expectedIntegrity)
63
+ throw new Error('staged artifact integrity mismatch; original retained');
64
+ // Atomic visibility of already-fsynced bytes, exclusively on the destination volume.
65
+ try{await link(staging,destination);}
66
+ catch(error){
67
+ if(error?.code!=='EEXIST')throw error;
68
+ const existing=await regular(destination,'preserved artifact');
69
+ const preserved=await readFile(destination);
70
+ if(existing.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
71
+ throw new Error('concurrent preserved artifact conflict; original retained');
72
+ }
73
+ committed=true;
74
+ }finally{
75
+ if(staging)await unlink(staging).catch(e=>{if(e?.code!=='ENOENT')throw e;});
76
+ }
77
+ }
78
+ // Final post-commit verification before relinquishing the source artifact.
79
+ const final=await regular(destination,'preserved artifact');
80
+ const preserved=await readFile(destination);
81
+ if(final.size!==bytes.length||sha256(preserved)!==hash||integrity(preserved)!==expectedIntegrity)
82
+ throw new Error('preserved artifact changed after commit; original retained');
83
+ const current=await regular(source,'source artifact');
84
+ assertSameIdentity(original,current);
85
+ if(sha256(await readFile(source))!==hash)throw new Error('source bytes changed after commit; retain for reconciliation');
86
+ await unlink(source);
87
+ return {status:staging?'VERIFIED_PRESERVED':'VERIFIED_ALREADY_PRESERVED',
88
+ sha256:hash,bytes:bytes.length,integrity:expectedIntegrity};
89
+ }
90
+
91
+ export async function assertVacantPackDestination({root,filename}={}){
92
+ if(typeof root!=='string'||!path.isAbsolute(root)||typeof filename!=='string'||
93
+ !/^[A-Za-z0-9][A-Za-z0-9._-]*\.tgz$/.test(filename)||filename!==path.basename(filename))
94
+ throw new Error('invalid npm pack target');
95
+ const target=path.join(root,filename);
96
+ try{await lstat(target);}
97
+ catch(error){if(error?.code==='ENOENT')return target;throw error;}
98
+ throw new Error('PACK_BLOCKED existing npm tarball must be archived and reconciled before npm pack: '+target);
99
+ }
@@ -1,7 +1,9 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { reconcileNpmPublication } from './npm-publication-r65.mjs';
4
- import { mkdtemp, readFile, rm, unlink, mkdir, rename } from 'node:fs/promises';
4
+ import { mkdtemp, readFile, rm, unlink, mkdir } from 'node:fs/promises';
5
+ import {preserveNpmTarball,assertVacantPackDestination} from './npm-preserve-artifact-r67.mjs';
6
+ import {assertR67NpmPublishReady} from './npm-release-readiness-r67.mjs';
5
7
  import { existsSync } from 'node:fs';
6
8
  import { resolveStateRoot } from '../src/state-root.mjs';
7
9
  import path from 'node:path';
@@ -86,6 +88,7 @@ async function main() {
86
88
  if (provenance.version !== version) {
87
89
  throw new Error(`PROVENANCE.json version mismatch: package=${version} provenance=${provenance.version}`);
88
90
  }
91
+ if (!isVerifyOnly && version==='0.15.44') assertR67NpmPublishReady(release);
89
92
  if (release.parent?.release_id !== provenance.parent_release_id ||
90
93
  release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
91
94
  release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
@@ -113,6 +116,7 @@ async function main() {
113
116
  if(publicationDir)await mkdir(publicationDir,{recursive:true});
114
117
  if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
115
118
  if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
119
+ if(!resuming)await assertVacantPackDestination({root,filename:`deadbyte-mcp-${version}.tgz`});
116
120
  const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
117
121
  const packRecord=resuming?{filename:path.basename(preservedTarball)}:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
118
122
  const filename = packRecord?.filename;
@@ -196,7 +200,11 @@ async function main() {
196
200
  return;
197
201
  }
198
202
 
199
- if(!resuming)await rename(tarball,preservedTarball);
203
+ if(!resuming){
204
+ const preserved=await preserveNpmTarball({source:tarball,destination:preservedTarball,
205
+ expectedIntegrity:packRecord.integrity,expectedShasum:packRecord.shasum});
206
+ console.log('Immutable cross-volume artifact preservation verified: '+JSON.stringify(preserved));
207
+ }
200
208
  const publishTarball=preservedTarball;
201
209
  const localBytes = await readFile(publishTarball);
202
210
  const localIntegrity=`sha512-${createHash('sha512').update(localBytes).digest('base64')}`;