deadbyte-mcp 0.15.65 → 0.15.66

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 (57) hide show
  1. package/MANIFEST.SHA256 +56 -19
  2. package/PROVENANCE.json +19 -7
  3. package/README.txt +31 -18
  4. package/RELEASE.json +25 -7
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +32 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-process-policy.json +4 -4
  9. package/docs/R168-ACTIVATION-RECONCILIATION-BLOCKER-20261026.md +39 -0
  10. package/docs/R168-CRASH-JOURNAL-PROTECTED-PREFLIGHT-20261026.md +46 -0
  11. package/docs/R168-FIVE-PLANE-FENCE-20261026.md +41 -0
  12. package/docs/R168-NATIVE-EXCLUSION-TERMINAL-CONTRACT-20261026.md +53 -0
  13. package/docs/R168-NPX-REMOTE-SKEW-HOTFIX-20261026.md +41 -0
  14. package/docs/R168-READONLY-AUDIT-AND-RESUME-20261026.md +56 -0
  15. package/docs/R168-SIGNED-RECONCILIATION-PRECOMMIT-20261026.md +45 -0
  16. package/package.json +4 -2
  17. package/scripts/gate-windows-r168.ps1 +75 -0
  18. package/scripts/npm-release-r48.mjs +23 -3
  19. package/scripts/npm-release-readiness-r168.mjs +70 -0
  20. package/scripts/r168-audit-readonly.mjs +32 -0
  21. package/scripts/r168-npm-registry-readonly.mjs +19 -0
  22. package/scripts/r168-protected-preflight.mjs +65 -0
  23. package/scripts/r168-release-scan.mjs +28 -0
  24. package/scripts/r168-source-full-test-runner.mjs +46 -0
  25. package/scripts/r168-subject-readonly.mjs +49 -0
  26. package/src/activation-reconciliation-audit-r168.mjs +200 -0
  27. package/src/activation-reconciliation-consent-r168.mjs +235 -0
  28. package/src/activation-reconciliation-fence-r168.mjs +87 -0
  29. package/src/activation-reconciliation-journal-r168.mjs +100 -0
  30. package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
  31. package/src/activation-reconciliation-terminal-r168.mjs +111 -0
  32. package/src/deadbyte-cli.mjs +20 -0
  33. package/src/npm-publication-resume-r168.mjs +25 -0
  34. package/src/remote-one-shot-coherence-r168.mjs +36 -0
  35. package/src/version.mjs +1 -1
  36. package/test/r130-release-security-contract.test.mjs +7 -7
  37. package/test/r132-release-identity.test.mjs +7 -7
  38. package/test/r153-prepublish-identity.test.mjs +1 -1
  39. package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
  40. package/test/r165-live-cli-skew.test.mjs +7 -7
  41. package/test/r167-release-contract.test.mjs +10 -10
  42. package/test/r168-combined-targeted-runner.mjs +14 -0
  43. package/test/r168-crash-journal.test.mjs +149 -0
  44. package/test/r168-five-plane-fence-runner.mjs +11 -0
  45. package/test/r168-five-plane-fence.test.mjs +128 -0
  46. package/test/r168-focused-suite.test.mjs +4 -0
  47. package/test/r168-native-five-plane-runner.mjs +11 -0
  48. package/test/r168-native-five-plane-scratch.test.mjs +49 -0
  49. package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
  50. package/test/r168-npm-resume-identity.test.mjs +49 -0
  51. package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
  52. package/test/r168-protected-entrypoint.test.mjs +53 -0
  53. package/test/r168-reconciliation-audit.test.mjs +42 -0
  54. package/test/r168-release-contract.test.mjs +56 -0
  55. package/test/r168-remote-one-shot-coherence.test.mjs +65 -0
  56. package/test/r59-tool-surface-compat.test.mjs +1 -1
  57. package/test/r63-one-shot-coherence.test.mjs +10 -5
