deadbyte-mcp 0.15.65 → 0.15.67

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 (69) hide show
  1. package/MANIFEST.SHA256 +68 -21
  2. package/PROVENANCE.json +35 -9
  3. package/README.txt +31 -18
  4. package/RELEASE.json +43 -7
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +45 -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/docs/R169-NPX-ACTIVE-CONTROLLER-DELEGATION-20261026.md +57 -0
  17. package/node_modules/which/CHANGELOG.md +166 -0
  18. package/package.json +5 -2
  19. package/scripts/gate-windows-r168.ps1 +75 -0
  20. package/scripts/npm-release-r48.mjs +23 -3
  21. package/scripts/npm-release-readiness-r168.mjs +70 -0
  22. package/scripts/r168-audit-readonly.mjs +32 -0
  23. package/scripts/r168-npm-registry-readonly.mjs +19 -0
  24. package/scripts/r168-protected-preflight.mjs +65 -0
  25. package/scripts/r168-release-scan.mjs +28 -0
  26. package/scripts/r168-source-full-test-runner.mjs +46 -0
  27. package/scripts/r168-subject-readonly.mjs +49 -0
  28. package/scripts/r172-protected-preflight.mjs +42 -0
  29. package/src/activation-continuity-abort-r172.mjs +108 -0
  30. package/src/activation-lock-r65.mjs +5 -1
  31. package/src/activation-preflight-r65.mjs +2 -1
  32. package/src/activation-reconciliation-audit-r168.mjs +200 -0
  33. package/src/activation-reconciliation-consent-r168.mjs +235 -0
  34. package/src/activation-reconciliation-fence-r168.mjs +87 -0
  35. package/src/activation-reconciliation-journal-r168.mjs +100 -0
  36. package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
  37. package/src/activation-reconciliation-terminal-r168.mjs +111 -0
  38. package/src/active-controller-delegation-r169.mjs +57 -0
  39. package/src/deadbyte-cli.mjs +38 -0
  40. package/src/npm-publication-resume-r168.mjs +25 -0
  41. package/src/remote-one-shot-coherence-r168.mjs +43 -0
  42. package/src/version.mjs +1 -1
  43. package/test/r130-release-security-contract.test.mjs +7 -7
  44. package/test/r132-release-identity.test.mjs +8 -8
  45. package/test/r153-prepublish-identity.test.mjs +1 -1
  46. package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
  47. package/test/r165-live-cli-skew.test.mjs +7 -7
  48. package/test/r167-release-contract.test.mjs +11 -10
  49. package/test/r168-combined-targeted-runner.mjs +14 -0
  50. package/test/r168-crash-journal.test.mjs +149 -0
  51. package/test/r168-five-plane-fence-runner.mjs +11 -0
  52. package/test/r168-five-plane-fence.test.mjs +128 -0
  53. package/test/r168-focused-suite.test.mjs +4 -0
  54. package/test/r168-native-five-plane-runner.mjs +11 -0
  55. package/test/r168-native-five-plane-scratch.test.mjs +49 -0
  56. package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
  57. package/test/r168-npm-resume-identity.test.mjs +49 -0
  58. package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
  59. package/test/r168-protected-entrypoint.test.mjs +53 -0
  60. package/test/r168-reconciliation-audit.test.mjs +42 -0
  61. package/test/r168-release-contract.test.mjs +46 -0
  62. package/test/r168-remote-one-shot-coherence.test.mjs +66 -0
  63. package/test/r169-delegate-active.test.mjs +36 -0
  64. package/test/r169-delegate-cli-integration.test.mjs +93 -0
  65. package/test/r169-targeted-runner.mjs +16 -0
  66. package/test/r172-activation-recovery.test.mjs +95 -0
  67. package/test/r172-release-contract.test.mjs +35 -0
  68. package/test/r59-tool-surface-compat.test.mjs +1 -1
  69. package/test/r63-one-shot-coherence.test.mjs +10 -5
