deadbyte-mcp 0.15.47 → 0.15.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +111 -48
- package/PROVENANCE.json +1 -1
- package/README.txt +18 -18
- package/RELEASE.json +3 -3
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +2 -2
- package/bench/r121-epoch-readhead.mjs +54 -0
- package/bench/r124-epoch-predecessor.mjs +53 -0
- package/bin/deadbyte-epoch-lock-r85.node +0 -0
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/HARNESS2-ADAPTATION-20261020.md +24 -0
- package/docs/R128-RELEASE-CANDIDATE.md +26 -0
- package/docs/R75-RECOVERY-HANDOFF.md +16 -0
- package/docs/R85-EPOCH-LOCK-BINARY.json +13 -0
- package/native/deadbyte-epoch-lock-r85.cpp +241 -0
- package/package.json +1 -1
- package/scripts/build-epoch-lock-r85.ps1 +34 -0
- package/scripts/deploy-r48.mjs +3 -1
- package/scripts/npm-release-r48.mjs +18 -4
- package/scripts/r75-npm-bin-integrity-gate.mjs +33 -0
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/verify-authority-epoch-history.mjs +38 -0
- package/scripts/verify-r70-full-regression.ps1 +12 -2
- package/src/activation-authority-proof-r75.mjs +67 -0
- package/src/activation-continuity-engine-r65.mjs +17 -10
- package/src/activation-preflight-r65.mjs +13 -0
- package/src/agent-eval-r42.mjs +30 -0
- package/src/authority-continuity-r65.mjs +442 -29
- package/src/autonomous-local-supervisor.mjs +77 -47
- package/src/deadbyte-cli.mjs +73 -46
- package/src/epoch-lock-r85.mjs +48 -0
- package/src/foreground-marker-cas-win32.mjs +284 -0
- package/src/foreground-process-identity.mjs +3 -3
- package/src/privileged-install-trust-r89.mjs +196 -0
- package/src/remote-live-log.mjs +15 -8
- package/src/remote-log-decode-r60.mjs +65 -0
- package/src/remote-session-guardian.mjs +13 -12
- package/src/runtime-update.mjs +5 -22
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +24 -0
- package/test/autonomous-local-supervisor.test.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/harness2-eval-context-isolation.test.mjs +90 -0
- package/test/helpers/r75-authority-proof.mjs +9 -0
- package/test/r100-posix-owned-lock-race.test.mjs +72 -0
- package/test/r101-acl-probe-environment.test.mjs +51 -0
- package/test/r106-trusted-powershell-spawn.test.mjs +53 -0
- package/test/r107-updater-powershell-path.test.mjs +77 -0
- package/test/r108-remote-powershell-path.test.mjs +59 -0
- package/test/r110-supervisor-powershell-path.test.mjs +28 -0
- package/test/r111-supervisor-marker-cas.test.mjs +116 -0
- package/test/r112-supervisor-claim-cas.test.mjs +63 -0
- package/test/r113-supervisor-cleanup-terminality.test.mjs +100 -0
- package/test/r114-supervisor-absence-proof.test.mjs +45 -0
- package/test/r115-posix-continuity-entrypoint.test.mjs +17 -0
- package/test/r116-authority-crash-matrix.test.mjs +83 -0
- package/test/r117-concurrent-orphan-recovery.test.mjs +54 -0
- package/test/r118-cross-process-orphan-recovery.test.mjs +99 -0
- package/test/r119-recovery-crash-after-commit.test.mjs +91 -0
- package/test/r120-authority-history-audit.test.mjs +103 -0
- package/test/r121-signed-transition-invariant.test.mjs +132 -0
- package/test/r122-epoch-bounded-read.test.mjs +58 -0
- package/test/r123-recovery-bounded-read.test.mjs +64 -0
- package/test/r124-live-log-integrity.test.mjs +66 -0
- package/test/r124-live-log-tail-boundaries.test.mjs +57 -0
- package/test/r124-signed-predecessor-shape.test.mjs +66 -0
- package/test/r125-restore-binding-types.test.mjs +63 -0
- package/test/r126-restore-policy-envelope.test.mjs +89 -0
- package/test/r127-restore-append-policy-boundary.test.mjs +25 -0
- package/test/r33-closeout-regression.test.mjs +2 -1
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +2 -2
- package/test/r55-offline-predecessor-guardian-family.test.mjs +2 -2
- package/test/r56-rollback-launcher-restore.test.mjs +2 -2
- package/test/r57-core-startup-race.test.mjs +2 -2
- package/test/r58-concurrent-recovery.test.mjs +10 -7
- package/test/r58-stale-owner.test.mjs +5 -6
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +2 -1
- package/test/r65-activation-preflight.test.mjs +7 -6
- package/test/r65-authority-epoch-recovery.test.mjs +3 -1
- package/test/r65-authority-epoch-store.test.mjs +9 -6
- package/test/r65-authority-restored-safe.test.mjs +2 -1
- package/test/r65-deploy-preflight.test.mjs +13 -14
- package/test/r65-epoch-native-activation-integration.test.mjs +5 -4
- package/test/r65-legacy-authority-migration.test.mjs +2 -1
- package/test/r67-npm-cross-volume.test.mjs +9 -2
- package/test/r71-full-verifier-artifact.test.mjs +21 -15
- package/test/r71-isolated-slot-rollback-restore.test.mjs +3 -1
- package/test/r71-release-identity.test.mjs +3 -1
- package/test/r74-kernel-lock.test.mjs +12 -6
- package/test/r74-prepublish-contract.test.mjs +3 -1
- package/test/r75-activation-authority-proof.test.mjs +46 -0
- package/test/r75-authority-evidence.test.mjs +77 -0
- package/test/r76-pinned-marker-cas.test.mjs +78 -0
- package/test/r77-epoch-grant-cas.test.mjs +51 -0
- package/test/r78-parent-junction-cas.test.mjs +62 -0
- package/test/r79-orphan-revocation-reconcile.test.mjs +88 -0
- package/test/r79-windows-fsync-regression.test.mjs +45 -0
- package/test/r80-durable-epoch-commit.test.mjs +76 -0
- package/test/r82-epoch-lock-owned-cas.test.mjs +93 -0
- package/test/r83-epoch-lock-fileid-cas.test.mjs +92 -0
- package/test/r84-epoch-lock-created-fileid.test.mjs +112 -0
- package/test/r85-epoch-lock-release-lifetime.test.mjs +25 -0
- package/test/r85-epoch-repeat-append.test.mjs +30 -0
- package/test/r86-epoch-cleanup-and-loader.test.mjs +126 -0
- package/test/r87-crashed-orphan-revoke-reconcile.test.mjs +110 -0
- package/test/r88-native-disposition-failure.test.mjs +35 -0
- package/test/r89-privileged-install-boundary.test.mjs +123 -0
- package/test/r92-acl-ace-applicability.test.mjs +45 -0
- package/test/r94-win32-propagation-enum.test.mjs +51 -0
- package/test/r99-privileged-import-closure.test.mjs +45 -0
- package/test/remote-session-cli.test.mjs +69 -9
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {generateKeyPairSync, randomBytes} from 'node:crypto';
|
|
2
|
+
import {mkdtemp, mkdir, writeFile, rm} from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {pathToFileURL} from 'node:url';
|
|
6
|
+
|
|
7
|
+
const oldSource = process.env.DEADBYTE_R121_BASELINE_EPOCH_MODULE;
|
|
8
|
+
const writerSource = process.env.DEADBYTE_TEST_PROTECTED_EPOCH_MODULE;
|
|
9
|
+
if(!oldSource||!writerSource)throw new Error('R121 benchmark requires exact baseline and protected writer modules');
|
|
10
|
+
const currentSource = new URL('../src/authority-continuity-r65.mjs', import.meta.url).href;
|
|
11
|
+
const [baseline,current,writer] = await Promise.all([
|
|
12
|
+
import(pathToFileURL(oldSource).href), import(currentSource), import(pathToFileURL(writerSource).href)
|
|
13
|
+
]);
|
|
14
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r121-readhead-bench-'));
|
|
15
|
+
const quantile=(values,p)=>{
|
|
16
|
+
const sorted=[...values].sort((a,b)=>a-b);
|
|
17
|
+
return Number(sorted[Math.min(sorted.length-1,Math.floor(p*(sorted.length-1)))].toFixed(4));
|
|
18
|
+
};
|
|
19
|
+
try{
|
|
20
|
+
const runtimeDir=path.join(root,'runtime'),trust=path.join(root,'trust');
|
|
21
|
+
await mkdir(runtimeDir);await mkdir(trust);
|
|
22
|
+
const keys=generateKeyPairSync('ed25519');
|
|
23
|
+
await writeFile(path.join(trust,'ed25519-private.pem'),keys.privateKey.export({type:'pkcs8',format:'pem'}),{flag:'wx'});
|
|
24
|
+
await writeFile(path.join(trust,'ed25519-public.pem'),keys.publicKey.export({type:'spki',format:'pem'}),{flag:'wx'});
|
|
25
|
+
const nonce=randomBytes(32).toString('hex');
|
|
26
|
+
const grant=Buffer.from(JSON.stringify({instance_nonce:nonce,fixture:'R121 benchmark'}));
|
|
27
|
+
await writer.createAuthorityEpochStore({runtimeDir}).append({plane:'process',action:'grant',
|
|
28
|
+
grantBytes:grant,grantInstanceNonce:nonce});
|
|
29
|
+
const stores={baseline:baseline.createAuthorityEpochStore({runtimeDir}),
|
|
30
|
+
candidate:current.createAuthorityEpochStore({runtimeDir})};
|
|
31
|
+
for(let i=0;i<100;i++){
|
|
32
|
+
await stores.baseline.readHead({plane:'process'});
|
|
33
|
+
await stores.candidate.readHead({plane:'process'});
|
|
34
|
+
}
|
|
35
|
+
const durations={baseline:[],candidate:[]};
|
|
36
|
+
const rssBefore=process.memoryUsage().rss;
|
|
37
|
+
for(let i=0;i<600;i++){
|
|
38
|
+
const order=i%2?['candidate','baseline']:['baseline','candidate'];
|
|
39
|
+
for(const name of order){
|
|
40
|
+
const start=process.hrtime.bigint();
|
|
41
|
+
const head=await stores[name].readHead({plane:'process'});
|
|
42
|
+
if(head.action!=='grant')throw new Error('R121 benchmark head changed');
|
|
43
|
+
durations[name].push(Number(process.hrtime.bigint()-start)/1e6);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const result={schema:'deadbyte.r121-epoch-readhead-benchmark.v1',samples_per_variant:600,
|
|
47
|
+
workload:'read-only signed head and predecessor verification, one grant event',
|
|
48
|
+
baseline:{p50_ms:quantile(durations.baseline,.5),p95_ms:quantile(durations.baseline,.95),
|
|
49
|
+
max_ms:quantile(durations.baseline,1)},
|
|
50
|
+
candidate:{p50_ms:quantile(durations.candidate,.5),p95_ms:quantile(durations.candidate,.95),
|
|
51
|
+
max_ms:quantile(durations.candidate,1)},
|
|
52
|
+
rss_delta_bytes:process.memoryUsage().rss-rssBefore};
|
|
53
|
+
process.stdout.write(JSON.stringify(result)+'\n');
|
|
54
|
+
}finally{await rm(root,{recursive:true,force:true,maxRetries:8,retryDelay:100});}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {generateKeyPairSync, randomBytes} from 'node:crypto';
|
|
2
|
+
import {mkdtemp, mkdir, writeFile, rm} from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {pathToFileURL} from 'node:url';
|
|
6
|
+
|
|
7
|
+
const oldSource = process.env.DEADBYTE_R124_BASELINE_EPOCH_MODULE;
|
|
8
|
+
const writerSource = process.env.DEADBYTE_TEST_PROTECTED_EPOCH_MODULE;
|
|
9
|
+
if (!oldSource || !writerSource) throw new Error('R124 benchmark requires exact R123 baseline and protected writer modules');
|
|
10
|
+
const [baseline, current, writer] = await Promise.all([
|
|
11
|
+
import(pathToFileURL(oldSource).href),
|
|
12
|
+
import(new URL('../src/authority-continuity-r65.mjs', import.meta.url).href),
|
|
13
|
+
import(pathToFileURL(writerSource).href),
|
|
14
|
+
]);
|
|
15
|
+
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-r124-bench-'));
|
|
16
|
+
const percentile = (values, p) => {
|
|
17
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
18
|
+
return Number(sorted[Math.min(sorted.length - 1, Math.floor(p * (sorted.length - 1)))].toFixed(4));
|
|
19
|
+
};
|
|
20
|
+
try {
|
|
21
|
+
const runtimeDir = path.join(root, 'runtime');
|
|
22
|
+
const trust = path.join(root, 'trust');
|
|
23
|
+
await mkdir(runtimeDir); await mkdir(trust);
|
|
24
|
+
const keys = generateKeyPairSync('ed25519');
|
|
25
|
+
await writeFile(path.join(trust, 'ed25519-private.pem'), keys.privateKey.export({type:'pkcs8', format:'pem'}), {flag:'wx'});
|
|
26
|
+
await writeFile(path.join(trust, 'ed25519-public.pem'), keys.publicKey.export({type:'spki', format:'pem'}), {flag:'wx'});
|
|
27
|
+
const store = writer.createAuthorityEpochStore({runtimeDir});
|
|
28
|
+
for (let i = 0; i < 2; i++) {
|
|
29
|
+
const nonce = randomBytes(32).toString('hex');
|
|
30
|
+
await store.append({plane:'process', action:'grant', grantBytes:Buffer.from(JSON.stringify({instance_nonce:nonce, fixture:`r124-${i}`})), grantInstanceNonce:nonce});
|
|
31
|
+
}
|
|
32
|
+
const stores = {baseline:baseline.createAuthorityEpochStore({runtimeDir}), candidate:current.createAuthorityEpochStore({runtimeDir})};
|
|
33
|
+
for (let i = 0; i < 100; i++) {
|
|
34
|
+
await stores.baseline.readHead({plane:'process'});
|
|
35
|
+
await stores.candidate.readHead({plane:'process'});
|
|
36
|
+
}
|
|
37
|
+
const samples = {baseline:[], candidate:[]};
|
|
38
|
+
for (let i = 0; i < 600; i++) {
|
|
39
|
+
const order = i % 2 ? ['candidate', 'baseline'] : ['baseline', 'candidate'];
|
|
40
|
+
for (const name of order) {
|
|
41
|
+
const start = process.hrtime.bigint();
|
|
42
|
+
const head = await stores[name].readHead({plane:'process'});
|
|
43
|
+
if (head.seq !== 2) throw new Error('R124 benchmark head changed');
|
|
44
|
+
samples[name].push(Number(process.hrtime.bigint() - start) / 1e6);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log(JSON.stringify({schema:'deadbyte.r124-epoch-predecessor-benchmark.v1', samples_per_variant:600,
|
|
48
|
+
workload:'read-only signed two-event head with predecessor verification',
|
|
49
|
+
baseline:{p50_ms:percentile(samples.baseline,.5), p95_ms:percentile(samples.baseline,.95), max_ms:percentile(samples.baseline,1)},
|
|
50
|
+
candidate:{p50_ms:percentile(samples.candidate,.5), p95_ms:percentile(samples.candidate,.95), max_ms:percentile(samples.candidate,1)}}));
|
|
51
|
+
} finally {
|
|
52
|
+
await rm(root, {recursive:true, force:true, maxRetries:8, retryDelay:100});
|
|
53
|
+
}
|
|
Binary file
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "deadbyte.process-policy.v1",
|
|
3
3
|
"enabled": true,
|
|
4
|
-
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
5
|
-
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
4
|
+
"grant_file": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-epoch-transition-audit-dev\\controller\\runtime\\process-grant.json",
|
|
5
|
+
"state_dir": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-epoch-transition-audit-dev\\controller\\runtime\\process-state",
|
|
6
6
|
"native_host": {
|
|
7
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
7
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-epoch-transition-audit-dev\\bin\\deadbyte-process-host.exe",
|
|
8
8
|
"sha256": "daf9cfb371bee88cafbf5597eaa507fec504dbe0b0ddd40d0c3c2e02817f49e4"
|
|
9
9
|
},
|
|
10
10
|
"roots": {
|
|
11
11
|
"core": {
|
|
12
|
-
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-
|
|
12
|
+
"path": "D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-epoch-transition-audit-dev",
|
|
13
13
|
"deny_paths": [
|
|
14
14
|
"node_modules",
|
|
15
15
|
"controller/runtime",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Harness2 mechanism review — isolated development only
|
|
2
|
+
|
|
3
|
+
Source: `D:/Projects/deadbyte-mcp/references/harness2-main` (Apache-2.0). This work uses behavior patterns only; no donor source bytes, Python dependencies, benchmark runner, or third-party assets were copied. Source parent: `D:/Projects/deadbyte-mcp/development/deadbyte-mcp-0.15.48-r108-remote-shell-dev`. The separate, unfinished 0.15.49 release candidate and production remain untouched.
|
|
4
|
+
|
|
5
|
+
| Donor mechanism and source | DEADBYTE comparison | Decision |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Context tripwires in `harness2/core/context.py` reject answer-key paths and grader verdicts before a model sees them. | `src/agent-eval-r42.mjs` already constructs separate generator/judge inputs, but a nested secret or verdict object in `allowed_context` passed directly to the generator. | ADAPT: reject judge-only secret/token values and known judge-result structures before generator invocation. No public API or authority change. |
|
|
8
|
+
| Completion mark bound to harness fingerprint in `harness2/core/runmark.py`; `tests/test_workflow.py` tests resume invalidation after harness drift. | `src/r67-regression-shards.mjs` and `test/r67-regression-shards.test.mjs` already bind regression evidence to source/test fingerprint; R42 evaluation evidence binds suite and subject manifest hashes. | KEEP-OURS: no second completion-marker format. |
|
|
9
|
+
| Bounded rollout/session accounting and duplicate-label rejection in `harness2/core/ledger.py`. | `src/agent-runtime.mjs` already bounds retries; R67 shard planner/reconciler detects duplicate coverage and nonterminal outcomes. | KEEP-OURS: do not graft recursive rollout budget into runtime. |
|
|
10
|
+
| Anonymized candidate selection and evidence-first comparison in `harness2/core/selection.py`; judge isolation in `harness2/core/judge.py`. | `src/agent-eval-r42.mjs` uses generator/judge separation and advisory judge triage; DEADBYTE's task flow does not require a recursive candidate tournament. | DEFER: evaluate only if a future bounded multi-candidate workflow is approved. |
|
|
11
|
+
|
|
12
|
+
RED: `node --test test/harness2-eval-context-isolation.test.mjs` returned 1 pass, 1 fail. The missing rejection allowed a generator-visible context to carry a judge-only token; the generator ran and evaluation returned normally.
|
|
13
|
+
|
|
14
|
+
GREEN after the first fix: focused R42/eval tests returned 9 pass, 0 fail, 0 skip. A later adversarial test proved a second RED: explicit judge-only secrets shorter than 16 characters still reached the generator (3 pass, 1 fail). The minimum-length exemption was removed; identical focused suite now returns 10 pass, 0 fail, 0 skip. Nested and short secrets, task/acceptance leakage and redacted errors are covered. Ordinary shared acceptance terms remain allowed.
|
|
15
|
+
|
|
16
|
+
Adversarial RED 3: a nested `hidden_oracle`/judge-verdict object in `allowed_context` still reached generator dispatch (4 pass, 1 fail). The context-key tripwire rejects only known judge-only structures, not ordinary `expected`/task text. Identical focused suite is GREEN 11 pass, 0 fail, 0 skip.
|
|
17
|
+
|
|
18
|
+
Full-regression attempt: `npm test` returned exit 1 in the ordinary D: development checkout. The observed failing native authority tests reported `EPOCH_NATIVE_SLOT_UNTRUSTED` / `UNTRUSTED_WRITE_ACE` from `src/privileged-install-trust-r89.mjs`; this checkout lacks the protected Windows installation ACL required by those tests. A UAC-approved protected isolated fixture subsequently verified its 2,342-file manifest and ran 1,627 pass / 0 fail / 48 skip / 1,675 total, TAP SHA-256 `41141fee766c5ae94893c869f5d7aa9767c1984827e13c06da30aae496ca0c0a`. That run predates the short-secret repair and is **not** final-byte full-regression proof. Its verify-only package gate passed (2,343 members, isolated doctor/version; transient tarball SHA-256 `2367c95380793ac48629ecfe8296b3d7436cb8672fa11f7b84c877b384fc8fbd`), also on pre-repair bytes.
|
|
19
|
+
|
|
20
|
+
Changed source SHA-256: `src/agent-eval-r42.mjs` = `4e8e442497a15bce43b7d02ade497403d80b86e307473d26c172a0fba3a6d4ea`.
|
|
21
|
+
New test SHA-256: `test/harness2-eval-context-isolation.test.mjs` = `32806f49cf8988bf0c6ff2059759cc7ed1b299ead68124140a68d9d91e52d3eb`.
|
|
22
|
+
Candidate-only process policy root SHA-256: `ae29ee73d118538659aef60385065786ac238f4548d8aeb3ae13b9558761c51f`.
|
|
23
|
+
|
|
24
|
+
This is not release evidence. The 0.15.49 publish path is paused at the user's request; no npm submission, production activation, production grant change, or sealed-slot edit is part of this adaptation.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# DEADBYTE MCP 0.15.49 / R128 (isolated candidate)
|
|
2
|
+
|
|
3
|
+
Status: **INCOMPLETE / NOT RELEASE READY** until independent protected-native Windows full gate, exact package acceptance, and npm authorization.
|
|
4
|
+
|
|
5
|
+
Source lineage:
|
|
6
|
+
- R125 (protected Windows development checkpoint, 1684 PASS / 0 FAIL / 49 historical SKIP, 54 PASS / 0 FAIL / 2 SKIP focused).
|
|
7
|
+
- R126 RED reproduced for incorrectly bound predecessor/successor policy envelopes.
|
|
8
|
+
- R127: read-path checks signed transaction_restore predecessor and successor envelope SHA-256 against present signed event policy envelopes; append rejects mismatched observed predecessor and successor policy envelopes before committing. Early successor mismatch preflight rejects before native lock or event creation.
|
|
9
|
+
- Live Logs: merge of independently hash-checked R124 live-log implementation; missing audit outcomes do not imply success, stream reset warnings, bounded handle-pinned tail reads, partial UTF-8/UTF-16LE, and corrupted audit JSON rejection.
|
|
10
|
+
- R128: version metadata update only in isolated staging for package 0.15.49. Production active pointer stays v0.15.45-34aa8d1241cfb8bf.
|
|
11
|
+
|
|
12
|
+
Observed tests inherited from development only:
|
|
13
|
+
- Live-log regressions 55 PASS / 0 FAIL / 0 SKIP.
|
|
14
|
+
- R126 signed restore reader 3 PASS / 0 FAIL.
|
|
15
|
+
- R127 wrong successor envelope prelock test PASS.
|
|
16
|
+
- R120/R121/R122/R125/R126 focused subset 12 PASS / 0 FAIL. These are not a complete protected-native Windows suite for exact R128 bytes.
|
|
17
|
+
|
|
18
|
+
Unresolved and blocking:
|
|
19
|
+
- R128 must pass its own full protected-native Windows gate, preferably with hash-pinned installed test fixture and independent read-only review.
|
|
20
|
+
- Historical fixtures without policy envelope SHA fields remain supported; they do not prove strict envelope matching.
|
|
21
|
+
- R127 append predecessor check runs after native lock; no successful exact-R128 protected-native append/restore regression yet.
|
|
22
|
+
- Full log rollover cross-gap detection, Windows directory durability, physical power loss and authority hot path performance acceptance remain open.
|
|
23
|
+
- The npm CLI currently returned E401 Unauthorized from npm whoami; owner authentication is necessary before publishing.
|
|
24
|
+
- No production deploy, activation, grant change, npm publish, or dist-tag mutation has been executed by this candidate setup.
|
|
25
|
+
|
|
26
|
+
The previous protected R125 fixture and R125/R126/R127 parent trees must be kept unchanged. Package --verify-only success alone is not release proof.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# R75 isolated authority recovery hardening
|
|
2
|
+
|
|
3
|
+
- Source predecessor: `D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.48-r75`, non-Git, verified 2,304-file manifest SHA-256 `e70561e261610d451d33731a5f19b8439548d7062bed4bf109c4e90f1cd8cf38` before copy.
|
|
4
|
+
- Published source identity remains `0.15.47`; active runtime predecessor observed read-only as `v0.15.45-34aa8d1241cfb8bf`, OFFLINE, with no plane armed. The Autonomous status reason was a broad Coding root, not verified clean-disarmed evidence.
|
|
5
|
+
- This successor keeps `0.15.47/R74` package metadata during behavior verification. It is neither sealed nor release-ready. Production slots, grants, pointer, and historical evidence were not mutated.
|
|
6
|
+
- RED: an `armed=false` Autonomous observation with an unverified reason was activation-safe; an object or Boolean observation without direct grant proof was activation-safe; a startup primary failure was hidden by cleanup failure; malformed ownership marker exited successfully. Each now has a matching focused GREEN test.
|
|
7
|
+
- Authority proof reads each policy by digest, resolves its policy-bound grant location within the isolated runtime, checks grant presence, and requires stable regular-file identity across reads. Host policy v1/v2 remain accepted. The broad-root issuance validator is not used for revocation observation.
|
|
8
|
+
- The existing R75 Autonomous revocation split and startup AggregateError were retained. The new CLI change addresses failure masking in the post-synchronization lifecycle and makes unverified marker cleanup terminal rather than success.
|
|
9
|
+
- R72 isolated controller fixtures verify broad-root absent-grant revocation, signed epoch revocation attempt for a present grant, external-path denial and failed epoch handling. These tests do not operate the production predecessor.
|
|
10
|
+
- Recovery boundary added after independent review: absent owner PID and PID reuse are not ownership evidence. A marker with no verified guardian/owner now remains in place and startup fails closed without touching authority. Identical behavioral RED 0/2 (stale marker proceeded, PID reuse passed), then GREEN 2/2; full remote-session CLI suite 30/30. The verified guardian cleanup path remains intact. This leaves manual reconciliation necessary for an orphan marker without a verified guardian; it does not authorize marker deletion.
|
|
11
|
+
- Remaining proof before `R75_RECOVERY_VERIFIED`: rerun final-byte full regression, Windows gate, manifest, native/bin identity, package inspection, isolated install/doctor, and final independent review.
|
|
12
|
+
- First full verifier run on an intermediate manifest produced 1,574 tests: 1,525 pass, 5 fail, 44 historical skips. The failures were isolated preflight fixture lacking the newly required runtime/policy proof; copied Process policy still pointed to R74; a CLI source-grep harness assumed LF only; and two frozen R72 identity tests asserted version 0.15.46 against current development 0.15.47. The first three fixtures/paths were corrected, and R72-only tests now carry explicit historical-version skip reasons. This first run is not final-byte PASS evidence.
|
|
13
|
+
- A later full verifier on intermediate bytes produced 1,575 tests: 1,527 pass, 2 fail, 46 historical skips. Both failures were R58 tests expecting automatic stale-marker removal from an absent/reused PID. Their isolated fixtures now assert the safer R75 contract instead: no authority command, no marker removal, no foreground READY. Focused rerun 4/4 passed. This intermediate full run is not final-byte PASS evidence.
|
|
14
|
+
- Final production-source gates: durable full verifier `1,575 tests / 1,529 pass / 0 fail / 46 historical/version-scoped skip`, stdout SHA-256 `740f30b8078ecc88142645b4197fcce65abfdc952f086457734769b77dd12a35`; Windows gate focused `56 / 54 pass / 0 fail / 2 historical skip`, repeated full `1,575 / 1,529 pass / 0 fail / 46 skip`, stdout SHA-256 `3b1878039f6b3f123dc2fd4d0bf58b97c51a54309dfa380c50508a35e026e9ba`. Isolated doctor PASS.
|
|
15
|
+
- Exact development package before this handoff update: 2,310 tar members, 12,088,356 bytes, tarball SHA-256 `ee550022b468b998e9d34d2a01e61fb85af9fe89c7812fcffa70d08fca1dda28`, isolated npm install/doctor PASS and canonical npm release `--verify-only` PASS. Publication and activation were not run. Regenerate the development manifest/package after this documentation update before using an exact package claim.
|
|
16
|
+
- Independent review: the owner-PID stale-marker deletion path is closed. Armed epoch validity is verified by the controller and composed with independent pinned grant-file observation, not reverified from signed epoch bytes by this module. `releaseRemoteSession` still has a read-identity then pathname-remove TOCTOU boundary; no production-only runtime or crash proof was performed. These limitations must not be described as proven away.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "deadbyte.r85-epoch-lock-binary.v1",
|
|
3
|
+
"status": "development-candidate",
|
|
4
|
+
"platform": "win32-x64",
|
|
5
|
+
"node_api_version": 8,
|
|
6
|
+
"source": "native/deadbyte-epoch-lock-r85.cpp",
|
|
7
|
+
"source_sha256": "c451c257467b6a141918400b61855bac0e466015c1f3901e8be56ce6d9c32c7f",
|
|
8
|
+
"addon": "bin/deadbyte-epoch-lock-r85.node",
|
|
9
|
+
"addon_sha256": "fb73007ca2303d66120940f192461aac2fa4c45dc4603d920f0878d77e65cfed",
|
|
10
|
+
"build_node_header_version": "25.9.0",
|
|
11
|
+
"build_node_api_header_sha256": "2d4560831e525b47b060ec8a0864ab73993df9e20215ce9f0fe7b24cd31af32a",
|
|
12
|
+
"build_node_import_lib_sha256": "e3577a5a4a772b21646fe05a24d53ce3727395bbbc412f326889ddf7129bc7a9"
|
|
13
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN
|
|
2
|
+
#define NOMINMAX
|
|
3
|
+
#define NAPI_VERSION 8
|
|
4
|
+
#include <windows.h>
|
|
5
|
+
#include <node_api.h>
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <cstddef>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
struct Lease {
|
|
15
|
+
HANDLE leaf = INVALID_HANDLE_VALUE;
|
|
16
|
+
std::vector<HANDLE> ancestors;
|
|
17
|
+
bool renamed = false;
|
|
18
|
+
~Lease() {
|
|
19
|
+
if (leaf != INVALID_HANDLE_VALUE) CloseHandle(leaf);
|
|
20
|
+
for (auto it = ancestors.rbegin(); it != ancestors.rend(); ++it) CloseHandle(*it);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
void Throw(napi_env env, const char* message) { napi_throw_error(env, nullptr, message); }
|
|
25
|
+
void ThrowWin32(napi_env env, const char* message, DWORD code) {
|
|
26
|
+
std::string detail = std::string(message) + " (win32=" + std::to_string(code) + ")";
|
|
27
|
+
Throw(env, detail.c_str());
|
|
28
|
+
}
|
|
29
|
+
bool Text(napi_env env, napi_value value, std::string& out) {
|
|
30
|
+
size_t size = 0;
|
|
31
|
+
if (napi_get_value_string_utf8(env, value, nullptr, 0, &size) != napi_ok || size > 32760) return false;
|
|
32
|
+
out.resize(size);
|
|
33
|
+
size_t actual = 0;
|
|
34
|
+
return napi_get_value_string_utf8(env, value, out.data(), size + 1, &actual) == napi_ok && actual == size &&
|
|
35
|
+
out.find('\0') == std::string::npos;
|
|
36
|
+
}
|
|
37
|
+
bool Wide(const std::string& input, std::wstring& output) {
|
|
38
|
+
if (input.empty()) return false;
|
|
39
|
+
int needed = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
|
40
|
+
if (needed < 1) return false;
|
|
41
|
+
output.resize(needed);
|
|
42
|
+
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, input.data(), static_cast<int>(input.size()),
|
|
43
|
+
output.data(), needed) == needed;
|
|
44
|
+
}
|
|
45
|
+
bool LocalFullPath(const std::wstring& input, std::wstring& output) {
|
|
46
|
+
DWORD needed = GetFullPathNameW(input.c_str(), 0, nullptr, nullptr);
|
|
47
|
+
if (needed < 4 || needed > 32760) return false;
|
|
48
|
+
std::vector<wchar_t> chars(needed);
|
|
49
|
+
if (GetFullPathNameW(input.c_str(), needed, chars.data(), nullptr) != needed - 1) return false;
|
|
50
|
+
output.assign(chars.data());
|
|
51
|
+
if (output.size() < 4 || !((output[0] >= L'A' && output[0] <= L'Z') ||
|
|
52
|
+
(output[0] >= L'a' && output[0] <= L'z')) || output[1] != L':' || output[2] != L'\\' ||
|
|
53
|
+
output.find(L':', 2) != std::wstring::npos || output.back() == L'\\') return false;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
bool PinDirectory(const std::wstring& directory, Lease& lease) {
|
|
57
|
+
HANDLE h = CreateFileW(directory.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
|
58
|
+
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
|
|
59
|
+
if (h == INVALID_HANDLE_VALUE) return false;
|
|
60
|
+
FILE_ATTRIBUTE_TAG_INFO info{};
|
|
61
|
+
if (!GetFileInformationByHandleEx(h, FileAttributeTagInfo, &info, sizeof(info)) ||
|
|
62
|
+
!(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
|
63
|
+
CloseHandle(h); SetLastError(ERROR_REPARSE_TAG_INVALID); return false;
|
|
64
|
+
}
|
|
65
|
+
lease.ancestors.push_back(h);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
bool PinAncestors(const std::wstring& full, Lease& lease) {
|
|
69
|
+
const size_t end = full.find_last_of(L'\\');
|
|
70
|
+
if (end == std::wstring::npos || end < 2) return false;
|
|
71
|
+
std::wstring current = full.substr(0, 3);
|
|
72
|
+
if (!PinDirectory(current, lease)) return false;
|
|
73
|
+
size_t at = 3;
|
|
74
|
+
while (at < end) {
|
|
75
|
+
size_t next = full.find(L'\\', at);
|
|
76
|
+
if (next == std::wstring::npos || next > end) next = end;
|
|
77
|
+
if (next == at) return false;
|
|
78
|
+
current += full.substr(at, next - at);
|
|
79
|
+
if (!PinDirectory(current, lease)) return false;
|
|
80
|
+
current += L'\\';
|
|
81
|
+
at = next + 1;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
DWORD ClosePinnedAncestors(Lease& lease) {
|
|
86
|
+
DWORD firstError = ERROR_SUCCESS;
|
|
87
|
+
for (auto it = lease.ancestors.rbegin(); it != lease.ancestors.rend(); ++it) {
|
|
88
|
+
if (!CloseHandle(*it) && firstError == ERROR_SUCCESS) firstError = GetLastError();
|
|
89
|
+
}
|
|
90
|
+
lease.ancestors.clear();
|
|
91
|
+
return firstError;
|
|
92
|
+
}
|
|
93
|
+
DWORD DisposeHeldLeaf(Lease& lease) {
|
|
94
|
+
if (lease.leaf == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
|
|
95
|
+
FILE_DISPOSITION_INFO disposition{};
|
|
96
|
+
disposition.DeleteFile = TRUE;
|
|
97
|
+
if (!SetFileInformationByHandle(lease.leaf, FileDispositionInfo, &disposition, sizeof(disposition)))
|
|
98
|
+
return GetLastError();
|
|
99
|
+
return ERROR_SUCCESS;
|
|
100
|
+
}
|
|
101
|
+
void ThrowWin32WithCleanup(napi_env env, const char* message, DWORD primary, DWORD cleanup) {
|
|
102
|
+
std::string detail = std::string(message) + " (win32=" + std::to_string(primary);
|
|
103
|
+
if (cleanup != ERROR_SUCCESS) detail += ", cleanup_win32=" + std::to_string(cleanup);
|
|
104
|
+
detail += ")";
|
|
105
|
+
Throw(env, detail.c_str());
|
|
106
|
+
}
|
|
107
|
+
void Finalize(napi_env, void* data, void*) { delete static_cast<Lease*>(data); }
|
|
108
|
+
|
|
109
|
+
napi_value Acquire(napi_env env, napi_callback_info info) {
|
|
110
|
+
size_t argc = 3;
|
|
111
|
+
napi_value argv[3];
|
|
112
|
+
if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc != 3) {
|
|
113
|
+
Throw(env, "epoch native acquire arguments invalid"); return nullptr;
|
|
114
|
+
}
|
|
115
|
+
std::string pathUtf8, nonce;
|
|
116
|
+
void* content = nullptr;
|
|
117
|
+
size_t contentLength = 0;
|
|
118
|
+
bool buffer = false;
|
|
119
|
+
if (!Text(env, argv[0], pathUtf8) || napi_is_buffer(env, argv[1], &buffer) != napi_ok || !buffer ||
|
|
120
|
+
napi_get_buffer_info(env, argv[1], &content, &contentLength) != napi_ok ||
|
|
121
|
+
contentLength < 2 || contentLength > 16384 || !Text(env, argv[2], nonce) || nonce.size() != 32 ||
|
|
122
|
+
!std::all_of(nonce.begin(), nonce.end(), [](unsigned char c) {
|
|
123
|
+
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
|
|
124
|
+
})) {
|
|
125
|
+
Throw(env, "epoch native acquire arguments invalid"); return nullptr;
|
|
126
|
+
}
|
|
127
|
+
std::wstring widePath, full;
|
|
128
|
+
if (!Wide(pathUtf8, widePath) || !LocalFullPath(widePath, full)) {
|
|
129
|
+
Throw(env, "epoch native lock requires ordinary local-drive path"); return nullptr;
|
|
130
|
+
}
|
|
131
|
+
auto lease = std::make_unique<Lease>();
|
|
132
|
+
if (!PinAncestors(full, *lease)) {
|
|
133
|
+
ThrowWin32(env, "epoch native ancestor pin failed closed", GetLastError()); return nullptr;
|
|
134
|
+
}
|
|
135
|
+
DWORD existing = GetFileAttributesW(full.c_str());
|
|
136
|
+
if (existing != INVALID_FILE_ATTRIBUTES) {
|
|
137
|
+
napi_value result;
|
|
138
|
+
napi_get_null(env, &result);
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
DWORD existenceError = GetLastError();
|
|
142
|
+
if (existenceError != ERROR_FILE_NOT_FOUND && existenceError != ERROR_PATH_NOT_FOUND) {
|
|
143
|
+
ThrowWin32(env, "epoch native target existence uncertain", existenceError); return nullptr;
|
|
144
|
+
}
|
|
145
|
+
std::wstring nonceWide(nonce.begin(), nonce.end());
|
|
146
|
+
std::wstring temporary = full + L".r85-" + nonceWide + L".tmp";
|
|
147
|
+
lease->leaf = CreateFileW(temporary.c_str(), GENERIC_READ | GENERIC_WRITE | DELETE | FILE_READ_ATTRIBUTES,
|
|
148
|
+
0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
|
|
149
|
+
if (lease->leaf == INVALID_HANDLE_VALUE) {
|
|
150
|
+
ThrowWin32(env, "epoch native temp create failed closed", GetLastError()); return nullptr;
|
|
151
|
+
}
|
|
152
|
+
FILE_ATTRIBUTE_TAG_INFO tag{};
|
|
153
|
+
if (!GetFileInformationByHandleEx(lease->leaf, FileAttributeTagInfo, &tag, sizeof(tag)) ||
|
|
154
|
+
(tag.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) {
|
|
155
|
+
DWORD error = GetLastError(); DWORD cleanup = DisposeHeldLeaf(*lease);
|
|
156
|
+
ThrowWin32WithCleanup(env, "epoch native leaf identity failed closed", error, cleanup); return nullptr;
|
|
157
|
+
}
|
|
158
|
+
const auto* bytes = static_cast<const unsigned char*>(content);
|
|
159
|
+
size_t written = 0;
|
|
160
|
+
while (written < contentLength) {
|
|
161
|
+
DWORD part = 0;
|
|
162
|
+
if (!WriteFile(lease->leaf, bytes + written, static_cast<DWORD>(contentLength - written), &part, nullptr) || !part) {
|
|
163
|
+
DWORD error = GetLastError(); DWORD cleanup = DisposeHeldLeaf(*lease);
|
|
164
|
+
ThrowWin32WithCleanup(env, "epoch native lock write failed closed", error, cleanup); return nullptr;
|
|
165
|
+
}
|
|
166
|
+
written += part;
|
|
167
|
+
}
|
|
168
|
+
if (!FlushFileBuffers(lease->leaf)) {
|
|
169
|
+
DWORD error = GetLastError(); DWORD cleanup = DisposeHeldLeaf(*lease);
|
|
170
|
+
ThrowWin32WithCleanup(env, "epoch native lock flush failed closed", error, cleanup); return nullptr;
|
|
171
|
+
}
|
|
172
|
+
const size_t renameSize = offsetof(FILE_RENAME_INFO, FileName) + (full.size() + 1) * sizeof(wchar_t);
|
|
173
|
+
std::vector<unsigned char> renameBuffer(renameSize, 0);
|
|
174
|
+
auto* renameInfo = reinterpret_cast<FILE_RENAME_INFO*>(renameBuffer.data());
|
|
175
|
+
renameInfo->ReplaceIfExists = FALSE;
|
|
176
|
+
renameInfo->RootDirectory = nullptr;
|
|
177
|
+
renameInfo->FileNameLength = static_cast<DWORD>(full.size() * sizeof(wchar_t));
|
|
178
|
+
std::memcpy(renameInfo->FileName, full.data(), renameInfo->FileNameLength);
|
|
179
|
+
if (!SetFileInformationByHandle(lease->leaf, FileRenameInfo, renameInfo, static_cast<DWORD>(renameSize))) {
|
|
180
|
+
DWORD error = GetLastError(); DWORD cleanup = DisposeHeldLeaf(*lease);
|
|
181
|
+
if (cleanup == ERROR_SUCCESS && (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS)) {
|
|
182
|
+
napi_value result;
|
|
183
|
+
napi_get_null(env, &result);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
ThrowWin32WithCleanup(env, "epoch native lock rename failed closed", error, cleanup); return nullptr;
|
|
187
|
+
}
|
|
188
|
+
lease->renamed = true;
|
|
189
|
+
napi_value result;
|
|
190
|
+
if (napi_create_external(env, lease.get(), Finalize, nullptr, &result) != napi_ok) {
|
|
191
|
+
DWORD cleanup = DisposeHeldLeaf(*lease);
|
|
192
|
+
ThrowWin32WithCleanup(env, "epoch native handle publication failed closed", ERROR_NOT_ENOUGH_MEMORY, cleanup);
|
|
193
|
+
return nullptr;
|
|
194
|
+
}
|
|
195
|
+
lease.release();
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
napi_value Release(napi_env env, napi_callback_info info) {
|
|
200
|
+
size_t argc = 1;
|
|
201
|
+
napi_value argv[1];
|
|
202
|
+
void* raw = nullptr;
|
|
203
|
+
if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc != 1 ||
|
|
204
|
+
napi_get_value_external(env, argv[0], &raw) != napi_ok || !raw) {
|
|
205
|
+
Throw(env, "epoch native release handle invalid"); return nullptr;
|
|
206
|
+
}
|
|
207
|
+
auto* lease = static_cast<Lease*>(raw);
|
|
208
|
+
if (lease->leaf == INVALID_HANDLE_VALUE || !lease->renamed) {
|
|
209
|
+
Throw(env, "epoch native lock already released"); return nullptr;
|
|
210
|
+
}
|
|
211
|
+
FILE_DISPOSITION_INFO disposition{};
|
|
212
|
+
disposition.DeleteFile = TRUE;
|
|
213
|
+
if (!SetFileInformationByHandle(lease->leaf, FileDispositionInfo, &disposition, sizeof(disposition))) {
|
|
214
|
+
ThrowWin32(env, "epoch native lock disposition failed closed", GetLastError()); return nullptr;
|
|
215
|
+
}
|
|
216
|
+
if (!CloseHandle(lease->leaf)) {
|
|
217
|
+
ThrowWin32(env, "epoch native lock close failed closed", GetLastError()); return nullptr;
|
|
218
|
+
}
|
|
219
|
+
lease->leaf = INVALID_HANDLE_VALUE;
|
|
220
|
+
lease->renamed = false;
|
|
221
|
+
DWORD ancestorCloseError = ClosePinnedAncestors(*lease);
|
|
222
|
+
if (ancestorCloseError != ERROR_SUCCESS) {
|
|
223
|
+
ThrowWin32(env, "epoch native ancestor close failed after lock release", ancestorCloseError);
|
|
224
|
+
return nullptr;
|
|
225
|
+
}
|
|
226
|
+
napi_value result;
|
|
227
|
+
napi_get_undefined(env, &result);
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
napi_value Init(napi_env env, napi_value exports) {
|
|
232
|
+
napi_value acquire, release;
|
|
233
|
+
if (napi_create_function(env, "acquire", NAPI_AUTO_LENGTH, Acquire, nullptr, &acquire) != napi_ok ||
|
|
234
|
+
napi_create_function(env, "release", NAPI_AUTO_LENGTH, Release, nullptr, &release) != napi_ok ||
|
|
235
|
+
napi_set_named_property(env, exports, "acquire", acquire) != napi_ok ||
|
|
236
|
+
napi_set_named_property(env, exports, "release", release) != napi_ok) return nullptr;
|
|
237
|
+
return exports;
|
|
238
|
+
}
|
|
239
|
+
} // namespace
|
|
240
|
+
|
|
241
|
+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deadbyte-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.49",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[Parameter(Mandatory=$true)][ValidatePattern('^[0-9a-f]{64}$')][string]$SourceSha256,
|
|
3
|
+
[string]$NodeHeaderRoot = (Join-Path $env:LOCALAPPDATA 'node-gyp\Cache\25.9.0')
|
|
4
|
+
)
|
|
5
|
+
$ErrorActionPreference='Stop'
|
|
6
|
+
Set-StrictMode -Version Latest
|
|
7
|
+
$root=Split-Path -Parent $PSScriptRoot
|
|
8
|
+
$source=Join-Path $root 'native\deadbyte-epoch-lock-r85.cpp'
|
|
9
|
+
$observed=(Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
10
|
+
if($observed -ne $SourceSha256){throw 'R85 native source SHA mismatch; refusing build'}
|
|
11
|
+
$header=Join-Path $NodeHeaderRoot 'include\node\node_api.h'
|
|
12
|
+
$lib=Join-Path $NodeHeaderRoot 'x64\node.lib'
|
|
13
|
+
if(!(Test-Path -LiteralPath $header -PathType Leaf) -or !(Test-Path -LiteralPath $lib -PathType Leaf)){
|
|
14
|
+
throw 'R85 pinned Node-API headers/import library unavailable; no download fallback'
|
|
15
|
+
}
|
|
16
|
+
$vswhere='C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe'
|
|
17
|
+
if(!(Test-Path -LiteralPath $vswhere)){throw 'vswhere missing'}
|
|
18
|
+
$install=(& $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath | Select-Object -First 1)
|
|
19
|
+
if(!$install){throw 'MSVC x64 C++ toolchain missing'}
|
|
20
|
+
$dev=Join-Path $install 'Common7\Tools\VsDevCmd.bat'
|
|
21
|
+
$bin=Join-Path $root 'bin'
|
|
22
|
+
New-Item -ItemType Directory -Path $bin -Force | Out-Null
|
|
23
|
+
$output=Join-Path $bin 'deadbyte-epoch-lock-r85.node'
|
|
24
|
+
$object=Join-Path $bin 'deadbyte-epoch-lock-r85.obj'
|
|
25
|
+
$include=Join-Path $NodeHeaderRoot 'include\node'
|
|
26
|
+
$common='/nologo /std:c++17 /permissive- /W4 /WX /O2 /MT /EHsc /guard:cf /diagnostics:caret /utf-8 /Brepro /DUNICODE /D_UNICODE /D_WIN32_WINNT=0x0A00 /DNODE_GYP_MODULE_NAME=deadbyte_epoch_lock_r85'
|
|
27
|
+
$compile='cl {0} /LD /I"{1}" "{2}" /Fo:"{3}" /Fe:"{4}" /link /Brepro /INCREMENTAL:NO "{5}"' -f $common,$include,$source,$object,$output,$lib
|
|
28
|
+
$command='call "{0}" -no_logo -arch=x64 -host_arch=x64 && {1}' -f $dev,$compile
|
|
29
|
+
$cmd=if($env:ComSpec){$env:ComSpec}else{Join-Path $env:SystemRoot 'System32\cmd.exe'}
|
|
30
|
+
& $cmd /d /s /c $command
|
|
31
|
+
if($LASTEXITCODE -ne 0){throw "R85 native build failed exit=$LASTEXITCODE"}
|
|
32
|
+
if(!(Test-Path -LiteralPath $output -PathType Leaf)){throw 'R85 native addon missing'}
|
|
33
|
+
$hash=(Get-FileHash -LiteralPath $output -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
34
|
+
Write-Output "R85_NATIVE_BUILD_PASS source_sha256=$observed addon_sha256=$hash path=$output"
|
package/scripts/deploy-r48.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {parseAuthorityObservation} from '../src/remote-authority.mjs';
|
|
|
10
10
|
import {classifyActivationPreflight,classifyAuthorityContinuity,orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
11
11
|
import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
|
|
12
12
|
import {createActivationClaimManager} from '../src/activation-lock-r65.mjs';
|
|
13
|
+
import {observeAuthorityEvidence} from '../src/activation-authority-proof-r75.mjs';
|
|
13
14
|
|
|
14
15
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
15
16
|
const state=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
@@ -57,7 +58,8 @@ async function observeController(){
|
|
|
57
58
|
if(!info.isFile()||info.isSymbolicLink())throw new Error(`activation ${plane} policy must be a regular file`);
|
|
58
59
|
policySha256[plane]=await sha(file);
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
const authorityProof=await observeAuthorityEvidence({stateRoot:state,policyPaths,policySha256,authority:observed.authority});
|
|
62
|
+
return {...observed,authority_proof:authorityProof,policy_sha256:policySha256,policy_paths:policyPaths,active_release_id:active.release_id,active_manifest_sha256:active.manifest_sha256,
|
|
61
63
|
active_generation_manifest_sha256:active.generation_manifest_sha256};
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -109,6 +109,7 @@ async function main() {
|
|
|
109
109
|
|
|
110
110
|
if (isAlreadyPublished) console.log(`Notice: deadbyte-mcp@${version} exists on registry (${existingText}); exact-byte reconciliation is required.`);
|
|
111
111
|
|
|
112
|
+
run(node, [path.join(root, 'scripts', 'r75-npm-bin-integrity-gate.mjs')]);
|
|
112
113
|
console.log('[2/7] Pre-pack gates: verifying working tree manifest and doctor...');
|
|
113
114
|
run(node, [path.join(root, 'scripts', 'release-manifest.mjs'), 'verify']);
|
|
114
115
|
const doctorState=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-release-doctor-'));
|
|
@@ -135,7 +136,8 @@ async function main() {
|
|
|
135
136
|
try {
|
|
136
137
|
console.log('[4/7] Verifying packed artifact file surface against MANIFEST.SHA256 and packaging contract...');
|
|
137
138
|
const manifestText = await readFile(path.join(root, 'MANIFEST.SHA256'), 'utf8');
|
|
138
|
-
const
|
|
139
|
+
const claimData = resuming ? JSON.parse(await readFile(path.join(publicationDir, 'claim.json'), 'utf8')) : null;
|
|
140
|
+
const manifestSha256 = resuming ? claimData.manifest_sha256 : createHash('sha256').update(manifestText).digest('hex');
|
|
139
141
|
const manifestEntries = new Set();
|
|
140
142
|
for (const line of manifestText.split(/\r?\n/)) {
|
|
141
143
|
if (!line) continue;
|
|
@@ -223,12 +225,24 @@ async function main() {
|
|
|
223
225
|
lookup:async()=>{
|
|
224
226
|
try{
|
|
225
227
|
const viewed=parseJson(runNpm(['view',`deadbyte-mcp@${version}`,'version','dist.integrity','dist.shasum','dist.tarball','--json'],{timeout:120000}));
|
|
226
|
-
|
|
228
|
+
const item = Array.isArray(viewed)?viewed[0]:viewed;
|
|
229
|
+
if (item && !item.dist && item['dist.tarball']) {
|
|
230
|
+
item.dist = {
|
|
231
|
+
tarball: item['dist.tarball'],
|
|
232
|
+
shasum: item['dist.shasum'],
|
|
233
|
+
integrity: item['dist.integrity']
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return item;
|
|
227
237
|
}catch(error){if(/E404|404 Not Found/.test(String(error?.message)))return null;throw error;}
|
|
228
238
|
},
|
|
229
|
-
tags:async()=>
|
|
239
|
+
tags:async()=>{
|
|
240
|
+
const parsed=parseJson(runNpm(['view','deadbyte-mcp','dist-tags','--json'],{timeout:120000}));
|
|
241
|
+
return Array.isArray(parsed)?parsed[0]:parsed;
|
|
242
|
+
},
|
|
230
243
|
fetchTarball:async published=>{
|
|
231
|
-
const
|
|
244
|
+
const url = published?.dist?.tarball ?? published?.['dist.tarball'];
|
|
245
|
+
const response=await fetch(url,{signal:AbortSignal.timeout(120000)});
|
|
232
246
|
if(!response.ok)throw Object.assign(new Error(`registry tarball fetch failed status=${response.status}`),{status:response.status});
|
|
233
247
|
return Buffer.from(await response.arrayBuffer());
|
|
234
248
|
}});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
8
|
+
const bin = typeof pkg.bin === 'string' ? { [pkg.name]: pkg.bin } : pkg.bin;
|
|
9
|
+
if (!bin || typeof bin !== 'object' || Array.isArray(bin) || !Object.keys(bin).length)
|
|
10
|
+
throw new Error('BIN_INTEGRITY: package.json.bin missing or invalid');
|
|
11
|
+
const lines = readFileSync(path.join(root, 'MANIFEST.SHA256'), 'utf8').split(/\r?\n/).filter(Boolean);
|
|
12
|
+
const entries = new Map();
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
const m = /^([0-9a-f]{64}) (.+)$/.exec(line);
|
|
15
|
+
if (!m || entries.has(m[2])) throw new Error('BIN_INTEGRITY: invalid or duplicate manifest entry');
|
|
16
|
+
entries.set(m[2], m[1]);
|
|
17
|
+
}
|
|
18
|
+
for (const [name, relRaw] of Object.entries(bin)) {
|
|
19
|
+
if (typeof relRaw !== 'string' || !relRaw || relRaw.includes('\\'))
|
|
20
|
+
throw new Error(`BIN_INTEGRITY: invalid bin path for ${name}`);
|
|
21
|
+
const rel = relRaw.replace(/^\.\//, '');
|
|
22
|
+
const full = path.resolve(root, rel);
|
|
23
|
+
if (!full.startsWith(root + path.sep) || !entries.has(rel))
|
|
24
|
+
throw new Error(`BIN_INTEGRITY: unmanifested or escaping bin '${name}'`);
|
|
25
|
+
const bytes = readFileSync(full);
|
|
26
|
+
const firstLf = bytes.indexOf(0x0a);
|
|
27
|
+
if (firstLf < 2 || bytes[0] !== 0x23 || bytes[1] !== 0x21 || bytes[firstLf - 1] === 0x0d)
|
|
28
|
+
throw new Error(`BIN_INTEGRITY: bin '${name}' must begin with shebang LF (no CRLF)`);
|
|
29
|
+
const sha = createHash('sha256').update(bytes).digest('hex');
|
|
30
|
+
if (sha !== entries.get(rel))
|
|
31
|
+
throw new Error(`BIN_INTEGRITY: manifest and bin bytes differ for '${name}'`);
|
|
32
|
+
process.stdout.write(`BIN_INTEGRITY_PASS ${name} ${rel} ${sha}\n`);
|
|
33
|
+
}
|
|
@@ -18,7 +18,7 @@ const requiredNativePayload = [
|
|
|
18
18
|
'bin/bootstrap-kernel32.exe', 'bin/bootstrap-advapi32.exe',
|
|
19
19
|
'bin/appcontainer-stage.exe', 'bin/bootstrap-exitcode.exe',
|
|
20
20
|
'bin/child-control-stage.exe', 'bin/child-control-probe.exe',
|
|
21
|
-
'bin/deadbyte-exec.exe', 'bin/contained-transform-worker.exe',
|
|
21
|
+
'bin/deadbyte-exec.exe', 'bin/deadbyte-epoch-lock-r85.node', 'bin/contained-transform-worker.exe',
|
|
22
22
|
'bin/contained-reverse-worker.exe', 'bin/deadbyte-tunnel-host.exe', 'bin/WORKER-REGISTRY.txt'
|
|
23
23
|
];
|
|
24
24
|
const requiredBundledDependencyRoots = [
|