@@ -0,0 +1,65 @@
1
+ // R168 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ // This code must be part of a signed manifest in a protected Program Files install.
3
+ import {createHash} from 'node:crypto';
4
+ import {readFile} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {fileURLToPath} from 'node:url';
7
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
8
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
9
+
10
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
11
+ const version='0.15.66';
12
+ const expectedTrain='R168';
13
+ const mandatory=['scripts/r168-protected-preflight.mjs',
14
+ 'src/activation-reconciliation-journal-r168.mjs',
15
+ 'src/activation-reconciliation-consent-r168.mjs',
16
+ 'src/activation-reconciliation-terminal-r168.mjs',
17
+ 'src/activation-reconciliation-fence-r168.mjs',
18
+ 'src/activation-reconciliation-audit-r168.mjs',
19
+ 'src/activation-reconciliation-receipt-store-r168.mjs'];
20
+ function denied(reason){throw new Error('R168_'+reason);}
21
+ function actualProtectedRoot(){
22
+ const trusted='C:\\Program Files';
23
+ const rel=path.win32.relative(trusted,root);
24
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))
25
+ denied('ROOT_UNPROTECTED');
26
+ return root;
27
+ }
28
+ async function main(){
29
+ if(process.platform!=='win32'||process.arch!=='x64')
30
+ denied('WINDOWS_X64_REQUIRED');
31
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
32
+ const verifiedRoot=actualProtectedRoot();
33
+ const packageInfo=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
34
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
35
+ if(packageInfo.name!=='deadbyte-mcp'||packageInfo.version!==version||
36
+ release.name!=='deadbyte-mcp'||release.version!==version||release.train!==expectedTrain)
37
+ denied('VERSION_DRIFT');
38
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
39
+ const entries=parseManifest(manifest.toString('utf8'));
40
+ for(const name of mandatory){
41
+ if(!entries.has(name))denied('R168_SOURCE_NOT_IN_MANIFEST');
42
+ }
43
+ // The installed package is checked against its complete manifest, not a
44
+ // handpicked critical-file subset or a writable development source root.
45
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
46
+ const important=[
47
+ ...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
48
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
49
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),
50
+ path.win32.join(verifiedRoot,'RELEASE.json'),
51
+ path.win32.join(verifiedRoot,'package.json')
52
+ ];
53
+ // OS ACL trust is checked *after* content verification, under the protected
54
+ // root. No user-controlled environment may override this inventory.
55
+ await inspectTrustedInstallation(important);
56
+ const hash=createHash('sha256').update(manifest).digest('hex');
57
+ process.stdout.write(JSON.stringify({status:'R168_PROTECTED_PREFLIGHT_READONLY_PASS',
58
+ version,train:expectedTrain,manifest_sha256:hash,files:entries.size,
59
+ terminalization_authorized:false,activation_authorized:false})+'\n');
60
+ }
61
+ try{await main();}
62
+ catch(error){
63
+ process.stderr.write('R168_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');
64
+ process.exitCode=1;
65
+ }
@@ -0,0 +1,28 @@
1
+ // R168 bounded read-only scan of current version-bound source.
2
+ import {readdir,readFile,lstat} from 'node:fs/promises';
3
+ import {fileURLToPath} from 'node:url';
4
+ import path from 'node:path';
5
+ const root=fileURLToPath(new URL('..',import.meta.url));
6
+ let count=0, results=[];
7
+ const scopes=['test','src','scripts','controller','bridge'];
8
+ for(const scope of scopes){
9
+ const visit=async(dir,depth)=>{
10
+ if(depth>14)throw Error('scan depth exceeded');
11
+ const entries=await readdir(dir,{withFileTypes:true});
12
+ if(entries.length>3000)throw Error('inventory exceeds budget');
13
+ for(const e of entries){
14
+ if(e.isSymbolicLink())throw Error('symlink in bounded scan');
15
+ const file=path.join(dir,e.name),rel=path.relative(root,file).replaceAll('\\','/');
16
+ if(e.isDirectory()) {if(!['node_modules','.git','tmp'].includes(e.name))await visit(file,depth+1);continue}
17
+ if(!e.isFile()||!/(\.mjs|\.ps1|\.json)$/.test(e.name))continue;
18
+ const stats=await lstat(file);if(stats.size>300000)continue;
19
+ const text=await readFile(file,'utf8');
20
+ const lines=text.split(/\r?\n/);
21
+ const matches=lines.map((s,j)=>({line:j+1,s})).filter(q=>/0\.15\.65|R167.*version|R167_CANDIDATE_VERSION_DRIFT|r167:0\.15\.65|R167_STAGE_PASS=/.test(q.s));
22
+ if(matches.length)results.push({file:rel,lines:matches.slice(0,7)});
23
+ count++;
24
+ }
25
+ };
26
+ await visit(path.join(root,scope),0);
27
+ }
28
+ console.log(JSON.stringify({scannedFiles:count,matchedFiles:results.length,results},null,2));
@@ -0,0 +1,46 @@
1
+ // R168 Windows unprotected SOURCE gate. No publication, activation or claims.
2
+ import {spawnSync} from 'node:child_process';
3
+ import {createHash} from 'node:crypto';
4
+ import {closeSync,fsyncSync,openSync} from 'node:fs';
5
+ import {mkdir,readFile,writeFile} from 'node:fs/promises';
6
+ import {fileURLToPath} from 'node:url';
7
+ import path from 'node:path';
8
+
9
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
10
+ const outDir=path.resolve(root,'..','..','testing','r168-source-full-20261026-fix1');
11
+ const manifestBytes=await readFile(path.join(root,'MANIFEST.SHA256'));
12
+ const manifestHash=createHash('sha256').update(manifestBytes).digest('hex');
13
+ const fileCount=manifestBytes.toString('utf8').trimEnd().split(/\r?\n/).length;
14
+ const digest=b=>createHash('sha256').update(b).digest('hex');
15
+ const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
16
+ if(process.platform!=='win32'||release.version!=='0.15.66'||release.train!=='R168')
17
+ throw new Error('R168 Windows source release identity mismatch');
18
+ const manifestCheck=spawnSync(process.execPath,[path.join(root,'scripts','release-manifest.mjs'),'verify'],{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:180000});
19
+ if(manifestCheck.status!==0)throw new Error('R168 manifest verify failed '+String(manifestCheck.stderr??manifestCheck.stdout).slice(0,1200));
20
+ await mkdir(outDir,{recursive:true});
21
+ const stdoutPath=path.join(outDir,'windows-full.stdout.log');
22
+ const stderrPath=path.join(outDir,'windows-full.stderr.log');
23
+ let o=null,e=null;
24
+ try{
25
+ o=openSync(stdoutPath,'wx');e=openSync(stderrPath,'wx');
26
+ const cross=path.join(root,'..','..','testing','r168-crossvolume-scratch');
27
+ await mkdir(cross,{recursive:true});
28
+ const result=spawnSync(process.execPath,
29
+ ['--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs'],{
30
+ cwd:root,shell:false,windowsHide:true,stdio:['ignore',o,e],
31
+ timeout:480000,env:{...process.env,DEADBYTE_TEST_CROSS_VOLUME_ROOT:cross}
32
+ });
33
+ fsyncSync(o);fsyncSync(e);
34
+ closeSync(o);closeSync(e);o=e=null;
35
+ const full=await readFile(stdoutPath,'utf8');
36
+ const match=[...full.matchAll(/^# tests (\d+)\s*\r?\n# suites (\d+)\s*\r?\n# pass (\d+)\s*\r?\n# fail (\d+)\s*\r?\n# cancelled (\d+)\s*\r?\n# skipped (\d+)/gm)].at(-1);
37
+ const summary={status:result.status===0?'SOURCE_FULL_PASS':'SOURCE_FULL_FAIL',
38
+ exit_code:result.status,terminated_signal:result.signal??null,
39
+ files_checked:fileCount,manifest_sha256:manifestHash,
40
+ tests:match?Number(match[1]):null,passes:match?Number(match[3]):null,
41
+ failures:match?Number(match[4]):null,skipped:match?Number(match[6]):null,
42
+ stdout_sha256:digest(await readFile(stdoutPath)),stderr_sha256:digest(await readFile(stderrPath))};
43
+ console.log(JSON.stringify(summary,null,2));
44
+ if(result.error)throw result.error;
45
+ if(result.status!==0)process.exitCode=1;
46
+ }catch(error){if(o!==null)closeSync(o);if(e!==null)closeSync(e);throw error;}
@@ -0,0 +1,49 @@
1
+ // R168 read-only subject preparation. No signing keys or activation mutation.
2
+ import {readFile} from 'node:fs/promises';
3
+ import {createHash} from 'node:crypto';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {readPinnedEpochLockSnapshot} from '../src/foreground-marker-cas-win32.mjs';
7
+ import {inspectR168UnknownWithoutMutation} from '../src/activation-reconciliation-audit-r168.mjs';
8
+ import {createR168ReconciliationSubject,makeR168ConsentChallenge} from '../src/activation-reconciliation-consent-r168.mjs';
9
+
10
+ const sha=b=>createHash('sha256').update(b).digest('hex');
11
+ const sourceExpected={
12
+ 'activation-reconciliation-audit-r168.mjs':'467268f6f195e9359b819e35c8b418c81e1cb40c9ba04b990fd2656e354c65a5',
13
+ 'activation-reconciliation-consent-r168.mjs':'a82b05e1c521cc77f3398985d6faa1beb0fe8f65595f50bed474227550d462c5'
14
+ };
15
+ const stateRoot=path.join(os.homedir(),'.deadbyte-mcp');
16
+ const lockPath=path.join(stateRoot,'evidence','updates','r65-activation.lock');
17
+ try {
18
+ if(process.platform!=='win32')throw new Error('Windows-only R168 subject');
19
+ for(const [name,expected] of Object.entries(sourceExpected)){
20
+ const bytes=await readFile(new URL('../src/'+name,import.meta.url));
21
+ if(sha(bytes)!==expected)throw new Error('R168 source identity changed: '+name);
22
+ }
23
+ const pinnedBefore=readPinnedEpochLockSnapshot(lockPath);
24
+ if(!pinnedBefore)throw new Error('old claim missing');
25
+ const observation=await inspectR168UnknownWithoutMutation({
26
+ stateRoot,
27
+ expectedOperationId:'c84d7ffa4eee80a2f467acda3ddd9332',
28
+ expectedSubjectSha256:'fba80e0128da9657dcbfcaa27d3a4dc8d9d8939146e81a097293fa5fcb6ae8fc',
29
+ expectedOutcomeFilename:'r65-activation-outcome-1792993876612-cc7c498632c7757d.json',
30
+ expectedContinuityId:'r65-ef42bc84451ce024c4dee736d4047b36',
31
+ expectedPredecessorReleaseId:'v0.15.61-055d1ff0aa99e60b',
32
+ expectedPredecessorManifestSha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
33
+ expectedPredecessorGenerationSha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4',
34
+ expectedCandidateManifestSha256:'ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849'
35
+ });
36
+ const pinnedAfter=readPinnedEpochLockSnapshot(lockPath);
37
+ if(!pinnedAfter||pinnedAfter.sha256!==pinnedBefore.sha256||pinnedAfter.fileId!==pinnedBefore.fileId)
38
+ throw new Error('R168 lock changed during subject derivation');
39
+ const subject=createR168ReconciliationSubject({observation,claimSnapshot:pinnedBefore,stateRoot});
40
+ console.log(JSON.stringify({status:'R168_SUBJECT_READY_READONLY',claim_operation_id:subject.claim_operation_id,
41
+ observation_sha256:observation.observation_sha256,subject_sha256:subject.subject_sha256,
42
+ native_claim_sha256:pinnedBefore.sha256,native_claim_file_id:pinnedBefore.fileId,
43
+ verified_signed_events:observation.signed_event_count,
44
+ approval_challenge:makeR168ConsentChallenge(subject),
45
+ owner_signature:'NOT_PRESENT',terminalization_committed:false,activation_authorized:false},null,2));
46
+ }catch(error){
47
+ console.error('R168_SUBJECT_READONLY_BLOCKED '+String(error?.message??error));
48
+ process.exitCode=1;
49
+ }
@@ -0,0 +1,200 @@
1
+ // R168 read-only forensic audit. NEVER clears a claim or grants authority.
2
+ import {createHash} from 'node:crypto';
3
+ import {readFile,lstat,readdir,realpath} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
6
+ import {canonicalJson} from './canonical-json.mjs';
7
+
8
+ const PLANES=Object.freeze(['process','coding','autonomous','desktop','host']);
9
+ const GRANT_FIELD={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
10
+ const SHA=/^[0-9a-f]{64}$/;
11
+ const sha=x=>createHash('sha256').update(x).digest('hex');
12
+ function check(condition,message){if(!condition)throw new Error('R168_AUDIT_BLOCKED '+message);}
13
+ function within(root,file){
14
+ const relative=path.relative(path.resolve(root),path.resolve(file));
15
+ return relative!==''&&relative!=='..'&&!relative.startsWith('..'+path.sep)&&!path.isAbsolute(relative);
16
+ }
17
+ async function checkedBytes(file,limit=2*1024*1024){
18
+ const stat=await lstat(file);
19
+ check(stat.isFile()&&!stat.isSymbolicLink()&&stat.size>0&&stat.size<=limit,'nonregular or oversized evidence');
20
+ const target=await realpath(file);
21
+ check(path.resolve(target).toLowerCase()===path.resolve(file).toLowerCase(),'evidence reparse changed path');
22
+ const bytes=await readFile(file);
23
+ check(bytes.length===stat.size,'evidence changed while reading');
24
+ return bytes;
25
+ }
26
+ async function checkedJson(file,limit){
27
+ const bytes=await checkedBytes(file,limit);
28
+ let value;
29
+ try{value=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));}
30
+ catch{throw new Error('R168_AUDIT_BLOCKED invalid evidence JSON');}
31
+ return {value,sha256:sha(bytes)};
32
+ }
33
+ async function inventory(dir,max=4096){
34
+ const names=await readdir(dir,{withFileTypes:true});
35
+ check(names.length<=max,'inventory exceeds bounded scan budget');
36
+ check(names.every(x=>!x.isSymbolicLink()),'inventory contains symlink');
37
+ return names;
38
+ }
39
+ function immutableRef(value){return JSON.parse(JSON.stringify(value));}
40
+
41
+ export function rejectR168MutationEvidence({events,continuityId,claimCreatedAt}){
42
+ check(typeof continuityId==='string'&&/^r65-[0-9a-f]{32}$/.test(continuityId),'invalid continuity identity');
43
+ check(Number.isFinite(Date.parse(claimCreatedAt)),'invalid claim time');
44
+ check(Array.isArray(events)&&events.length<=50000,'event inventory invalid');
45
+ for(const event of events){
46
+ check(event&&PLANES.includes(event.plane)&&Number.isSafeInteger(event.seq)&&event.seq>=1,'event identity incomplete');
47
+ check(!event.update_id||event.update_id!==continuityId,'old continuity mutation exists');
48
+ const t=Date.parse(event.recorded_at_utc);
49
+ check(Number.isFinite(t)&&t<Date.parse(claimCreatedAt),'authority event at or after old claim');
50
+ }
51
+ return {checked_events:events.length,old_continuity_events:0};
52
+ }
53
+
54
+ // This assessment is NOT a signed reconciliation receipt. It cannot terminalize claims.
55
+ // It inventories the exact preserved unknown outcome against current durable authority.
56
+ export async function inspectR168UnknownWithoutMutation({
57
+ stateRoot,expectedOperationId,expectedSubjectSha256,expectedOutcomeFilename,
58
+ expectedContinuityId,expectedPredecessorReleaseId,expectedPredecessorManifestSha256,
59
+ expectedPredecessorGenerationSha256,expectedCandidateManifestSha256
60
+ }={}){
61
+ check(process.platform==='win32','Windows host required');
62
+ check(typeof stateRoot==='string'&&path.win32.isAbsolute(stateRoot),'state root must be Windows absolute');
63
+ check(/^[0-9a-f]{32}$/.test(String(expectedOperationId))&&SHA.test(String(expectedSubjectSha256)),'old claim identity invalid');
64
+ check(SHA.test(String(expectedPredecessorManifestSha256))&&SHA.test(String(expectedPredecessorGenerationSha256))&&
65
+ SHA.test(String(expectedCandidateManifestSha256)),'release identity incomplete');
66
+ check(/^r65-activation-outcome-[0-9]+-[0-9a-f]{16}\.json$/.test(String(expectedOutcomeFilename)),'outcome filename invalid');
67
+ const state=path.resolve(stateRoot),updatesDir=path.join(state,'evidence','updates');
68
+ const lockFile=path.join(updatesDir,'r65-activation.lock');
69
+ const outcomeFile=path.join(updatesDir,expectedOutcomeFilename);
70
+ const beforeLock=await checkedJson(lockFile,8192);
71
+ const beforeOutcome=await checkedJson(outcomeFile,32768);
72
+ const lock=beforeLock.value,outcome=beforeOutcome.value;
73
+ check(lock.schema==='deadbyte.r65-activation-claim.v1'&&lock.operation_id===expectedOperationId&&
74
+ lock.subject_sha256===expectedSubjectSha256,'old claim mismatch');
75
+ check(outcome.schema==='deadbyte.r65-activation-outcome.v1'&&
76
+ outcome.claim_operation_id===expectedOperationId&&outcome.preflight_subject_sha256===expectedSubjectSha256&&
77
+ outcome.candidate_version==='0.15.64'&&outcome.candidate_manifest_sha256===expectedCandidateManifestSha256&&
78
+ outcome.predecessor_release_id===expectedPredecessorReleaseId&&
79
+ outcome.update_id===null&&outcome.state_path===null&&
80
+ outcome.continuity_update_id===expectedContinuityId&&
81
+ outcome.outcome?.state==='ACTIVATION_OUTCOME_UNKNOWN'&&
82
+ String(outcome.outcome?.reason??'').includes('UNTRUSTED_WRITE_ACE'),'old unknown outcome mismatch');
83
+ check(Number.isFinite(Date.parse(lock.created_at_utc))&&Number.isFinite(Date.parse(outcome.observed_at_utc))&&
84
+ Date.parse(outcome.observed_at_utc)>=Date.parse(lock.created_at_utc),'claim/outcome ordering invalid');
85
+
86
+ const beforePointer=await checkedJson(path.join(state,'active-release.json'),32768);
87
+ const pointer=beforePointer.value;
88
+ check(pointer.release_id===expectedPredecessorReleaseId&&
89
+ pointer.manifest_sha256===expectedPredecessorManifestSha256&&
90
+ pointer.generation_manifest_sha256===expectedPredecessorGenerationSha256&&
91
+ pointer.version==='0.15.61','active predecessor changed');
92
+ const generation=path.resolve(pointer.generation_root??'');
93
+ check(within(path.join(state,'generations'),generation),'active generation escapes state');
94
+
95
+ const updateEntries=await inventory(updatesDir,4096);
96
+ const oldPrefix='r27-'+expectedCandidateManifestSha256.slice(0,16)+'-';
97
+ check(!updateEntries.some(x=>x.name.startsWith(oldPrefix)),'old candidate update evidence exists');
98
+ const matchingOutcomes=[];
99
+ for(const item of updateEntries){
100
+ if(!/^r65-activation-outcome-.*\.json$/.test(item.name))continue;
101
+ check(item.isFile(),'outcome inventory includes nonfile');
102
+ const parsed=await checkedJson(path.join(updatesDir,item.name),32768);
103
+ if(parsed.value?.claim_operation_id===expectedOperationId)matchingOutcomes.push({file:item.name,sha256:parsed.sha256});
104
+ }
105
+ check(matchingOutcomes.length===1&&matchingOutcomes[0].file===expectedOutcomeFilename&&
106
+ matchingOutcomes[0].sha256===beforeOutcome.sha256,'ambiguous old claim outcome inventory');
107
+
108
+ const runtime=path.join(state,'runtime'),store=createAuthorityEpochStore({runtimeDir:runtime});
109
+ const planeAudits={},observed=[];
110
+ for(const plane of PLANES){
111
+ const beforeAudit=await store.auditHistory({plane});
112
+ check(['PASS','EMPTY'].includes(beforeAudit.status),'signed history not complete');
113
+ const head=await store.readHead({plane});
114
+ const dir=path.join(runtime,'authority-epochs',plane,'events');
115
+ let files;
116
+ try{files=await inventory(dir,10000);}
117
+ catch(e){if(e.code==='ENOENT'&&beforeAudit.status==='EMPTY')files=[];else throw e;}
118
+ check(files.length===beforeAudit.verified_events,'epoch history file count drift');
119
+ const events=[];
120
+ for(const item of files){
121
+ check(item.isFile(),'epoch history contains unexpected entry');
122
+ const m=/^(\d{16})-([0-9a-f]{64})\.json$/.exec(item.name);
123
+ check(Boolean(m),'epoch history event filename invalid');
124
+ const bytes=await checkedBytes(path.join(dir,item.name),1048576);
125
+ check(sha(bytes)===m[2],'epoch history event digest mismatch');
126
+ const entry=JSON.parse(bytes.toString('utf8'));
127
+ check(entry.seq===Number(m[1])&&entry.plane===plane,'epoch event identity changed');
128
+ events.push({plane,seq:entry.seq,action:entry.action,update_id:entry.update_id,
129
+ recorded_at_utc:entry.recorded_at_utc,event_sha256:m[2]});
130
+ }
131
+ const afterAudit=await store.auditHistory({plane});
132
+ check(afterAudit.status===beforeAudit.status&&
133
+ afterAudit.verified_events===beforeAudit.verified_events&&
134
+ (afterAudit.event_sha256??null)===(beforeAudit.event_sha256??null),'signed epoch history changed');
135
+ rejectR168MutationEvidence({events,continuityId:expectedContinuityId,claimCreatedAt:lock.created_at_utc});
136
+ planeAudits[plane]={status:afterAudit.status,verified_events:afterAudit.verified_events,
137
+ event_sha256:afterAudit.event_sha256??null,head_action:head?.action??null};
138
+ observed.push(...events);
139
+ }
140
+
141
+ const disarmedPolicies={};
142
+ for(const plane of PLANES.filter(x=>x!=='coding')){
143
+ // Configured-but-disarmed is an actual absence assertion, not a guessed scope.
144
+ const policyFile=path.join(generation,plane+'-policy.json');
145
+ const currentPolicy=await checkedJson(policyFile,2*1024*1024);
146
+ const grantPath=path.resolve(currentPolicy.value?.[GRANT_FIELD[plane]]??'');
147
+ check(currentPolicy.value?.enabled===true&&within(runtime,grantPath),
148
+ plane+' policy or grant binding unavailable');
149
+ let present=false;
150
+ try{await lstat(grantPath);present=true;}
151
+ catch(error){if(error?.code!=='ENOENT')throw error;}
152
+ check(!present,plane+' disarmed grant file exists');
153
+ // A historical signed head is not itself an armed grant: the grant file is
154
+ // mandatory to authorize a session. Never rewrite or erase the head here.
155
+ // Full signature-chain audit and the no-post-claim-event rule ran above.
156
+ disarmedPolicies[plane]={policy_sha256:currentPolicy.sha256,grant_absent:true,
157
+ historical_head_action:planeAudits[plane].head_action,
158
+ historical_grant_head_without_grant_file:['grant','transaction_restore'].includes(planeAudits[plane].head_action)};
159
+ }
160
+
161
+ const codingPolicy=await checkedJson(path.join(generation,'coding-policy.json'),2*1024*1024);
162
+ check(codingPolicy.value?.schema==='deadbyte.coding-policy.v1','active Coding policy invalid');
163
+ const grantFile=path.resolve(codingPolicy.value.lease_file??'');
164
+ check(within(runtime,grantFile),'Coding grant outside state');
165
+ const grant=await checkedJson(grantFile,32768);
166
+ check(SHA.test(grant.value.policy_sha256)&&grant.value.policy_sha256===codingPolicy.sha256,
167
+ 'Coding grant policy changed');
168
+ const trusted=await createAuthorityEpochStore({runtimeDir:runtime,policyBytes:await checkedBytes(path.join(generation,'coding-policy.json'))})
169
+ .verifyGrant({plane:'coding',grantBytes:await checkedBytes(grantFile),grantInstanceNonce:grant.value.instance_nonce,
170
+ policyBytes:await checkedBytes(path.join(generation,'coding-policy.json'))});
171
+ check(trusted.trusted===true,'Coding grant no longer signed trusted');
172
+ check(planeAudits.coding.event_sha256===trusted.event_sha256,'Coding head changed during audit');
173
+
174
+ for(const plane of PLANES){
175
+ const finalHistory=await store.auditHistory({plane});
176
+ check(finalHistory.status===planeAudits[plane].status&&
177
+ finalHistory.verified_events===planeAudits[plane].verified_events&&
178
+ (finalHistory.event_sha256??null)===planeAudits[plane].event_sha256,
179
+ 'signed epoch history changed after policy/grant verification');
180
+ }
181
+ const afterLock=await checkedJson(lockFile,8192);
182
+ const afterOutcome=await checkedJson(outcomeFile,32768);
183
+ const afterPointer=await checkedJson(path.join(state,'active-release.json'),32768);
184
+ check(beforeLock.sha256===afterLock.sha256&&beforeOutcome.sha256===afterOutcome.sha256&&
185
+ beforePointer.sha256===afterPointer.sha256,'claim/outcome/pointer changed during audit');
186
+ const evidence={
187
+ schema:'deadbyte.r168-readonly-reconciliation-observation.v1',
188
+ status:'NO_MATCHING_TRANSACTION_OBSERVED',terminalization_authorized:false,
189
+ old_claim_sha256:beforeLock.sha256,old_outcome_sha256:beforeOutcome.sha256,
190
+ predecessor_pointer_sha256:beforePointer.sha256,old_operation_id:expectedOperationId,
191
+ old_continuity_update_id:expectedContinuityId,
192
+ candidate_manifest_sha256:expectedCandidateManifestSha256,
193
+ coding_policy_sha256:codingPolicy.sha256,coding_grant_sha256:grant.sha256,
194
+ coding_epoch_sha256:trusted.event_sha256,
195
+ disarmed_plane_policies:disarmedPolicies,
196
+ update_inventory_count:updateEntries.length,outcome_inventory:matchingOutcomes,
197
+ epoch_history:planeAudits,signed_event_count:observed.length
198
+ };
199
+ return Object.freeze({...evidence,observation_sha256:sha(Buffer.from(canonicalJson(evidence)))});
200
+ }
@@ -0,0 +1,235 @@
1
+ // R168 cryptographic consent contract. Intentionally no filesystem, native CAS,
2
+ // signing-key discovery, publication or authority mutation in this module.
3
+ import path from 'node:path';
4
+ import {createHash,createPublicKey,sign,verify} from 'node:crypto';
5
+ import {canonicalJson} from './canonical-json.mjs';
6
+
7
+ const SHA=/^[0-9a-f]{64}$/, OP=/^[0-9a-f]{32}$/, FILE_ID=/^[0-9a-f]{16}:[0-9a-f]{32}$/;
8
+ const PLANES=['process','coding','autonomous','desktop','host'];
9
+ const OWNER_DOMAIN='DEADBYTE-R168-OWNER-APPROVAL-V1';
10
+ const MACHINE_DOMAIN='DEADBYTE-R168-RECONCILIATION-ATTESTATION-V1';
11
+ const hash=value=>createHash('sha256').update(value).digest('hex');
12
+ const valueBytes=value=>Buffer.from(canonicalJson(value),'utf8');
13
+ const signedMessage=(domain,body)=>Buffer.concat([Buffer.from(domain+'\0','utf8'),valueBytes(body)]);
14
+ function requireR168(condition,message){if(!condition)throw new Error('R168 '+message);}
15
+ function pub(input,label){
16
+ let k;
17
+ try{k=input?.type==='public'&&input?.asymmetricKeyType==='ed25519'?input:createPublicKey(input);}
18
+ catch{throw new Error('R168 '+label+' public key invalid');}
19
+ requireR168(k.asymmetricKeyType==='ed25519',label+' key is not Ed25519');
20
+ return k;
21
+ }
22
+ function strictSignature(attestation,domain){
23
+ requireR168(attestation&&typeof attestation==='object'&&
24
+ attestation.algorithm==='ed25519'&&attestation.domain===domain&&
25
+ typeof attestation.signature_base64==='string','signature identity invalid');
26
+ const buf=Buffer.from(attestation.signature_base64,'base64');
27
+ requireR168(buf.length===64&&buf.toString('base64')===attestation.signature_base64,'signature encoding invalid');
28
+ return buf;
29
+ }
30
+ function canonicalStateRoot(value){
31
+ requireR168(typeof value==='string'&&/^[A-Za-z]:\\/.test(value)&&
32
+ path.win32.isAbsolute(value)&&!value.includes('\0')&&!value.includes('/')&&
33
+ !value.startsWith('\\\\'),'state root invalid');
34
+ const normalized=path.win32.normalize(value);
35
+ requireR168(normalized===value,'state root must be canonical');
36
+ return normalized;
37
+ }
38
+ function canonicalSignedSubject(subject){
39
+ const fields=['schema','requested_action','state_root','claim_operation_id',
40
+ 'old_continuity_update_id','claim_file_id','claim_file_sha256','observation_sha256',
41
+ 'old_outcome_sha256','predecessor_pointer_sha256','coding_policy_sha256',
42
+ 'coding_grant_sha256','coding_epoch_sha256','candidate_manifest_sha256',
43
+ 'signed_event_count','update_inventory_count','epoch_history',
44
+ 'terminalization_committed','activation_authorized','subject_sha256'];
45
+ requireR168(subject&&typeof subject==='object'&&!Array.isArray(subject)&&
46
+ subject.schema==='deadbyte.r168-reconciliation-subject.v1'&&
47
+ SHA.test(String(subject.subject_sha256)),'subject schema/digest invalid');
48
+ requireR168(Object.keys(subject).length===fields.length&&
49
+ fields.every(key=>Object.hasOwn(subject,key)),'subject field shape incomplete or unknown');
50
+ const {subject_sha256,...body}=subject;
51
+ requireR168(hash(valueBytes(body))===subject_sha256,'subject digest mismatch');
52
+ canonicalStateRoot(subject.state_root);
53
+ requireR168(subject.requested_action==='terminalize_unknown_without_replay'&&
54
+ subject.terminalization_committed===false&&subject.activation_authorized===false&&
55
+ OP.test(subject.claim_operation_id)&&
56
+ /^r65-[0-9a-f]{32}$/.test(subject.old_continuity_update_id)&&
57
+ FILE_ID.test(subject.claim_file_id),
58
+ 'subject authority or claim identity invalid');
59
+ for(const key of ['claim_file_sha256','observation_sha256','old_outcome_sha256',
60
+ 'predecessor_pointer_sha256','coding_policy_sha256','coding_grant_sha256',
61
+ 'coding_epoch_sha256','candidate_manifest_sha256'])
62
+ requireR168(SHA.test(String(subject[key])),'subject evidence hash invalid');
63
+ requireR168(subject.epoch_history&&typeof subject.epoch_history==='object'&&
64
+ !Array.isArray(subject.epoch_history)&&
65
+ Object.keys(subject.epoch_history).length===PLANES.length&&
66
+ PLANES.every(plane=>subject.epoch_history[plane]?.status==='PASS'&&
67
+ SHA.test(String(subject.epoch_history[plane].event_sha256))&&
68
+ Number.isSafeInteger(subject.epoch_history[plane].verified_events)&&
69
+ subject.epoch_history[plane].verified_events>0),
70
+ 'subject signed history incomplete');
71
+ requireR168(subject.coding_epoch_sha256===subject.epoch_history.coding.event_sha256&&
72
+ Number.isSafeInteger(subject.signed_event_count)&&
73
+ PLANES.reduce((n,p)=>n+subject.epoch_history[p].verified_events,0)===subject.signed_event_count&&
74
+ Number.isSafeInteger(subject.update_inventory_count)&&subject.update_inventory_count>=0,
75
+ 'subject event inventory identity invalid');
76
+ return body;
77
+ }
78
+ function parseTime(value,name){
79
+ requireR168(typeof value==='string'&&/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(value)&&
80
+ Number.isFinite(Date.parse(value))&&new Date(value).toISOString()===value,name+' time invalid');
81
+ return Date.parse(value);
82
+ }
83
+
84
+ export function createR168ReconciliationSubject({observation,claimSnapshot,stateRoot}={}){
85
+ requireR168(observation?.schema==='deadbyte.r168-readonly-reconciliation-observation.v1'&&
86
+ observation.status==='NO_MATCHING_TRANSACTION_OBSERVED'&&observation.terminalization_authorized===false,
87
+ 'observation is not read-only verified');
88
+ requireR168(SHA.test(String(observation.observation_sha256)),'observation digest missing');
89
+ const {observation_sha256,...details}=observation;
90
+ requireR168(hash(valueBytes(details))===observation.observation_sha256,'observation digest mismatch');
91
+ requireR168(OP.test(String(observation.old_operation_id))&&
92
+ /^r65-[0-9a-f]{32}$/.test(String(observation.old_continuity_update_id)),
93
+ 'claim/continuity identity invalid');
94
+ requireR168(SHA.test(String(claimSnapshot?.sha256))&&
95
+ claimSnapshot.sha256===observation.old_claim_sha256&&FILE_ID.test(String(claimSnapshot?.fileId)),
96
+ 'native claim lock file identity mismatch');
97
+ requireR168(PLANES.every(p=>observation.epoch_history?.[p]?.status==='PASS'&&
98
+ SHA.test(String(observation.epoch_history[p].event_sha256))&&
99
+ Number.isSafeInteger(observation.epoch_history[p].verified_events)&&observation.epoch_history[p].verified_events>0)&&
100
+ Object.keys(observation.epoch_history).length===PLANES.length,
101
+ 'signed epoch history incomplete');
102
+ requireR168(PLANES.reduce((n,p)=>n+observation.epoch_history[p].verified_events,0)===observation.signed_event_count,
103
+ 'signed epoch inventory count mismatch');
104
+ requireR168(SHA.test(String(observation.coding_epoch_sha256))&&
105
+ observation.epoch_history.coding.event_sha256===observation.coding_epoch_sha256&&
106
+ SHA.test(String(observation.coding_policy_sha256))&&
107
+ SHA.test(String(observation.coding_grant_sha256))&&SHA.test(String(observation.predecessor_pointer_sha256))&&
108
+ SHA.test(String(observation.old_outcome_sha256))&&
109
+ SHA.test(String(observation.candidate_manifest_sha256)),
110
+ 'observation authority or predecessor identity invalid');
111
+ requireR168(Number.isSafeInteger(observation.update_inventory_count)&&
112
+ observation.update_inventory_count>=0,'evidence inventory count invalid');
113
+ const body={
114
+ schema:'deadbyte.r168-reconciliation-subject.v1',
115
+ requested_action:'terminalize_unknown_without_replay',
116
+ state_root:canonicalStateRoot(stateRoot),
117
+ claim_operation_id:observation.old_operation_id,
118
+ old_continuity_update_id:observation.old_continuity_update_id,
119
+ claim_file_id:claimSnapshot.fileId,
120
+ claim_file_sha256:claimSnapshot.sha256,
121
+ observation_sha256:observation.observation_sha256,
122
+ old_outcome_sha256:observation.old_outcome_sha256,
123
+ predecessor_pointer_sha256:observation.predecessor_pointer_sha256,
124
+ coding_policy_sha256:observation.coding_policy_sha256,
125
+ coding_grant_sha256:observation.coding_grant_sha256,
126
+ coding_epoch_sha256:observation.coding_epoch_sha256,
127
+ candidate_manifest_sha256:observation.candidate_manifest_sha256,
128
+ signed_event_count:observation.signed_event_count,
129
+ update_inventory_count:observation.update_inventory_count,
130
+ epoch_history:structuredClone(observation.epoch_history),
131
+ terminalization_committed:false,activation_authorized:false
132
+ };
133
+ return Object.freeze({...body,subject_sha256:hash(valueBytes(body))});
134
+ }
135
+
136
+ export function makeR168ConsentChallenge(subject){
137
+ canonicalSignedSubject(subject);
138
+ return 'RECONCILE '+subject.claim_operation_id+' '+subject.subject_sha256;
139
+ }
140
+
141
+ export function verifyR168OwnerApproval({subject,approval,ownerPublicKey,now}={}){
142
+ canonicalSignedSubject(subject);
143
+ const ownerFields=['schema','action','subject_sha256','claim_operation_id','nonce','issued_at_utc','expires_at_utc'];
144
+ requireR168(approval&&typeof approval==='object'&&!Array.isArray(approval)&&
145
+ Object.keys(approval).length===2&&Object.hasOwn(approval,'body')&&Object.hasOwn(approval,'attestation')&&
146
+ approval.body&&typeof approval.body==='object'&&!Array.isArray(approval.body)&&
147
+ Object.keys(approval.body).length===ownerFields.length&&
148
+ ownerFields.every(key=>Object.hasOwn(approval.body,key)),
149
+ 'owner approval field shape invalid');
150
+ requireR168(approval?.body?.schema==='deadbyte.r168-owner-approval.v1'&&
151
+ approval.body.action==='terminalize_unknown_without_replay'&&
152
+ approval.body.subject_sha256===subject.subject_sha256&&
153
+ approval.body.claim_operation_id===subject.claim_operation_id,
154
+ 'owner approval subject mismatch');
155
+ requireR168(SHA.test(String(approval.body.nonce)),'owner approval nonce invalid');
156
+ const issue=parseTime(approval.body.issued_at_utc,'approval issue');
157
+ const expiry=parseTime(approval.body.expires_at_utc,'approval expiry');
158
+ const nowValue=parseTime(now??new Date().toISOString(),'approval check');
159
+ requireR168(expiry>issue&&expiry-issue<=600000&&nowValue>=issue&&nowValue<expiry,
160
+ 'owner approval expired or outside window');
161
+ const sig=strictSignature(approval.attestation,OWNER_DOMAIN);
162
+ requireR168(verify(null,signedMessage(OWNER_DOMAIN,approval.body),pub(ownerPublicKey,'owner'),sig),
163
+ 'owner approval signature invalid');
164
+ return true;
165
+ }
166
+
167
+ export function createR168SignedAuthorization({
168
+ subject,approval,ownerPublicKey,machinePrivateKey,now,typedConfirmation
169
+ }={}){
170
+ verifyR168OwnerApproval({subject,approval,ownerPublicKey,now});
171
+ requireR168(typedConfirmation===makeR168ConsentChallenge(subject),
172
+ 'explicit typed confirmation does not match approved claim');
173
+ requireR168(machinePrivateKey?.asymmetricKeyType==='ed25519'&&machinePrivateKey?.type==='private',
174
+ 'machine signing key unavailable');
175
+ const ownerPublic=pub(ownerPublicKey,'owner');
176
+ const machinePublic=createPublicKey(machinePrivateKey);
177
+ const spki=k=>Buffer.from(k.export({type:'spki',format:'der'}));
178
+ requireR168(!spki(ownerPublic).equals(spki(machinePublic)),
179
+ 'owner and machine signing identities must use independent keys');
180
+ const body={
181
+ schema:'deadbyte.r168-reconciliation-authorization.v1',
182
+ action:'prepare_exact_claim_terminalization',
183
+ subject_sha256:subject.subject_sha256,
184
+ claim_operation_id:subject.claim_operation_id,
185
+ claim_file_id:subject.claim_file_id,
186
+ owner_approval_sha256:hash(valueBytes(approval)),
187
+ created_at_utc:now,
188
+ terminalization_committed:false,
189
+ activation_authorized:false
190
+ };
191
+ const signature=sign(null,signedMessage(MACHINE_DOMAIN,body),machinePrivateKey);
192
+ const unsigned={body,body_sha256:hash(valueBytes(body)),
193
+ attestation:{algorithm:'ed25519',domain:MACHINE_DOMAIN,signature_base64:signature.toString('base64')}};
194
+ return Object.freeze({...unsigned,receipt_sha256:hash(valueBytes(unsigned))});
195
+ }
196
+
197
+ export function verifyR168SignedAuthorization({
198
+ subject,approval,receipt,ownerPublicKey,machinePublicKey,now
199
+ }={}){
200
+ verifyR168OwnerApproval({subject,approval,ownerPublicKey,now});
201
+ const ownerKey=pub(ownerPublicKey,'owner'),machineKey=pub(machinePublicKey,'machine');
202
+ const spki=key=>Buffer.from(key.export({type:'spki',format:'der'}));
203
+ requireR168(!spki(ownerKey).equals(spki(machineKey)),
204
+ 'owner and machine signer identities must be independent');
205
+ const fields=['schema','action','subject_sha256','claim_operation_id','claim_file_id',
206
+ 'owner_approval_sha256','created_at_utc','terminalization_committed','activation_authorized'];
207
+ requireR168(receipt&&typeof receipt==='object'&&!Array.isArray(receipt)&&
208
+ Object.keys(receipt).length===4&&
209
+ ['body','body_sha256','attestation','receipt_sha256'].every(k=>Object.hasOwn(receipt,k))&&
210
+ receipt.body&&typeof receipt.body==='object'&&!Array.isArray(receipt.body)&&
211
+ Object.keys(receipt.body).length===fields.length&&fields.every(k=>Object.hasOwn(receipt.body,k)),
212
+ 'authorization receipt field shape invalid');
213
+ requireR168(receipt?.body?.schema==='deadbyte.r168-reconciliation-authorization.v1'&&
214
+ receipt.body.action==='prepare_exact_claim_terminalization'&&
215
+ receipt.body.subject_sha256===subject.subject_sha256&&
216
+ receipt.body.claim_operation_id===subject.claim_operation_id&&
217
+ receipt.body.claim_file_id===subject.claim_file_id&&
218
+ receipt.body.owner_approval_sha256===hash(valueBytes(approval))&&
219
+ receipt.body.terminalization_committed===false&&receipt.body.activation_authorized===false,
220
+ 'authorization receipt subject/permission mismatch');
221
+ const created=parseTime(receipt.body.created_at_utc,'receipt');
222
+ const issued=parseTime(approval.body.issued_at_utc,'owner approval issue');
223
+ const expiry=parseTime(approval.body.expires_at_utc,'owner approval expiry');
224
+ const current=parseTime(now??new Date().toISOString(),'verification');
225
+ requireR168(created>=issued&&created<=current&&created<expiry,
226
+ 'authorization receipt created outside owner approval time window or in the future');
227
+ requireR168(receipt.body_sha256===hash(valueBytes(receipt.body)),
228
+ 'authorization receipt body digest mismatch');
229
+ const {receipt_sha256,...unsigned}=receipt;
230
+ requireR168(receipt_sha256===hash(valueBytes(unsigned)),'authorization receipt digest mismatch');
231
+ const signature=strictSignature(receipt.attestation,MACHINE_DOMAIN);
232
+ requireR168(verify(null,signedMessage(MACHINE_DOMAIN,receipt.body),machineKey,signature),
233
+ 'authorization receipt signature invalid');
234
+ return true;
235
+ }