@@ -55,7 +55,8 @@ export function classifyActivationPreflight({pointer,expectedParent,candidate,ex
55
55
  for(const plane of PLANES){
56
56
  const entry=observation?.authority?.[plane],proof=observation?.authority_proof?.[plane];
57
57
  if(entry&&typeof entry==='object'){
58
- const knownReason=entry.armed?entry.reason==='standing-grant':entry.reason==='disarmed'||entry.reason==='coding-disarmed';
58
+ const knownReason=entry.armed?entry.reason==='standing-grant':
59
+ entry.reason==='disarmed'||entry.reason==='coding-disarmed'||entry.reason==='epoch-revoked';
59
60
  if(!knownReason)return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority reason unverified`,...bound});
60
61
  }
61
62
  if(!proof)
@@ -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
+ }
@@ -0,0 +1,87 @@
1
+ // R168 five-plane R85 exclusion, with a deliberately non-injectable production
2
+ // entrypoint. Simulation is ONLY a pure test of sequencing: no production proof.
3
+ import {randomBytes} from 'node:crypto';
4
+ import {lstat} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {canonicalJson} from './canonical-json.mjs';
7
+ import {acquireHeldEpochLock} from './epoch-lock-r85.mjs';
8
+ import {queryForegroundIdentity,validForegroundIdentity} from './foreground-process-identity.mjs';
9
+
10
+ const PLANES=Object.freeze(['process','coding','autonomous','desktop','host']);
11
+ const validate=(stateRoot,onHeld)=>{
12
+ if(typeof onHeld!=='function'||typeof stateRoot!=='string'||!path.isAbsolute(stateRoot))
13
+ throw new Error('R168 held epoch fence arguments invalid');
14
+ };
15
+
16
+ async function runHeldFence({stateRoot,onHeld,acquire,owner}){
17
+ validate(stateRoot,onHeld);
18
+ if(typeof acquire!=='function'||!validForegroundIdentity(owner)||owner.pid!==process.pid)
19
+ throw new Error('R168 native owner process identity unavailable');
20
+ const root=path.resolve(stateRoot),runtime=path.join(root,'runtime','authority-epochs');
21
+ const folders=PLANES.map(plane=>({plane,dir:path.join(runtime,plane)}));
22
+ // Preflight the entire directory set before acquiring any lock. Never make
23
+ // missing directories or silently repair a stale owner in reconciliation.
24
+ for(const entry of folders){
25
+ const stat=await lstat(entry.dir);
26
+ if(!stat.isDirectory()||stat.isSymbolicLink())
27
+ throw new Error('R168 epoch plane directory untrusted or unavailable: '+entry.plane);
28
+ }
29
+ const leases=[];
30
+ let started=false,primary=null,result;
31
+ try{
32
+ for(const entry of folders){
33
+ const bytes=Buffer.from(canonicalJson({
34
+ schema:'deadbyte.authority-epoch-lock.r65.v1',
35
+ pid:process.pid,token:randomBytes(16).toString('hex'),
36
+ created_at_utc:new Date().toISOString(),owner_identity:owner
37
+ }),'utf8');
38
+ const lease=await acquire(path.join(entry.dir,'.lock'),bytes);
39
+ if(!lease||typeof lease.release!=='function')
40
+ throw new Error('R168 epoch native lock busy: '+entry.plane);
41
+ leases.push({plane:entry.plane,lease});
42
+ }
43
+ started=true;
44
+ result=await onHeld(Object.freeze({
45
+ planeOrder:PLANES,
46
+ assertHeld:()=>{
47
+ if(!started||leases.length!==PLANES.length)
48
+ throw new Error('R168 five-plane native exclusion not held');
49
+ return true;
50
+ }
51
+ }));
52
+ }catch(error){primary=error;}
53
+ const releaseErrors=[];
54
+ for(const {plane,lease} of leases.reverse()){
55
+ try{await lease.release();}
56
+ catch(error){releaseErrors.push(new Error('R168 failed to release '+plane+' native epoch lock',{cause:error}));}
57
+ }
58
+ if(releaseErrors.length&&primary)
59
+ throw new AggregateError([primary,...releaseErrors],'R168 native epoch fence and cleanup ambiguous');
60
+ if(releaseErrors.length)throw new AggregateError(releaseErrors,'R168 native epoch fence release ambiguous');
61
+ if(primary)throw primary;
62
+ return result;
63
+ }
64
+
65
+ // Only this entrypoint can be used by any future trusted reconciliation commit.
66
+ // The R85 loader itself enforces R89 and native binary/source identity.
67
+ export async function withR168FivePlaneEpochFence(options={}){
68
+ if(!options||typeof options!=='object'||Array.isArray(options)||
69
+ Object.keys(options).some(key=>!['stateRoot','onHeld'].includes(key)))
70
+ throw new Error('R168 protected fence forbids injected or unknown options');
71
+ validate(options.stateRoot,options.onHeld);
72
+ if(process.platform!=='win32')throw new Error('R168 production epoch exclusion requires Windows');
73
+ const owner=queryForegroundIdentity(process.pid);
74
+ return runHeldFence({...options,owner,acquire:acquireHeldEpochLock});
75
+ }
76
+
77
+ // Non-authoritative simulation only. Deliberately NOT called by production
78
+ // entrypoint; test-worker environment is not a security boundary.
79
+ export async function simulateR168FivePlaneEpochFence({
80
+ stateRoot,onHeld,testAcquire,testOwnerIdentity
81
+ }={}){
82
+ if(process.env.NODE_TEST_CONTEXT!=='child-v8')
83
+ throw new Error('R168 simulation requires a Node test worker');
84
+ if(typeof testAcquire!=='function')
85
+ throw new Error('R168 simulation requires a fake acquire');
86
+ return runHeldFence({stateRoot,onHeld,acquire:testAcquire,owner:testOwnerIdentity});
87
+ }
@@ -0,0 +1,100 @@
1
+ // R168 fail-closed, non-mutating reconciliation intent journal.
2
+ // This is a standalone receipt contract. Production CAS MUST NOT use it
3
+ // without a trusted R89 entrypoint, held five-plane R85 locks, actual owner
4
+ // approval and native FileIdInfo observations. No delete/revoke here.
5
+ import {createHash} from 'node:crypto';
6
+ import {lstat,open,readFile,realpath} from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import {canonicalJson} from './canonical-json.mjs';
9
+ import {verifyR168SignedAuthorization} from './activation-reconciliation-consent-r168.mjs';
10
+ import {verifyR168TerminalResult} from './activation-reconciliation-terminal-r168.mjs';
11
+
12
+ const SHA=/^[a-f0-9]{64}$/;
13
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
14
+ const stable=body=>Buffer.from(canonicalJson(body)+'\n','utf8');
15
+ function check(condition,reason){if(!condition)throw new Error('R168 journal '+reason);}
16
+ async function normalDirectory(value){
17
+ check(typeof value==='string'&&path.isAbsolute(value),'evidence directory must be absolute');
18
+ const dir=path.resolve(value),stat=await lstat(dir);
19
+ check(stat.isDirectory()&&!stat.isSymbolicLink(),'evidence directory not ordinary');
20
+ check(path.resolve(await realpath(dir)).toLowerCase()===dir.toLowerCase(),
21
+ 'evidence directory reparse denied');
22
+ return dir;
23
+ }
24
+ function content(subject,approval,prepared){
25
+ const body={schema:'deadbyte.r168-terminal-intent.v1',
26
+ phase:'INTENT_PREPARED_ONLY',
27
+ subject_sha256:subject.subject_sha256,
28
+ claim_operation_id:subject.claim_operation_id,
29
+ claim_file_id:subject.claim_file_id,
30
+ claim_file_sha256:subject.claim_file_sha256,
31
+ predecessor_pointer_sha256:subject.predecessor_pointer_sha256,
32
+ owner_approval_nonce:approval.body.nonce,
33
+ owner_approval_sha256:sha(Buffer.from(canonicalJson(approval))),
34
+ prepared_receipt_sha256:prepared.receipt_sha256,
35
+ terminalization_committed:false,activation_authorized:false};
36
+ return {body,body_sha256:sha(Buffer.from(canonicalJson(body)))};
37
+ }
38
+ export async function prepareR168TerminalIntent({
39
+ evidenceDir,subject,approval,prepared,ownerPublicKey,machinePublicKey,now
40
+ }={}){
41
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,
42
+ ownerPublicKey,machinePublicKey,now});
43
+ const dir=await normalDirectory(evidenceDir);
44
+ check(SHA.test(approval.body.nonce),'owner approval nonce invalid');
45
+ const file=path.join(dir,'r168-terminal-intent-'+approval.body.nonce+'.json');
46
+ const encoded=stable(content(subject,approval,prepared));
47
+ check(encoded.length<32768,'intent size exceeded');
48
+ let handle;
49
+ try{handle=await open(file,'wx',0o600);}
50
+ catch(error){if(error?.code==='EEXIST')throw new Error('R168 journal one-use intent already exists; no-clobber');throw error;}
51
+ try{
52
+ // A write or fsync error leaves any incomplete intent in place to block
53
+ // replay. We never rename/rewrite it to appear as a successful receipt.
54
+ await handle.writeFile(encoded);
55
+ await handle.sync();
56
+ }finally{await handle.close();}
57
+ const checkBytes=await readFile(file);
58
+ check(checkBytes.equals(encoded),'intent changed during readback');
59
+ return Object.freeze({file,sha256:sha(encoded),bytes:encoded.length,
60
+ state:'INTENT_PREPARED_ONLY',activation_authorized:false});
61
+ }
62
+ export async function classifyR168Recovery({
63
+ recordFile,subject,approval,prepared,ownerPublicKey,machinePublicKey,
64
+ observedClaim,terminal=null,after=null,now
65
+ }={}){
66
+ // Historical validity of a recorded intent remains verifiable after the
67
+ // one-time owner's approval window expires. This DOES NOT grant fresh consent.
68
+ verifyR168SignedAuthorization({subject,approval,receipt:prepared,ownerPublicKey,machinePublicKey,
69
+ now:prepared?.body?.created_at_utc});
70
+ check(typeof recordFile==='string'&&path.isAbsolute(recordFile),'record path invalid');
71
+ const stat=await lstat(recordFile);
72
+ check(stat.isFile()&&!stat.isSymbolicLink()&&stat.size>0&&stat.size<=32768,
73
+ 'journal file is not regular or bounded');
74
+ const original=await readFile(recordFile);
75
+ check(original.length===stat.size,'journal file changed during audit');
76
+ let record;
77
+ try{record=JSON.parse(original.toString('utf8'));}catch{throw new Error('R168 journal damaged JSON');}
78
+ const expected=content(subject,approval,prepared);
79
+ check(record?.body_sha256===expected.body_sha256&&
80
+ canonicalJson(record.body)===canonicalJson(expected.body)&&
81
+ sha(Buffer.from(canonicalJson(record.body)))===record.body_sha256,
82
+ 'journal digest/identity tampered');
83
+ check(observedClaim&&typeof observedClaim==='object','claim observation absent');
84
+ if(observedClaim.state==='PRESENT'){
85
+ if(terminal)throw new Error('R168 journal terminal/claim state ambiguous');
86
+ const same=observedClaim.fileId===subject.claim_file_id&&
87
+ observedClaim.sha256===subject.claim_file_sha256;
88
+ return Object.freeze({state:same?'PRECOMMIT_CLAIM_PRESENT_BLOCKED':'CLAIM_CHANGED_BLOCKED',
89
+ activation_authorized:false,terminalization_committed:false});
90
+ }
91
+ check(observedClaim.state==='ABSENT','claim observation unverified');
92
+ if(!terminal)return Object.freeze({state:'CAS_OUTCOME_UNKNOWN_BLOCKED',
93
+ activation_authorized:false,terminalization_committed:false});
94
+ check(after,'terminal post-CAS evidence missing');
95
+ verifyR168TerminalResult({subject,approval,prepared,terminal,
96
+ ownerPublicKey,machinePublicKey,after,now});
97
+ return Object.freeze({state:'TERMINAL_VERIFIED_ACTIVATION_SEPARATE',
98
+ activation_authorized:false,terminalization_committed:true,
99
+ terminal_receipt_sha256:terminal.receipt_sha256});
100
+ }