deadbyte-mcp 0.15.67 → 0.15.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/MANIFEST.SHA256 +85 -40
  2. package/PROVENANCE.json +46 -24
  3. package/README.txt +46 -33
  4. package/RELEASE.json +44 -17
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +25 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +65 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R184-package-lock.json +97 -0
  11. package/docs/R186-OWNER-CONSENT-HANDOFF.txt +87 -0
  12. package/package.json +3 -2
  13. package/scripts/install-r186-owner-trust.ps1 +59 -0
  14. package/scripts/r173-protected-preflight.mjs +42 -0
  15. package/scripts/r175-focused-runner.mjs +38 -0
  16. package/scripts/r176-log-preview.mjs +18 -0
  17. package/scripts/r177-owner-trust-inventory.ps1 +36 -0
  18. package/scripts/r178-full-regression.mjs +21 -0
  19. package/scripts/r178-npm-ci.mjs +10 -0
  20. package/scripts/r178-npm-preflight.mjs +16 -0
  21. package/scripts/r178-release-gates-readonly.mjs +9 -0
  22. package/scripts/r178-shard-regression.mjs +25 -0
  23. package/scripts/r179-predecessor-registry.mjs +24 -0
  24. package/scripts/r179-protected-inventory.ps1 +21 -0
  25. package/scripts/r186-focused-runner.mjs +22 -0
  26. package/scripts/r186-owner-anchor-inspect.mjs +6 -0
  27. package/scripts/r186-owner-consent-check.mjs +86 -0
  28. package/scripts/r186-owner-grant-cli.mjs +45 -0
  29. package/scripts/r186-owner-key-local.mjs +22 -0
  30. package/scripts/r186-owner-review-export.mjs +34 -0
  31. package/src/activation-authority-proof-r75.mjs +28 -4
  32. package/src/activation-preflight-r65.mjs +2 -1
  33. package/src/deadbyte-cli.mjs +69 -56
  34. package/src/r174-owner-webauthn.mjs +176 -0
  35. package/src/r175-foreground-boundary.mjs +28 -0
  36. package/src/r186-owner-anchor.mjs +12 -0
  37. package/src/r186-owner-consent-preflight.mjs +63 -0
  38. package/src/r186-owner-grant-broker.mjs +96 -0
  39. package/src/r186-owner-local-signing.mjs +25 -0
  40. package/src/r186-owner-request.mjs +40 -0
  41. package/src/r186-owner-review-export.mjs +49 -0
  42. package/src/remote-live-log.mjs +81 -5
  43. package/src/remote-session-guardian.mjs +23 -0
  44. package/src/version.mjs +1 -1
  45. package/test/helpers/r175-active-pointer.mjs +28 -0
  46. package/test/helpers/release-lock.mjs +12 -0
  47. package/test/r129-sdk-security-baseline.test.mjs +2 -1
  48. package/test/r130-release-security-contract.test.mjs +11 -10
  49. package/test/r132-release-identity.test.mjs +12 -11
  50. package/test/r153-prepublish-identity.test.mjs +1 -1
  51. package/test/r155-default-live-log.test.mjs +17 -16
  52. package/test/r160-package-decision-manifest.test.mjs +1 -1
  53. package/test/r160-release-contract.test.mjs +4 -5
  54. package/test/r163-isolated-slot-rollback-restore.test.mjs +6 -6
  55. package/test/r165-live-cli-skew.test.mjs +6 -6
  56. package/test/r167-release-contract.test.mjs +15 -13
  57. package/test/r168-protected-entrypoint.test.mjs +14 -11
  58. package/test/r168-release-contract.test.mjs +4 -3
  59. package/test/r168-remote-one-shot-coherence.test.mjs +2 -1
  60. package/test/r172-release-contract.test.mjs +8 -8
  61. package/test/r173-release-contract.test.mjs +33 -0
  62. package/test/r174-owner-webauthn.test.mjs +144 -0
  63. package/test/r175-cli-flags.test.mjs +23 -0
  64. package/test/r175-foreground-boundary.test.mjs +87 -0
  65. package/test/r175-guardian-owner-boundary.test.mjs +56 -0
  66. package/test/r176-pretty-live-log.test.mjs +54 -0
  67. package/test/r177-windows-absolute-live-path.test.mjs +37 -0
  68. package/test/r179-absolute-path-live-log.test.mjs +40 -0
  69. package/test/r179-release-contract.test.mjs +62 -0
  70. package/test/r186-controller-owner-grant.test.mjs +20 -0
  71. package/test/r186-owner-consent-preflight.test.mjs +98 -0
  72. package/test/r186-owner-enrollment.test.mjs +20 -0
  73. package/test/r186-owner-grant-broker.test.mjs +85 -0
  74. package/test/r186-owner-local-signing.test.mjs +30 -0
  75. package/test/r186-owner-request.test.mjs +28 -0
  76. package/test/r186-owner-review-export.test.mjs +52 -0
  77. package/test/r48-release-contract.test.mjs +2 -1
  78. package/test/r51-runtime-coherence.test.mjs +4 -2
  79. package/test/r58-cleanup-retention.test.mjs +5 -12
  80. package/test/r58-concurrent-recovery.test.mjs +5 -1
  81. package/test/r58-stale-owner.test.mjs +9 -3
  82. package/test/r59-tool-surface-compat.test.mjs +1 -1
  83. package/test/r75-authority-evidence.test.mjs +32 -0
  84. package/test/remote-live-log-r37.test.mjs +2 -2
  85. package/test/remote-live-log-recovery-r36.test.mjs +1 -1
  86. package/test/remote-session-cli.test.mjs +92 -126
  87. package/node_modules/which/CHANGELOG.md +0 -166
@@ -0,0 +1,59 @@
1
+ #requires -Version 5.1
2
+ #requires -RunAsAdministrator
3
+ [CmdletBinding()]
4
+ param(
5
+ [Parameter(Mandatory=$true)][string]$OwnerPublicKeyPath,
6
+ [Parameter(Mandatory=$true)][string]$NodePath
7
+ )
8
+ $ErrorActionPreference = 'Stop'
9
+ $target = 'C:\Program Files\DEADBYTE-OwnerTrust'
10
+ $packageRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
11
+ $programFiles = [System.IO.Path]::GetFullPath($env:ProgramFiles).TrimEnd('\') + '\'
12
+ if (-not ($packageRoot.TrimEnd('\') + '\').StartsWith($programFiles,[System.StringComparison]::OrdinalIgnoreCase)) {
13
+ throw 'R186 owner enrollment installer must run from a protected Program Files package'
14
+ }
15
+ if (-not (Test-Path -LiteralPath $NodePath -PathType Leaf)) { throw 'R186 Node executable missing' }
16
+ if (-not (Test-Path -LiteralPath $OwnerPublicKeyPath -PathType Leaf)) { throw 'R186 owner public key missing' }
17
+ if (Test-Path -LiteralPath $target) { throw 'R186 owner trust already exists; replacement requires a separate revocation workflow' }
18
+ $inspect = Join-Path $packageRoot 'scripts\r186-owner-anchor-inspect.mjs'
19
+ if (-not (Test-Path -LiteralPath $inspect -PathType Leaf)) { throw 'R186 protected anchor inspector missing' }
20
+ $json = @(& $NodePath $inspect ([System.IO.Path]::GetFullPath($OwnerPublicKeyPath))) -join [Environment]::NewLine
21
+ if ($LASTEXITCODE -ne 0) { throw 'R186 owner public key inspection failed' }
22
+ $material = $json | ConvertFrom-Json
23
+ if ([string]$material.anchor.schema -cne 'deadbyte.r186-owner-anchor.v1' -or
24
+ [string]$material.anchor.owner_key_id -cnotmatch '^[0-9a-f]{64}$' -or
25
+ [string]$material.public_key_pem -notmatch 'BEGIN PUBLIC KEY') { throw 'R186 owner anchor material invalid' }
26
+ Write-Host ''
27
+ Write-Host 'DEADBYTE OWNER KEY FINGERPRINT (SHA-256 SPKI):'
28
+ Write-Host ([string]$material.anchor.owner_key_id) -ForegroundColor Yellow
29
+ Write-Host 'Confirm this fingerprint using your independent owner-key display.'
30
+ $typed = Read-Host 'Type the complete 64-character fingerprint to enroll it'
31
+ if ([string]$typed -cne [string]$material.anchor.owner_key_id) { throw 'R186 owner fingerprint confirmation mismatch' }
32
+
33
+ $admins = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
34
+ $system = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-18')
35
+ $users = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-545')
36
+ $inherit = [System.Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
37
+ $none = [System.Security.AccessControl.PropagationFlags]::None
38
+ $allow = [System.Security.AccessControl.AccessControlType]::Allow
39
+ New-Item -ItemType Directory -Path $target -ErrorAction Stop | Out-Null
40
+ $dirAcl = New-Object System.Security.AccessControl.DirectorySecurity
41
+ $dirAcl.SetAccessRuleProtection($true,$false)
42
+ $dirAcl.SetOwner($admins)
43
+ $dirAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($admins,'FullControl',$inherit,$none,$allow)))
44
+ $dirAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($system,'FullControl',$inherit,$none,$allow)))
45
+ $dirAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule($users,'ReadAndExecute',$inherit,$none,$allow)))
46
+ Set-Acl -LiteralPath $target -AclObject $dirAcl
47
+
48
+ $utf8 = New-Object System.Text.UTF8Encoding($false)
49
+ $anchorPath = Join-Path $target 'owner-anchor.json'
50
+ $keyPath = Join-Path $target 'owner-public.pem'
51
+ $anchorText = $material.anchor | ConvertTo-Json -Compress
52
+ foreach ($entry in @(@($anchorPath,$anchorText),@($keyPath,[string]$material.public_key_pem))) {
53
+ $stream = [System.IO.File]::Open([string]$entry[0],[System.IO.FileMode]::CreateNew,[System.IO.FileAccess]::Write,[System.IO.FileShare]::None)
54
+ try {
55
+ $writer = New-Object System.IO.StreamWriter($stream,$utf8)
56
+ try { $writer.Write([string]$entry[1]); $writer.Flush(); $stream.Flush($true) } finally { $writer.Dispose() }
57
+ } finally { $stream.Dispose() }
58
+ }
59
+ Write-Host "R186_OWNER_TRUST_ENROLLED owner_key_id=$($material.anchor.owner_key_id)"
@@ -0,0 +1,42 @@
1
+ // R173 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ import {createHash} from 'node:crypto';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
7
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
8
+
9
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
10
+ const version='0.15.68',expectedTrain='R173';
11
+ const mandatory=['scripts/r173-protected-preflight.mjs','src/activation-continuity-abort-r172.mjs',
12
+ 'src/activation-lock-r65.mjs','src/activation-preflight-r65.mjs',
13
+ 'src/activation-reconciliation-journal-r168.mjs','src/activation-reconciliation-terminal-r168.mjs',
14
+ 'src/activation-reconciliation-fence-r168.mjs','src/activation-reconciliation-audit-r168.mjs'];
15
+ function denied(reason){throw new Error('R173_'+reason);}
16
+ function actualProtectedRoot(){
17
+ const trusted='C:\\Program Files',rel=path.win32.relative(trusted,root);
18
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))denied('ROOT_UNPROTECTED');
19
+ return root;
20
+ }
21
+ async function main(){
22
+ if(process.platform!=='win32'||process.arch!=='x64')denied('WINDOWS_X64_REQUIRED');
23
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
24
+ const verifiedRoot=actualProtectedRoot();
25
+ const pkg=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
26
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
27
+ if(pkg.name!=='deadbyte-mcp'||pkg.version!==version||release.name!=='deadbyte-mcp'||
28
+ release.version!==version||release.train!==expectedTrain)denied('VERSION_DRIFT');
29
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
30
+ const entries=parseManifest(manifest.toString('utf8'));
31
+ for(const name of mandatory)if(!entries.has(name))denied('SOURCE_NOT_IN_MANIFEST');
32
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
33
+ const important=[...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
34
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
35
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),path.win32.join(verifiedRoot,'RELEASE.json'),
36
+ path.win32.join(verifiedRoot,'package.json')];
37
+ await inspectTrustedInstallation(important);
38
+ const hash=createHash('sha256').update(manifest).digest('hex');
39
+ process.stdout.write(JSON.stringify({status:'R173_PROTECTED_PREFLIGHT_READONLY_PASS',version,train:expectedTrain,
40
+ manifest_sha256:hash,files:entries.size,terminalization_authorized:false,activation_authorized:false})+'\n');
41
+ }
42
+ try{await main();}catch(error){process.stderr.write('R173_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');process.exitCode=1;}
@@ -0,0 +1,38 @@
1
+ // R175 development-only verification runner. Executes fixed local tests, not production.
2
+ import {spawnSync} from 'node:child_process';
3
+ import path from 'node:path';
4
+ import {fileURLToPath} from 'node:url';
5
+
6
+ const repo=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
7
+ const groups=Object.freeze({
8
+ boundary:['r175-foreground-boundary','r175-guardian-owner-boundary','r175-cli-flags',
9
+ 'remote-live-log-recovery-r36','stabilization-guardian',
10
+ 'r58-guardian-channel','r76-pinned-marker-cas'],
11
+ owner:['r174-owner-webauthn','r70-owner-approval','r70-autonomous-grant-expiry',
12
+ 'r70-autonomous-policy-bootstrap','r70-controller-owner-issue-boundary'],
13
+ legacy:['r51-runtime-coherence','r72-remote-safe-contract'],
14
+ logs:['r176-pretty-live-log','r155-default-live-log','remote-live-log-r37','r155-commander-live-log','r67-live-log-secret-canary','r177-windows-absolute-live-path'],
15
+ foreground:['remote-session-cli']
16
+ });
17
+ const group=process.argv[2]??'boundary';
18
+ if(!Object.hasOwn(groups,group))throw Error('R175 test group invalid');
19
+ for(const p of ['src/deadbyte-cli.mjs','src/remote-session-guardian.mjs','src/r175-foreground-boundary.mjs']){
20
+ const c=spawnSync(process.execPath,['--check',p],{cwd:repo,encoding:'utf8',windowsHide:true,timeout:20000});
21
+ if(c.error||c.status!==0)throw Error('R175 syntax '+p+': '+String(c.stderr||c.error));
22
+ }
23
+ const tests=groups[group].map(x=>'test/'+x+'.test.mjs');
24
+ const r=spawnSync(process.execPath,['--test','--test-concurrency=1',...tests],{
25
+ cwd:repo,encoding:'utf8',windowsHide:true,timeout:180000,
26
+ maxBuffer:12*1024*1024
27
+ });
28
+ const log=(r.stdout??'')+(r.stderr??'');
29
+ const output=log.split(/\r?\n/);
30
+ for(const row of output.filter(x=>/^(ℹ (tests|pass|fail|skipped|duration_ms)|✔|✖)/.test(x)).slice(-40))
31
+ process.stdout.write(row+'\n');
32
+ process.stdout.write('R175_GROUP='+group+' EXIT='+String(r.status)+'\n');
33
+ if(r.error)process.stderr.write('R175_RUNNER_ERROR='+String(r.error)+'\n');
34
+ if(r.status!==0){
35
+ const errors=output.filter(x=>/Error:|AssertionError|Missing expected|expected|actual|✖|fail/gi.test(x));
36
+ for(const line of errors.slice(-25))process.stderr.write(line.slice(0,700)+'\n');
37
+ process.exitCode=1;
38
+ }
@@ -0,0 +1,18 @@
1
+ // R176 presentation preview with fixed synthetic events. No runtime access.
2
+ import {createLiveTimelineTracker,defaultForegroundTimelineOptions} from '../src/remote-live-log.mjs';
3
+ const base=Date.parse('2026-10-27T12:25:56.000Z');let offset=0;
4
+ const tracker=createLiveTimelineTracker({clock:()=>base+offset,rootPaths:{projects:String.raw`D:\Projects`},...defaultForegroundTimelineOptions({verbose:false})});
5
+ let seq=0;
6
+ function send(e){const lines=tracker.observe(e,{sequence:++seq});for(const l of (Array.isArray(lines)?lines:[lines]))if(l)process.stdout.write(l+'\n');}
7
+ function call(requestId,tool,args,ms,status='ok',reason=null){
8
+ offset+=ms;send({event_type:'tool.request',tool,request_id:requestId,args});
9
+ offset+=ms;send({event_type:'tool.result',tool,request_id:requestId,
10
+ outcome:{status,is_error:status==='denied',duration_ms:ms, ...(reason?{reason}:{})}});
11
+ }
12
+ call('r176-write-preview','coding_file_write',{root_id:'projects',path:'deadbyte-mcp/development/deadbyte-mcp-0.15.69-r174-owner-consent/src/alpha.mjs',content:'example-only'},17);
13
+ for(let i=0;i<5;i++)call('poll-'+i,'machine_status',{},2);
14
+ call('r176-denied','host_file_read',{root_id:'core',path:'secret-file'},1,'denied','arm file missing');
15
+ offset+=1;send({event_type:'tool.request',tool:'coding_file_write',request_id:'r176-unknown',args:{root_id:'core',path:'output.bin'}});
16
+ offset+=1;send({event_type:'tool.result',tool:'coding_file_write',request_id:'r176-unknown',outcome:{status:'interrupted',side_effect_state:'unknown',reason:'result unavailable after dispatch'}});
17
+ for(const l of tracker.reconcileInterrupted())process.stdout.write(l+'\n');
18
+ process.stdout.write(tracker.summary()+'\n');
@@ -0,0 +1,36 @@
1
+ $ErrorActionPreference='Stop'
2
+ $state=Join-Path $HOME '.deadbyte-mcp'
3
+ $trust=Join-Path $state 'trust'
4
+ $pub=Join-Path $trust 'r70-owner-public.pem'
5
+ Write-Output ('R177_TRUST_DIR_EXISTS='+(Test-Path -LiteralPath $trust -PathType Container))
6
+ Write-Output ('R177_OWNER_PUBLIC_KEY_EXISTS='+(Test-Path -LiteralPath $pub -PathType Leaf))
7
+ if(Test-Path -LiteralPath $pub -PathType Leaf){
8
+ $entry=Get-Item -LiteralPath $pub
9
+ $acl=Get-Acl -LiteralPath $pub
10
+ Write-Output ('R177_PUBLIC_KEY_BYTES='+$entry.Length)
11
+ Write-Output ('R177_PUBLIC_KEY_SHA256='+(Get-FileHash -LiteralPath $pub -Algorithm SHA256).Hash.ToLowerInvariant())
12
+ Write-Output ('R177_PUBLIC_KEY_OWNER='+$acl.Owner)
13
+ Write-Output ('R177_PUBLIC_KEY_READONLY='+$entry.IsReadOnly)
14
+ }
15
+ $activePath=Join-Path $state 'active-release.json'
16
+ if(Test-Path $activePath){
17
+ $a=Get-Content -LiteralPath $activePath -Raw|ConvertFrom-Json
18
+ Write-Output ('R177_ACTIVE_VERSION='+$a.version)
19
+ Write-Output ('R177_ACTIVE_RELEASE='+$a.release_id)
20
+ $codingFile=Join-Path $a.generation_root 'coding-policy.json'
21
+ if(Test-Path $codingFile){
22
+ $policy=Get-Content -LiteralPath $codingFile -Raw|ConvertFrom-Json
23
+ foreach($root in $policy.roots.PSObject.Properties){
24
+ Write-Output ('R177_CODING_ROOT='+$root.Name+' exec='+[bool]$root.Value.exec+' write='+[bool]$root.Value.write)
25
+ }
26
+ }
27
+ }
28
+ $evidence=Join-Path $state 'evidence'
29
+ if(Test-Path -LiteralPath $evidence){
30
+ Get-ChildItem -LiteralPath $evidence -Recurse -File -ErrorAction SilentlyContinue|
31
+ Where-Object { $_.Name -match 'owner|approval|enrollment' }|
32
+ Sort-Object LastWriteTimeUtc -Descending|
33
+ Select-Object -First 12 |
34
+ ForEach-Object{Write-Output ('R177_OWNER_EVIDENCE_NAME='+$_.Name+' size='+$_.Length+' utc='+$_.LastWriteTimeUtc.ToString('o'))}
35
+ }
36
+ Write-Output 'R177_READ_ONLY_COMPLETE=true'
@@ -0,0 +1,21 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import {readdirSync,mkdirSync,writeFileSync,readFileSync} from 'node:fs';
3
+ import {createHash} from 'node:crypto';
4
+ import path from 'node:path';
5
+ const root=path.resolve(import.meta.dirname,'..');
6
+ const testRoot=path.join(root,'test');
7
+ const files=readdirSync(testRoot).filter(n=>n.endsWith('.test.mjs')).sort().map(n=>path.join('test',n));
8
+ const outDir=path.resolve(root,'..','..','testing','r178-01569-candidate');
9
+ mkdirSync(outDir,{recursive:true});
10
+ const args=['--test','--test-concurrency=4',...files];
11
+ console.log('R178_TOTAL_TEST_FILES='+files.length);
12
+ const result=spawnSync(process.execPath,args,{cwd:root,encoding:'utf8',windowsHide:true,timeout:300000,maxBuffer:40*1024*1024,
13
+ env:{...process.env,DEADBYTE_RELEASE_TEST:'1'}});
14
+ const log=String(result.stdout||'')+'\n=== STDERR ===\n'+String(result.stderr||'');
15
+ const fp=path.join(outDir,'R178-FULL-REGRESSION.txt');
16
+ writeFileSync(fp,log);
17
+ console.log('R178_FULL_EXIT='+result.status+' signal='+result.signal+' error='+String(result.error?.code??'none'));
18
+ for(const l of log.split(/\r?\n/).filter(s=>/^(ℹ (tests|pass|fail|skipped|duration_ms)|# (tests|pass|fail|skipped|duration_ms)|✖|not ok)/.test(s)).slice(-30))console.log(l.slice(0,300));
19
+ console.log('R178_EVIDENCE='+fp);
20
+ console.log('R178_EVIDENCE_SHA256='+createHash('sha256').update(readFileSync(fp)).digest('hex'));
21
+ if(result.status!==0)process.exitCode=1;
@@ -0,0 +1,10 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import path from 'node:path';
3
+ const root=path.resolve(import.meta.dirname,'..');
4
+ const npm=process.env.npm_execpath||path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
5
+ const r=spawnSync(process.execPath,[npm,'ci','--ignore-scripts','--no-audit','--no-fund','--prefer-offline'],{cwd:root,encoding:'utf8',timeout:180000,windowsHide:true,maxBuffer:1024*1024});
6
+ console.log('NPM_CI_EXIT='+r.status);
7
+ if(r.error)console.log('NPM_CI_ERROR='+String(r.error.code));
8
+ console.log('NPM_CI_STDOUT='+String(r.stdout||'').slice(-1500));
9
+ console.log('NPM_CI_STDERR='+String(r.stderr||'').slice(-1500));
10
+ if(r.status!==0)process.exitCode=1;
@@ -0,0 +1,16 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import {existsSync} from 'node:fs';
3
+ import path from 'node:path';
4
+ const cwd=path.resolve(import.meta.dirname,'..');
5
+ const npm=process.env.npm_execpath||path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
6
+ function exec(args,ms=30000){
7
+ const r=spawnSync(process.execPath,[npm,...args],{cwd,encoding:'utf8',windowsHide:true,timeout:ms,env:{...process.env,npm_config_update_notifier:'false'}});
8
+ const s=String(r.stdout||'').trim(),e=String(r.stderr||'').trim();
9
+ return {ok:r.status===0,exit:r.status,error:r.error?.code??null,
10
+ output:args[0]==='whoami'?(r.status===0?s:'[NOT_AUTHENTICATED]'):String((s||e).slice(0,500)).replace(/https?:\/\/[^\s]+/g,'[url-redacted]')};
11
+ }
12
+ console.log('NODE='+process.version+' npm_cli_available='+existsSync(npm));
13
+ console.log('node_modules_present='+existsSync(path.join(cwd,'node_modules')));
14
+ console.log('NPM_VERSION='+JSON.stringify(exec(['--version'])));
15
+ console.log('NPM_WHOAMI='+JSON.stringify(exec(['whoami'])));
16
+ console.log('NPM_01569_REGISTRY='+JSON.stringify(exec(['view','deadbyte-mcp@0.15.69','version','--json'])));
@@ -0,0 +1,9 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import path from 'node:path';
3
+ const root=path.resolve(import.meta.dirname,'..');
4
+ for(const args of [['scripts/release-manifest.mjs','verify'],['scripts/npm-release-r48.mjs','--verify-only']]){
5
+ const r=spawnSync(process.execPath,args,{cwd:root,encoding:'utf8',timeout:30000,windowsHide:true,maxBuffer:2*1024*1024});
6
+ console.log('R178_CHECK='+args.join(' ')+' EXIT='+r.status);
7
+ const out=String(r.stdout||'')+'\n'+String(r.stderr||'');
8
+ for(const line of out.split(/\r?\n/).filter(x=>/MANIFEST_|FAIL|release identity|version mismatch|not published|PROVENANCE|PUBLISH|READY|error:/i.test(x)).slice(0,12))console.log(line.slice(0,500));
9
+ }
@@ -0,0 +1,25 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import {readdirSync,mkdirSync,writeFileSync,readFileSync} from 'node:fs';
3
+ import {createHash} from 'node:crypto';
4
+ import path from 'node:path';
5
+ const root=path.resolve(import.meta.dirname,'..');
6
+ const testRoot=path.join(root,'test');
7
+ const files=readdirSync(testRoot).filter(n=>n.endsWith('.test.mjs')).sort();
8
+ const size=Number(process.argv[3]??16),index=Number(process.argv[2]);
9
+ if(!Number.isInteger(index)||index<0||!Number.isInteger(size)||size<1||size>32)throw Error('invalid shard');
10
+ const selection=files.slice(index*size,(index+1)*size);
11
+ if(!selection.length)throw Error('empty shard');
12
+ const outDir=path.resolve(root,'..','..','testing','r178-01569-candidate');
13
+ mkdirSync(outDir,{recursive:true});
14
+ const testArgs=['--test','--test-concurrency=3',...selection.map(n=>path.join('test',n))];
15
+ const result=spawnSync(process.execPath,testArgs,{cwd:root,encoding:'utf8',windowsHide:true,
16
+ timeout:26000,maxBuffer:12*1024*1024});
17
+ const log=String(result.stdout||'')+'\n=== STDERR ===\n'+String(result.stderr||'');
18
+ const fp=path.join(outDir,'shard-'+String(index).padStart(2,'0')+'.log');
19
+ writeFileSync(fp,log);
20
+ const sha=createHash('sha256').update(readFileSync(fp)).digest('hex');
21
+ const stat=log.split(/\r?\n/).filter(x=>/^(ℹ (tests|pass|fail|skipped|duration_ms)|# (tests|pass|fail|skipped|duration_ms)|✖|not ok)/.test(x));
22
+ console.log('R178_SHARD='+index+' FILES='+selection.length+' EXIT='+result.status+' TIMEOUT='+Boolean(result.error?.code==='ETIMEDOUT'));
23
+ console.log(stat.slice(-8).map(x=>x.slice(0,300)).join('\n'));
24
+ console.log('EVIDENCE='+fp+' SHA256='+sha);
25
+ if(result.status!==0)process.exitCode=1;
@@ -0,0 +1,24 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import {mkdtempSync,readdirSync,readFileSync,rmSync} from 'node:fs';
3
+ import {tmpdir} from 'node:os';
4
+ import {createHash} from 'node:crypto';
5
+ import path from 'node:path';
6
+ const npm=process.env.npm_execpath||path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
7
+ function run(args,timeout=80000){const p=spawnSync(process.execPath,[npm,...args],{encoding:'utf8',shell:false,windowsHide:true,timeout,maxBuffer:3*1024*1024});if(p.error||p.status!==0)throw Error('npm read-only '+args[0]+' failed '+(p.stderr||p.error));return p.stdout.trim();}
8
+ const dir=mkdtempSync(path.join(tmpdir(),'deadbyte-r179-source-'));
9
+ try {
10
+ const metadata=JSON.parse(run(['view','deadbyte-mcp@0.15.68','dist','--json']));
11
+ console.log('PREDECESSOR_DIST_TARBALL='+JSON.stringify(metadata.tarball));
12
+ console.log('PREDECESSOR_DIST_SHASUM='+metadata.shasum);
13
+ const info=JSON.parse(run(['pack','deadbyte-mcp@0.15.68','--pack-destination',dir,'--ignore-scripts','--json']));
14
+ if(!Array.isArray(info)||info.length!==1)throw Error('parent npm pack response shape invalid');
15
+ const file=path.join(dir,info[0].filename);
16
+ const bytes=readFileSync(file);
17
+ const sha256=createHash('sha256').update(bytes).digest('hex');
18
+ const sha1=createHash('sha1').update(bytes).digest('hex');
19
+ if(sha1!==metadata.shasum)throw Error('parent npm registry SHA-1 identity mismatch');
20
+ console.log('PREDECESSOR_VERSION=0.15.68');
21
+ console.log('PREDECESSOR_TARBALL_SHA256='+sha256);
22
+ console.log('PREDECESSOR_PACKED_BYTES='+bytes.length);
23
+ console.log('PREDECESSOR_REGISTRY_MATCH=PASS');
24
+ }finally{rmSync(dir,{recursive:true,force:true});}
@@ -0,0 +1,21 @@
1
+ $ErrorActionPreference='Stop'
2
+ $root='C:\Program Files'
3
+ $paths=@(
4
+ 'C:\Program Files\DEADBYTE-R153-Protected-01560-Test',
5
+ 'C:\Program Files\DEADBYTE-R168-Protected-Test',
6
+ 'C:\Program Files\DEADBYTE-R174-Test'
7
+ )
8
+ if(Test-Path -LiteralPath $root){
9
+ Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |
10
+ Where-Object {$_.Name -match 'DEADBYTE|R174|R175|R178'} |
11
+ Select-Object -First 25 | ForEach-Object {
12
+ $package=Join-Path $_.FullName 'package.json'
13
+ $ver='missing';if(Test-Path -LiteralPath $package){try{$ver=(Get-Content -Raw -LiteralPath $package|ConvertFrom-Json).version}catch{$ver='invalid'}}
14
+ $owner=(Get-Acl -LiteralPath $_.FullName).Owner
15
+ Write-Output ('PROTECTED_CANDIDATE='+$_.FullName+' version='+$ver+' owner='+$owner)
16
+ }
17
+ }
18
+ $state=Join-Path $HOME '.deadbyte-mcp'
19
+ $a=Get-Content (Join-Path $state 'active-release.json') -Raw|ConvertFrom-Json
20
+ Write-Output ('ACTIVE='+$a.release_id+' version='+$a.version)
21
+ Write-Output ('R179_READONLY_COMPLETE=TRUE')
@@ -0,0 +1,22 @@
1
+ import {spawnSync} from 'node:child_process';
2
+ import {createHash} from 'node:crypto';
3
+ import {readFileSync,writeFileSync,existsSync} from 'node:fs';
4
+ import path from 'node:path';
5
+ const root=path.resolve(import.meta.dirname,'..');
6
+ const dir=path.resolve(root,'..','..','testing','r186-owner-consent');
7
+ const tests=['test/r186-owner-consent-preflight.test.mjs','test/r186-owner-request.test.mjs',
8
+ 'test/r70-owner-approval.test.mjs',
9
+ 'test/r70-controller-owner-issue-boundary.test.mjs',
10
+ 'test/r70-autonomous-grant-expiry.test.mjs',
11
+ 'test/r174-owner-webauthn.test.mjs'];
12
+ const result=spawnSync(process.execPath,['--test','--test-concurrency=2',...tests],{
13
+ cwd:root,encoding:'utf8',windowsHide:true,timeout:60000,maxBuffer:2*1024*1024});
14
+ const s=String(result.stdout??'')+'\n'+String(result.stderr??'');
15
+ const sha=createHash('sha256').update(s).digest('hex');
16
+ const output=path.join(dir,'R186-OWNER-FOCUSED-V2-20261028.log');
17
+ if(existsSync(output))throw Error('R186_FOCUSED_EVIDENCE_ALREADY_EXISTS');
18
+ writeFileSync(output,s,{flag:'wx'});
19
+ for(const line of s.split(/\r?\n/).filter(x=>/^(✔|✖|ℹ (tests|pass|fail|skipped|cancelled))/.test(x)).slice(-45))
20
+ console.log(line);
21
+ console.log('R186_FOCUSED_EXIT='+result.status+' LOG_SHA256='+sha);
22
+ if(result.status!==0)process.exitCode=1;
@@ -0,0 +1,6 @@
1
+ import {readFile} from 'node:fs/promises';
2
+ import {createR186OwnerAnchor} from '../src/r186-owner-anchor.mjs';
3
+ const [file]=process.argv.slice(2);
4
+ if(process.argv.slice(2).length!==1)throw new Error('usage: r186-owner-anchor-inspect.mjs <owner-public.pem>');
5
+ const out=createR186OwnerAnchor(await readFile(file,'utf8'));
6
+ process.stdout.write(JSON.stringify(out));
@@ -0,0 +1,86 @@
1
+ // R186 read-only inspection of independently enrolled owner authorization.
2
+ // No key enrollment, challenge consumption, grant or policy mutation occurs here.
3
+ // This CLI MUST run from an R89-protected exact release. Any agent-writable
4
+ // source tree or owner public key path is rejected by the native OS ACL audit.
5
+ import {createHash} from 'node:crypto';
6
+ import {readFile,lstat} from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import {fileURLToPath} from 'node:url';
9
+ import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
10
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
11
+ import {verifyR186OwnerConsent,verifyR186PolicyRootBinding} from '../src/r186-owner-consent-preflight.mjs';
12
+
13
+ const THIS=fileURLToPath(import.meta.url);
14
+ const ROOT=path.resolve(path.dirname(THIS),'..');
15
+ const OWNER_DIR='C:\\Program Files\\DEADBYTE-OwnerTrust';
16
+ const KEY=path.win32.join(OWNER_DIR,'owner-public.pem');
17
+ const ANCHOR=path.win32.join(OWNER_DIR,'owner-anchor.json');
18
+ const SHA=/^[0-9a-f]{64}$/;
19
+ const sha=b=>createHash('sha256').update(b).digest('hex');
20
+ const reject=m=>{throw Error('R186_OWNER_PREFLIGHT_BLOCKED: '+m)};
21
+ async function bounded(file,limit){
22
+ const st=await lstat(file);
23
+ if(!st.isFile()||st.isSymbolicLink()||st.size<1||st.size>limit)reject('unsafe input file');
24
+ return readFile(file);
25
+ }
26
+ async function main(){
27
+ if(process.platform!=='win32'||process.arch!=='x64')reject('requires Windows x64');
28
+ // No dynamic owner key paths. Owner trust must have been installed by the
29
+ // physical owner through an independently approved protected workflow.
30
+ const args=process.argv.slice(2);
31
+ if(args.length!==5)reject('usage: <approval-json> <root-id> <project-root> <coding-policy-json> <autonomous-policy-json>');
32
+ const [approvalPath,rootId,projectRoot,codingPolicyPath,autonomousPolicyPath]=args;
33
+ if(![approvalPath,projectRoot,codingPolicyPath,autonomousPolicyPath].every(p=>path.win32.isAbsolute(p)))
34
+ reject('absolute paths required');
35
+ if(!/^[A-Za-z0-9._-]{1,64}$/.test(rootId))reject('root identity invalid');
36
+ // Do not trust agent-provided signature keys. R89 rejects writable ancestor,
37
+ // unsafe owner and any writable ACE on the pin or enrolled public key.
38
+ const trustedFiles=[
39
+ process.execPath,THIS,ANCHOR,KEY,
40
+ path.join(ROOT,'MANIFEST.SHA256'),
41
+ path.join(ROOT,'src','r186-owner-consent-preflight.mjs'),
42
+ path.join(ROOT,'src','r70-owner-approval.mjs'),
43
+ path.join(ROOT,'src','canonical-json.mjs'),
44
+ path.join(ROOT,'src','release-slot.mjs'),
45
+ path.join(ROOT,'src','privileged-install-trust-r89.mjs')
46
+ ];
47
+ const os=await inspectTrustedInstallation(trustedFiles);
48
+ if(!os.trusted)reject('owner trust ACL untrusted');
49
+ const manifestBytes=await bounded(path.join(ROOT,'MANIFEST.SHA256'),512*1024);
50
+ const manifest=parseManifest(manifestBytes.toString('utf8'));
51
+ await verifyManifestTreeExact(ROOT,manifest);
52
+ const codingBytes=await bounded(codingPolicyPath,1024*1024);
53
+ const autonomousBytes=await bounded(autonomousPolicyPath,1024*1024);
54
+ const binding=verifyR186PolicyRootBinding({
55
+ codingPolicy:JSON.parse(codingBytes.toString('utf8')),
56
+ autonomousPolicy:JSON.parse(autonomousBytes.toString('utf8')),
57
+ rootId,projectRoot,codingPolicySha256:sha(codingBytes)
58
+ });
59
+ const [approvalBuf,trustBuf,keyBuf]=await Promise.all([
60
+ bounded(approvalPath,64*1024),bounded(ANCHOR,4*1024),bounded(KEY,8*1024)
61
+ ]);
62
+ const decision=await verifyR186OwnerConsent({
63
+ ownerTrust:JSON.parse(trustBuf.toString('utf8')),
64
+ ownerPublicKeyPem:keyBuf.toString('utf8'),
65
+ approval:JSON.parse(approvalBuf.toString('utf8')),
66
+ expected:{
67
+ rootId:binding.root_id,projectRoot:binding.project_root,
68
+ codingPolicySha256:sha(codingBytes),
69
+ autonomousPolicySha256:sha(autonomousBytes),
70
+ reviewedArtifactSha256:sha(manifestBytes)
71
+ }
72
+ });
73
+ console.log(JSON.stringify({
74
+ schema:'deadbyte.r186-owner-preflight.v1',
75
+ status:decision.status,
76
+ authority_granted:false,
77
+ owner_key_id:decision.owner_key_id,
78
+ subject_sha256:decision.subject_sha256,
79
+ release_manifest_sha256:sha(manifestBytes),
80
+ os_acl_trusted:true,
81
+ project_root:decision.project_root,
82
+ expires_at_utc:decision.expires_at_utc,
83
+ next_gate:'OWNER_APPROVAL_SINGLE_USE_CONSUMPTION_AND_SIGNED_EPOCH_ISSUANCE'
84
+ }));
85
+ }
86
+ main().catch(e=>{console.error('R186_OWNER_PREFLIGHT_BLOCKED '+String(e?.message??e).slice(0,300));process.exitCode=2;});
@@ -0,0 +1,45 @@
1
+ // Protected, fixed-scope R186 grant broker entrypoint. The controller chooses
2
+ // neither a trust key nor an approval path; both are fixed by the contract.
3
+ import {createHash} from 'node:crypto';
4
+ import {lstat,readFile} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {fileURLToPath} from 'node:url';
7
+ import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
8
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
9
+ import {issueR186AutonomousGrant} from '../src/r186-owner-grant-broker.mjs';
10
+
11
+ const THIS=fileURLToPath(import.meta.url),ROOT=path.resolve(path.dirname(THIS),'..');
12
+ const OWNER_DIR='C:\\Program Files\\DEADBYTE-OwnerTrust';
13
+ const KEY=path.win32.join(OWNER_DIR,'owner-public.pem');
14
+ const ANCHOR=path.win32.join(OWNER_DIR,'owner-anchor.json');
15
+ const sha=b=>createHash('sha256').update(b).digest('hex');
16
+ const block=m=>{throw new Error(`R186_OWNER_GRANT_BLOCKED: ${m}`);};
17
+ async function bounded(file,limit){const st=await lstat(file);if(!st.isFile()||st.isSymbolicLink()||st.size<1||st.size>limit)block('unsafe input file');return readFile(file);}
18
+ async function main(){
19
+ if(process.platform!=='win32'||process.arch!=='x64')block('requires Windows x64');
20
+ const [runtimeDir,codingPolicyPath,autonomousPolicyPath]=process.argv.slice(2);
21
+ if(process.argv.slice(2).length!==3||![runtimeDir,codingPolicyPath,autonomousPolicyPath].every(p=>path.win32.isAbsolute(p)))
22
+ block('usage: <runtime-dir> <coding-policy-json> <autonomous-policy-json>');
23
+ const approvalPath=path.join(runtimeDir,'owner-consent','autonomous-approval.json');
24
+ const protectedFiles=[process.execPath,THIS,ANCHOR,KEY,path.join(ROOT,'MANIFEST.SHA256'),
25
+ path.join(ROOT,'src','r186-owner-grant-broker.mjs'),path.join(ROOT,'src','r186-owner-consent-preflight.mjs'),
26
+ path.join(ROOT,'src','r70-owner-approval.mjs'),path.join(ROOT,'src','authority-continuity-r65.mjs'),
27
+ path.join(ROOT,'src','epoch-lock-r85.mjs'),path.join(ROOT,'bin','deadbyte-epoch-lock-r85.node')];
28
+ const trust=await inspectTrustedInstallation(protectedFiles);
29
+ if(!trust.trusted)block('protected package or owner trust ACL untrusted');
30
+ const manifestBytes=await bounded(path.join(ROOT,'MANIFEST.SHA256'),512*1024);
31
+ await verifyManifestTreeExact(ROOT,parseManifest(manifestBytes.toString('utf8')));
32
+ const [codingBytes,autonomousBytes,approvalBytes,anchorBytes,keyBytes]=await Promise.all([
33
+ bounded(codingPolicyPath,1024*1024),bounded(autonomousPolicyPath,1024*1024),bounded(approvalPath,64*1024),
34
+ bounded(ANCHOR,4096),bounded(KEY,8192)]);
35
+ const autonomous=JSON.parse(autonomousBytes.toString('utf8'));
36
+ const result=await issueR186AutonomousGrant({runtimeDir,grantFile:autonomous.lease_file,
37
+ codingPolicy:JSON.parse(codingBytes.toString('utf8')),codingPolicyBytes:codingBytes,
38
+ autonomousPolicy:autonomous,autonomousPolicyBytes:autonomousBytes,releaseManifestBytes:manifestBytes,
39
+ ownerTrust:JSON.parse(anchorBytes.toString('utf8')),ownerPublicKeyPem:keyBytes.toString('utf8'),
40
+ approval:JSON.parse(approvalBytes.toString('utf8'))});
41
+ if(sha(await readFile(codingPolicyPath))!==sha(codingBytes)||sha(await readFile(autonomousPolicyPath))!==sha(autonomousBytes))
42
+ block('policy changed during grant transaction; outcome requires reconciliation');
43
+ process.stdout.write(JSON.stringify(result));
44
+ }
45
+ main().catch(error=>{console.error(String(error?.message??error).slice(0,500));process.exitCode=2;});
@@ -0,0 +1,22 @@
1
+ // Owner-operated only. Do not invoke from an agent workflow.
2
+ import {generateKeyPairSync} from 'node:crypto';
3
+ import {mkdir,open,readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {createR186OwnerAnchor} from '../src/r186-owner-anchor.mjs';
6
+ import {createR186OwnerApproval} from '../src/r186-owner-local-signing.mjs';
7
+
8
+ function args(values){const out={};for(let i=0;i<values.length;i+=2){if(!values[i]?.startsWith('--')||!values[i+1])throw new Error('invalid arguments');out[values[i].slice(2)]=path.resolve(values[i+1]);}return out;}
9
+ async function exclusive(file,data,mode=0o600){await mkdir(path.dirname(file),{recursive:true});const h=await open(file,'wx',mode);try{await h.writeFile(data);await h.sync();}finally{await h.close();}}
10
+ const [command,...rest]=process.argv.slice(2),opt=args(rest);
11
+ if(command==='keygen'){
12
+ if(!opt['private-out']||!opt['public-out'])throw new Error('usage: keygen --private-out PATH --public-out PATH');
13
+ const pair=generateKeyPairSync('ed25519'),privatePem=pair.privateKey.export({type:'pkcs8',format:'pem'}).toString();
14
+ const material=createR186OwnerAnchor(pair.publicKey.export({type:'spki',format:'pem'}).toString());
15
+ await exclusive(opt['private-out'],privatePem,0o600);await exclusive(opt['public-out'],material.public_key_pem,0o644);
16
+ console.log(`R186_OWNER_KEY_CREATED public=${opt['public-out']} owner_key_id=${material.anchor.owner_key_id}`);
17
+ }else if(command==='sign'){
18
+ if(!opt.request||!opt['private-key']||!opt['approval-out'])throw new Error('usage: sign --request PATH --private-key PATH --approval-out PATH');
19
+ const approval=createR186OwnerApproval({request:JSON.parse(await readFile(opt.request,'utf8')),privateKeyPem:await readFile(opt['private-key'],'utf8')});
20
+ await exclusive(opt['approval-out'],Buffer.from(JSON.stringify(approval,null,2)+'\n'),0o600);
21
+ console.log(`R186_OWNER_APPROVAL_CREATED approval=${opt['approval-out']} subject_sha256=${approval.subject_sha256} owner_key_id=${approval.owner_key_id}`);
22
+ }else throw new Error('usage: r186-owner-key-local.mjs <keygen|sign> ...');
@@ -0,0 +1,34 @@
1
+ // Protected exact-byte review-request export. No owner key or grant is used.
2
+ import path from 'node:path';
3
+ import {fileURLToPath} from 'node:url';
4
+ import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
5
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
6
+ import {writeR186OwnerReviewRequest} from '../src/r186-owner-review-export.mjs';
7
+ import {readFile} from 'node:fs/promises';
8
+
9
+ const THIS=fileURLToPath(import.meta.url),ROOT=path.resolve(path.dirname(THIS),'..');
10
+ const fail=message=>{throw new Error(`R186_OWNER_REVIEW_EXPORT_BLOCKED: ${message}`);};
11
+ async function main(){
12
+ if(process.platform!=='win32'||process.arch!=='x64')fail('requires Windows x64');
13
+ const [rootId,projectRoot,codingPolicyPath,autonomousPolicyPath,outputPath]=process.argv.slice(2);
14
+ if(process.argv.slice(2).length!==5||
15
+ ![projectRoot,codingPolicyPath,autonomousPolicyPath,outputPath].every(value=>path.win32.isAbsolute(value)))
16
+ fail('usage: <root-id> <project-root> <coding-policy-json> <autonomous-policy-json> <output-json>');
17
+ const manifestPath=path.join(ROOT,'MANIFEST.SHA256');
18
+ const protectedFiles=[process.execPath,THIS,manifestPath,
19
+ path.join(ROOT,'src','r186-owner-review-export.mjs'),
20
+ path.join(ROOT,'src','r186-owner-request.mjs'),
21
+ path.join(ROOT,'src','r186-owner-consent-preflight.mjs'),
22
+ path.join(ROOT,'src','canonical-json.mjs'),
23
+ path.join(ROOT,'src','privileged-install-trust-r89.mjs'),
24
+ path.join(ROOT,'src','release-slot.mjs')];
25
+ const trust=await inspectTrustedInstallation(protectedFiles);
26
+ if(!trust.trusted)fail('protected package ACL untrusted');
27
+ const manifestBytes=await readFile(manifestPath);
28
+ await verifyManifestTreeExact(ROOT,parseManifest(manifestBytes.toString('utf8')));
29
+ const request=await writeR186OwnerReviewRequest({rootId,projectRoot,codingPolicyPath,
30
+ autonomousPolicyPath,manifestPath,outputPath});
31
+ process.stdout.write(JSON.stringify({status:'REVIEW_REQUEST_EXPORTED',authority_granted:false,
32
+ owner_signature_present:false,subject_sha256:request.subject_sha256,output_path:outputPath}));
33
+ }
34
+ main().catch(error=>{console.error(String(error?.message??error).slice(0,500));process.exitCode=2;});
@@ -1,6 +1,7 @@
1
1
  import {createHash} from 'node:crypto';
2
2
  import {lstat,open,realpath} from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
4
5
 
5
6
  const FIELDS=Object.freeze({process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'});
6
7
  const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
@@ -22,7 +23,9 @@ async function readRegularPinned(file,label){
22
23
  }finally{await handle.close();}
23
24
  }
24
25
 
25
- export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority}){
26
+ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority,
27
+ epochStoreFactory=createAuthorityEpochStore}){
28
+ if(typeof epochStoreFactory!=='function')throw new Error('authority epoch store factory invalid');
26
29
  const runtimeRoot=await realpath(path.join(stateRoot,'runtime'));
27
30
  const result={};
28
31
  for(const [plane,field] of Object.entries(FIELDS)){
@@ -49,11 +52,31 @@ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha2
49
52
  const grantAbsent=grantInfo===undefined;
50
53
  if(grantInfo&&(!grantInfo.isFile()||grantInfo.isSymbolicLink()))throw new Error(`${plane} grant file type invalid`);
51
54
  const validEntry=entry&&entry.configured===true&&typeof entry.armed==='boolean';
52
- const validDisarmed=validEntry&&!entry.armed&&grantAbsent&&
53
- (entry.reason==='disarmed'||(plane==='autonomous'&&entry.reason==='coding-disarmed'));
55
+ const grantBytes=grantInfo?await readRegularPinned(checkedPath,`${plane} grant`):null;
56
+ const grantSha=grantBytes?sha(grantBytes):null;
57
+ let transactionRevoked=false;
58
+ if(validEntry&&!entry.armed&&entry.reason==='epoch-revoked'&&grantBytes){
59
+ let grant;
60
+ try{grant=JSON.parse(grantBytes.toString('utf8').replace(/^\uFEFF/,''));}catch{}
61
+ const nonce=grant?.instance_nonce;
62
+ if(typeof nonce==='string'&&/^[0-9a-f]{64}$/.test(nonce)){
63
+ const store=epochStoreFactory({runtimeDir:parent,policyBytes});
64
+ const [audit,head,verification]=await Promise.all([
65
+ store.auditHistory({plane}),store.readHead({plane}),
66
+ store.verifyGrant({plane,grantBytes,grantInstanceNonce:nonce,policyBytes})
67
+ ]);
68
+ transactionRevoked=audit?.status==='PASS'&&Number.isSafeInteger(audit.verified_events)&&audit.verified_events>0&&
69
+ head?.action==='transaction_revoke'&&Number.isSafeInteger(head.seq)&&head.seq>0&&
70
+ /^[0-9a-f]{64}$/.test(String(head.event_sha256??''))&&
71
+ head.policy_sha256===expectedHash&&head.grant_sha256===grantSha&&head.grant_instance_nonce===nonce&&
72
+ /^r65-[0-9a-f]{32}$/.test(String(head.update_id??''))&&verification?.trusted===false&&
73
+ verification?.reason==='epoch-revoked'&&verification?.event_sha256===head.event_sha256;
74
+ }
75
+ }
76
+ const validDisarmed=validEntry&&!entry.armed&&(
77
+ (grantAbsent&&(entry.reason==='disarmed'||(plane==='autonomous'&&entry.reason==='coding-disarmed')))||transactionRevoked);
54
78
  const validArmed=validEntry&&entry.armed&&!grantAbsent&&entry.reason==='standing-grant'&&
55
79
  Number.isSafeInteger(entry.epoch_seq)&&entry.epoch_seq>0&&/^[0-9a-f]{64}$/.test(entry.epoch_sha256??'');
56
- const grantSha=grantInfo?sha(await readRegularPinned(checkedPath,`${plane} grant`)):null;
57
80
  let grantFinal;
58
81
  try{grantFinal=await lstat(checkedPath);}
59
82
  catch(error){if(error?.code!=='ENOENT')throw error;}
@@ -61,6 +84,7 @@ export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha2
61
84
  throw new Error(`${plane} grant identity changed during observation`);
62
85
  result[plane]=Object.freeze({state:validDisarmed?'VERIFIED_DISARMED':validArmed?'VERIFIED_ARMED':'UNVERIFIED',
63
86
  policy_sha256:expectedHash,grant_absent:grantAbsent,
87
+ ...(transactionRevoked?{transaction_revoked:true}:{}),
64
88
  ...(grantSha?{grant_sha256:grantSha}:{})});
65
89
  }
66
90
  return Object.freeze(result);