@lapxo/bound 0.2.4 → 0.2.6
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/CITATION.cff +1 -1
- package/README.md +20 -3
- package/TARGET.bound +85 -47
- package/dist/cli/accept.js +61 -590
- package/dist/cli/act-result.js +3 -103
- package/dist/cli/args.js +3 -44
- package/dist/cli/custodian.js +2 -29
- package/dist/cli/host.js +4 -32
- package/dist/cli/names.js +1 -12
- package/dist/cli/native-descendant.js +1 -19
- package/dist/cli/native.js +3 -60
- package/dist/cli/observe.js +1 -13
- package/dist/cli/owner.js +3 -139
- package/dist/cli/pass.js +22 -321
- package/dist/cli/place.js +6 -282
- package/dist/cli/release-result.js +3 -80
- package/dist/cli/retained-judgment.js +1 -25
- package/dist/cli/standing-result.js +4 -19
- package/dist/cli/takes.js +10 -112
- package/dist/cli/verb.d.ts +1 -2
- package/dist/cli/verb.js +85 -684
- package/dist/engine/computation.js +2 -0
- package/dist/engine/conformance.js +4 -0
- package/dist/engine/encoding.js +1 -2
- package/dist/engine/index.d.ts +1 -13
- package/dist/engine/index.js +2 -11
- package/dist/engine/object.js +1 -38
- package/dist/engine/ports.js +2 -117
- package/dist/fold/admission-history.js +1 -17
- package/dist/fold/bootstrap-anchor.js +1 -28
- package/dist/fold/ceilings.js +1 -142
- package/dist/fold/claims.js +2 -132
- package/dist/fold/closed.js +24 -235
- package/dist/fold/configures.js +1 -25
- package/dist/fold/digests.js +10 -226
- package/dist/fold/evidence.js +1 -22
- package/dist/fold/fold-worker.js +1 -13
- package/dist/fold/kept.js +12 -173
- package/dist/fold/keys.js +3 -91
- package/dist/fold/laid.js +3 -90
- package/dist/fold/nos.js +1 -48
- package/dist/fold/object.js +1 -2
- package/dist/fold/observed.js +28 -534
- package/dist/fold/opened.js +1 -33
- package/dist/fold/own.js +3 -217
- package/dist/fold/paid.js +1 -92
- package/dist/fold/place-inputs.js +1 -70
- package/dist/fold/places.js +3 -100
- package/dist/fold/rank.js +1 -9
- package/dist/fold/reach.js +1 -83
- package/dist/fold/readers.js +6 -142
- package/dist/fold/receipts.js +1 -47
- package/dist/fold/region-declarations.js +1 -13
- package/dist/fold/region.js +8 -286
- package/dist/fold/release-policy.js +1 -101
- package/dist/fold/resolved.js +6 -145
- package/dist/fold/roles.js +2 -59
- package/dist/fold/second.js +4 -71
- package/dist/fold/shards.js +12 -100
- package/dist/fold/signed.js +3 -81
- package/dist/fold/signers.js +1 -54
- package/dist/fold/stranger.js +6 -24
- package/dist/fold/views.js +1 -66
- package/dist/fold/wire.js +1 -46
- package/dist/fold/zoom.js +3 -81
- package/dist/host/act-cost.js +1 -25
- package/dist/host/adapters/keys/ed25519.js +1 -30
- package/dist/host/adapters/modules/javascript.js +1 -50
- package/dist/host/adapters/transport/http.js +1 -28
- package/dist/host/admitted-place.js +1 -35
- package/dist/host/archive.js +1 -152
- package/dist/host/call-context.js +1 -14
- package/dist/host/capsule-process.js +2 -62
- package/dist/host/capsule.js +9 -171
- package/dist/host/cell-receipts.js +12 -128
- package/dist/host/command-server.js +4 -0
- package/dist/host/commit-result.js +1 -5
- package/dist/host/content-needed.js +1 -8
- package/dist/host/content-store.js +1 -31
- package/dist/host/contextual-place.js +2 -72
- package/dist/host/digest.js +2 -15
- package/dist/host/hash.js +1 -9
- package/dist/host/host-controls.js +1 -19
- package/dist/host/in-process-act.js +3 -84
- package/dist/host/in-process-context.js +3 -91
- package/dist/host/in-process-root.js +6 -84
- package/dist/host/in-process.js +4 -55
- package/dist/host/index.d.ts +1 -15
- package/dist/host/index.js +1 -7
- package/dist/host/io.js +1 -11
- package/dist/host/lifetime.js +1 -79
- package/dist/host/located.js +6 -105
- package/dist/host/native/authority-card-view.js +5 -0
- package/dist/host/native/authority-card.js +1 -0
- package/dist/host/native/bounded-file.js +1 -32
- package/dist/host/native/command.js +1 -91
- package/dist/host/native/control-bridge-worker.js +2 -88
- package/dist/host/native/control-bridge.js +1 -110
- package/dist/host/native/control-selection.js +1 -32
- package/dist/host/native/custody.js +1 -91
- package/dist/host/native/deadline.js +1 -28
- package/dist/host/native/descendants.js +1 -528
- package/dist/host/native/generated-custodian.js +5 -267
- package/dist/host/native/generated.js +1 -379
- package/dist/host/native/image.js +1 -45
- package/dist/host/native/installed-package.d.ts +1 -0
- package/dist/host/native/installed-package.js +5 -0
- package/dist/host/native/jobs.js +2 -148
- package/dist/host/native/loader.js +1 -269
- package/dist/host/native/materialize.js +1 -175
- package/dist/host/native/module-closure.js +1 -130
- package/dist/host/native/module-delivery.d.ts +1 -62
- package/dist/host/native/module-delivery.js +1 -151
- package/dist/host/native/module-inventory.js +2 -0
- package/dist/host/native/module-syntax.js +1 -69
- package/dist/host/native/native-binary.js +1 -115
- package/dist/host/native/native-program.js +1 -72
- package/dist/host/native/node-jobs.js +3 -187
- package/dist/host/native/node-launch.js +1 -159
- package/dist/host/native/preload.js +1 -45
- package/dist/host/native/probes.js +1 -0
- package/dist/host/native/reading-witness-admission.js +5 -0
- package/dist/host/native/reading.js +1 -249
- package/dist/host/native/receipt-environment.js +1 -41
- package/dist/host/native/references.js +1 -30
- package/dist/host/native/root.js +1 -37
- package/dist/host/native/route.js +1 -216
- package/dist/host/native/runtime.js +1 -9
- package/dist/host/native-place-state.js +1 -0
- package/dist/host/native-reader-context.js +1 -8
- package/dist/host/native-receiver.js +1 -25
- package/dist/host/objects.js +1 -38
- package/dist/host/order.js +1 -2
- package/dist/host/persistent-host.js +1 -0
- package/dist/host/physical-lines.d.ts +1 -0
- package/dist/host/physical-lines.js +1 -0
- package/dist/host/place-act-transaction.js +4 -78
- package/dist/host/place-clock-selection.js +5 -124
- package/dist/host/ports/ingress.js +2 -57
- package/dist/host/ports/local-act.js +5 -183
- package/dist/host/ports/place-act.js +1 -134
- package/dist/host/ports/publication.js +1 -56
- package/dist/host/ports/signing.js +1 -74
- package/dist/host/ports/store.js +1 -18
- package/dist/host/ports/transport.js +1 -48
- package/dist/host/process-lifetime.js +1 -17
- package/dist/host/profile-composition.d.ts +1 -0
- package/dist/host/profile-composition.js +2 -0
- package/dist/host/provide.js +1 -183
- package/dist/host/read-only.js +1 -9
- package/dist/host/reading-closures.js +7 -227
- package/dist/host/reading-context.js +1 -53
- package/dist/host/reading-identity-types.js +0 -0
- package/dist/host/reading-identity.js +3 -0
- package/dist/host/reading-preparation.js +6 -0
- package/dist/host/reference-authenticated.d.ts +1 -40
- package/dist/host/reference-authenticated.js +3 -100
- package/dist/host/reference-contracts.js +1 -6
- package/dist/host/reference-export.js +6 -81
- package/dist/host/reference-native-api.d.ts +1 -3
- package/dist/host/reference-native-api.js +1 -3
- package/dist/host/reference-native.js +1 -74
- package/dist/host/reference-profile.js +1 -63
- package/dist/host/reference-standing.js +2 -18
- package/dist/host/reference.js +1 -24
- package/dist/host/refusals.js +2 -43
- package/dist/host/release.js +4 -256
- package/dist/host/root-clock-source.js +1 -307
- package/dist/host/selected-capsules.js +1 -50
- package/dist/host/selected-topos.js +1 -80
- package/dist/host/signature-era.js +1 -12
- package/dist/host/signing.js +2 -48
- package/dist/host/source-content.js +2 -111
- package/dist/host/standing-carrier.js +1 -18
- package/dist/host/standing-projection.js +1 -50
- package/dist/host/timing.js +1 -7
- package/dist/host/walk.js +20 -193
- package/dist/host/work-trace.js +1 -0
- package/dist/judge/late.js +1 -33
- package/dist/judge/take.js +2 -75
- package/dist/judge/widening.js +1 -25
- package/dist/land/act.js +8 -336
- package/dist/land/alias.js +1 -41
- package/dist/land/held.js +1 -23
- package/dist/land/inert.js +1 -33
- package/dist/land/leaving.js +1 -55
- package/dist/land/ledger.js +6 -188
- package/dist/land/owned.js +4 -66
- package/dist/land/sign.js +1 -53
- package/dist/land/standing-pin.js +1 -49
- package/dist/land/vouched.js +1 -112
- package/dist/land/withdraw.js +1 -24
- package/dist/observe/files.js +1 -88
- package/dist/observe/identity.js +1 -61
- package/dist/observe/match.js +1 -8
- package/dist/observe/regions.js +7 -306
- package/dist/observe/run.js +1 -41
- package/dist/observe/runner.js +4 -101
- package/dist/public-types.d.ts +2019 -0
- package/dist/render/capsule.js +1 -30
- package/dist/render/field.js +1 -40
- package/dist/render/names.js +1 -4
- package/dist/render/open.js +1 -61
- package/dist/render/page.js +1 -7
- package/dist/render/receipt-projection.js +1 -0
- package/dist/render/receipts.js +1 -47
- package/dist/render/shape.js +1 -74
- package/dist/render/target.js +4 -52
- package/dist/render/view.js +7 -197
- package/dist/vendor/bundle.json +1 -0
- package/dist/vendor/library14.js +1 -0
- package/dist/vendor/object-provider.js +42 -0
- package/dist/vendor/runtime.js +61 -0
- package/dist/wasm/adapter.d.ts +1 -51
- package/dist/wasm/adapter.js +1 -180
- package/dist/wasm/node-worker.js +1 -13
- package/dist/wasm/object.js +1 -76
- package/dist/wasm/worker.js +1 -7
- package/docs/contracts/authenticated-reference.publication.json +1 -1
- package/docs/contracts/authority-preflight.md +17 -0
- package/docs/contracts/authority-preflight.publication.json +25 -0
- package/docs/contracts/closure-delivery.md +1 -1
- package/docs/contracts/closure-delivery.publication.json +1 -1
- package/docs/contracts/conformance.json +142 -0
- package/docs/contracts/determinism.bound +9 -0
- package/docs/contracts/engine-object-projection.publication.json +1 -1
- package/docs/contracts/engine.md +21 -1
- package/docs/contracts/installed-package.md +21 -0
- package/docs/contracts/installed-package.publication.json +25 -0
- package/docs/contracts/materialized-leaf-sequence.md +15 -0
- package/docs/contracts/materialized-leaf-sequence.publication.json +25 -0
- package/docs/contracts/module-inventory.md +15 -0
- package/docs/contracts/module-inventory.publication.json +25 -0
- package/docs/contracts/native-host.md +29 -1
- package/docs/contracts/native-host.publication.json +4 -4
- package/docs/contracts/native-probes.md +13 -0
- package/docs/contracts/native-probes.publication.json +25 -0
- package/docs/contracts/native-publication.inventory.json +10 -1
- package/docs/contracts/native-view-batch.md +17 -0
- package/docs/contracts/native-view-batch.publication.json +25 -0
- package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
- package/docs/contracts/physical-lines.md +19 -0
- package/docs/contracts/physical-lines.publication.json +25 -0
- package/docs/contracts/profile-composition.md +17 -0
- package/docs/contracts/profile-composition.publication.json +25 -0
- package/docs/contracts/profile-preparation-native-meaning.md +9 -0
- package/docs/contracts/publication.md +12 -0
- package/docs/contracts/reading-identity.md +61 -0
- package/docs/contracts/reading-identity.publication.json +25 -0
- package/docs/contracts/reading-witness-admission.md +49 -0
- package/docs/contracts/readme.publication.json +1 -1
- package/docs/contracts/reference-port.md +11 -0
- package/docs/contracts/reference-port.publication.json +1 -1
- package/docs/contracts/refusals.bound +346 -94
- package/docs/contracts/refusals.md +119 -39
- package/docs/contracts/refusals.publication.json +2 -2
- package/docs/contracts/views.md +8 -0
- package/docs/reference.md +9 -0
- package/licenses/wasm/{fast-text-encoding.txt → NOTICE.txt} +111 -0
- package/package.json +84 -34
- package/receipts.bound +12952 -10449
- package/samples/authority-preflight/no.json +6 -0
- package/samples/authority-preflight/yes.json +30 -0
- package/samples/engine-alphabet/reader.js +3 -1457
- package/samples/engine-alphabet/receipts.bound +7 -7
- package/samples/engine-interval/reader.js +3 -1457
- package/samples/engine-interval/receipts.bound +7 -7
- package/samples/installed-package/consumer.mjs +29 -0
- package/samples/installed-package/fixture.json +48 -0
- package/samples/installed-package/no.json +10 -0
- package/samples/installed-package/yes.json +52 -0
- package/samples/materialized-leaf-sequence/no.json +11 -0
- package/samples/materialized-leaf-sequence/yes.json +10 -0
- package/samples/module-inventory/yes.json +28 -0
- package/samples/native/physical-line-reader.mjs +1 -0
- package/samples/native/physical-line-reader.source.mjs +13 -0
- package/samples/native-host/no.json +6 -1
- package/samples/native-host/yes.json +18 -1
- package/samples/native-probes/inputs/inspection.json +28 -0
- package/samples/native-probes/no.json +7 -0
- package/samples/native-probes/yes.json +28 -0
- package/samples/native-view-batch/no.json +14 -0
- package/samples/native-view-batch/yes.json +10 -0
- package/samples/no/engine-order/reader.js +3 -1457
- package/samples/no/module-inventory/no.json +10 -0
- package/samples/physical-lines/no.json +5 -0
- package/samples/physical-lines/yes.json +9 -0
- package/samples/profile-composition/no.json +6 -0
- package/samples/profile-composition/yes.json +14 -0
- package/samples/reading-identity/no.json +26 -0
- package/samples/reading-identity/yes.json +69 -0
- package/vectors/native/authority-preflight.vector.json +55 -0
- package/vectors/native/installed-package.vector.json +31 -0
- package/vectors/native/materialized-leaf-sequence.vector.json +31 -0
- package/vectors/native/module-inventory.vector.json +56 -0
- package/vectors/native/native-host.vector.json +214 -0
- package/vectors/native/native-probes.vector.json +31 -0
- package/vectors/native/native-view-batch.vector.json +31 -0
- package/vectors/native/physical-lines.vector.json +105 -0
- package/vectors/native/profile-composition.vector.json +53 -0
- package/vectors/native/reading-identity.vector.json +139 -0
- package/vectors/native/refusals.vector.json +138 -0
- package/vendor/syntax/LICENSE.md +7 -0
- package/vendor/syntax/parser.js +1 -0
- package/.github/workflows/release.yml +0 -67
- package/.github/workflows/test.yml +0 -35
- package/dist/engine/object.d.ts +0 -17
- package/dist/engine/ports.d.ts +0 -37
- package/dist/fold/claims.d.ts +0 -29
- package/dist/host/call-context.d.ts +0 -20
- package/dist/host/commit-result.d.ts +0 -6
- package/dist/host/in-process-act.d.ts +0 -82
- package/dist/host/in-process-context.d.ts +0 -46
- package/dist/host/in-process-root.d.ts +0 -117
- package/dist/host/in-process.d.ts +0 -41
- package/dist/host/native/module-closure.d.ts +0 -35
- package/dist/host/native-receiver.d.ts +0 -19
- package/dist/host/place-act-transaction.d.ts +0 -24
- package/dist/host/place-clock-selection.d.ts +0 -55
- package/dist/host/ports/local-act.d.ts +0 -44
- package/dist/host/ports/place-act.d.ts +0 -49
- package/dist/host/ports/publication.d.ts +0 -10
- package/dist/host/reference-export.d.ts +0 -33
- package/dist/host/reference-native.d.ts +0 -30
- package/dist/host/reference-profile.d.ts +0 -23
- package/dist/host/refusals.d.ts +0 -14
- package/dist/host/root-clock-source.d.ts +0 -86
- package/licenses/wasm/jitl-quickjs-ffi-types.txt +0 -21
- package/licenses/wasm/jitl-quickjs-wasmfile-release-sync.txt +0 -47
- package/licenses/wasm/quickjs-emscripten-core.txt +0 -21
- package/licenses/wasm/topos.txt +0 -21
- /package/{licenses/wasm/bound.txt → dist/vendor/28cba430228db1c04f446c93f81b09a2705d6bf3e222d35adbe5a4cee4061884.LICENSE} +0 -0
- /package/{licenses/wasm/obligations.txt → dist/vendor/7aca161279d14bc0e054dcc281cd110aeb95be2e026b42fdf527a260e58b2aae.LICENSE} +0 -0
package/dist/host/lifetime.js
CHANGED
|
@@ -1,79 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const deadline = BigInt(process.argv[2] ?? '0'), responseBytes = Number(process.argv[3]);
|
|
3
|
-
if (deadline <= 0n || !Number.isSafeInteger(responseBytes) || responseBytes <= 0)
|
|
4
|
-
throw Error('REFUSE·reader invalid process limits');
|
|
5
|
-
let child, finished = false, failure, size = 0;
|
|
6
|
-
const stdout = [], stderr = [];
|
|
7
|
-
const grouped = process.platform !== 'win32';
|
|
8
|
-
const cancel = () => { if (child?.pid === undefined)
|
|
9
|
-
return; try {
|
|
10
|
-
if (grouped)
|
|
11
|
-
process.kill(-child.pid, 'SIGKILL');
|
|
12
|
-
else
|
|
13
|
-
child.kill('SIGKILL');
|
|
14
|
-
}
|
|
15
|
-
catch { } };
|
|
16
|
-
const finish = (status, signal) => {
|
|
17
|
-
if (finished)
|
|
18
|
-
return;
|
|
19
|
-
finished = true;
|
|
20
|
-
clearTimeout(timer);
|
|
21
|
-
cancel();
|
|
22
|
-
child?.stdin?.destroy();
|
|
23
|
-
child?.stdout?.destroy();
|
|
24
|
-
child?.stderr?.destroy();
|
|
25
|
-
process.stdin.destroy();
|
|
26
|
-
const bytes = failure ? Buffer.alloc(0) : Buffer.concat(stdout);
|
|
27
|
-
const result = { stdout: bytes.toString('utf8'), stdoutBytes: bytes.toString('base64'), stderr: Buffer.concat(stderr).toString('utf8'), status, signal, ...(failure ? { failure } : {}) };
|
|
28
|
-
process.stdout.write(JSON.stringify(result));
|
|
29
|
-
};
|
|
30
|
-
const refuse = (why) => { if (failure && why === 'response exceeds declared byte bound' && !failure.includes(why))
|
|
31
|
-
failure += ' · ' + why;
|
|
32
|
-
else
|
|
33
|
-
failure ?? (failure = why); finish(null, 'SIGKILL'); };
|
|
34
|
-
// A closed input pipe can precede the queued stderr/close events of an early
|
|
35
|
-
// rejecting child. Cancel immediately, but drain only until the SAME deadline.
|
|
36
|
-
const inputFailed = (why) => { if (finished)
|
|
37
|
-
return; failure ?? (failure = why); stdout.length = 0; cancel(); child?.stdin?.destroy(); process.stdin.destroy(); if (process.hrtime.bigint() >= deadline)
|
|
38
|
-
finish(null, 'SIGKILL'); };
|
|
39
|
-
const remaining = Number(deadline - process.hrtime.bigint()) / 1e6;
|
|
40
|
-
const timer = setTimeout(() => refuse('declared timeout exhausted'), Math.max(0, remaining));
|
|
41
|
-
process.once('SIGTERM', () => refuse('interrupted'));
|
|
42
|
-
try {
|
|
43
|
-
if (remaining <= 0)
|
|
44
|
-
refuse('declared timeout exhausted');
|
|
45
|
-
const chunks = [];
|
|
46
|
-
for await (const chunk of process.stdin) {
|
|
47
|
-
if (finished)
|
|
48
|
-
break;
|
|
49
|
-
chunks.push(Buffer.from(chunk));
|
|
50
|
-
}
|
|
51
|
-
if (!finished) {
|
|
52
|
-
const request = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
53
|
-
if (request.limits.responseBytes !== responseBytes || !Number.isSafeInteger(request.limits.timeoutMs) || request.limits.timeoutMs <= 0)
|
|
54
|
-
throw Error('invalid process limits');
|
|
55
|
-
// Parsing is synchronous; recheck before spawning even if the timer has not
|
|
56
|
-
// had a turn to run. An expired handoff must never start the command.
|
|
57
|
-
if (process.hrtime.bigint() >= deadline) {
|
|
58
|
-
refuse('declared timeout exhausted');
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
child = spawn(request.command, [...request.args], { cwd: request.cwd, env: request.env, detached: grouped, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
62
|
-
const receive = (items, bytes) => { if (finished)
|
|
63
|
-
return; size += bytes.length; if (size > responseBytes)
|
|
64
|
-
refuse('response exceeds declared byte bound');
|
|
65
|
-
else if (!failure || items === stderr)
|
|
66
|
-
items.push(bytes); };
|
|
67
|
-
child.stdout.on('data', (b) => receive(stdout, b));
|
|
68
|
-
child.stderr.on('data', (b) => receive(stderr, b));
|
|
69
|
-
child.stdin.on('error', error => inputFailed('input failed: ' + error.message));
|
|
70
|
-
child.once('error', error => refuse(error.message));
|
|
71
|
-
child.once('close', finish);
|
|
72
|
-
child.stdin.end(request.input);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
if (!finished)
|
|
78
|
-
refuse('input unavailable or malformed');
|
|
79
|
-
}
|
|
1
|
+
var I=Object.defineProperty;var d=(s,e)=>I(s,"name",{value:e,configurable:!0});import{spawn as y}from"node:child_process";const u=BigInt(process.argv[2]??"0"),a=Number(process.argv[3]);if(u<=0n||!Number.isSafeInteger(a)||a<=0)throw Error("REFUSE\xB7reader invalid process limits");let t,o=!1,i,g=0;const f=[],l=[],h=process.platform!=="win32",b=d(()=>{if(t?.pid!==void 0)try{h?process.kill(-t.pid,"SIGKILL"):t.kill("SIGKILL")}catch{}},"cancel"),p=d((s,e)=>{if(o)return;o=!0,clearTimeout(v),b(),t?.stdin?.destroy(),t?.stdout?.destroy(),t?.stderr?.destroy(),process.stdin.destroy();const c=i?Buffer.alloc(0):Buffer.concat(f),r={stdout:c.toString("utf8"),stdoutBytes:c.toString("base64"),stderr:Buffer.concat(l).toString("utf8"),status:s,signal:e,...i?{failure:i}:{}};process.stdout.write(JSON.stringify(r))},"finish"),n=d(s=>{i&&s==="response exceeds declared byte bound"&&!i.includes(s)?i+=" \xB7 "+s:i??(i=s),p(null,"SIGKILL")},"refuse"),B=d(s=>{o||(i??(i=s),f.length=0,b(),t?.stdin?.destroy(),process.stdin.destroy(),process.hrtime.bigint()>=u&&p(null,"SIGKILL"))},"inputFailed"),S=Number(u-process.hrtime.bigint())/1e6,v=setTimeout(()=>n("declared timeout exhausted"),Math.max(0,S));process.once("SIGTERM",()=>n("interrupted"));try{S<=0&&n("declared timeout exhausted");const s=[];for await(const e of process.stdin){if(o)break;s.push(Buffer.from(e))}if(!o){const e=JSON.parse(Buffer.concat(s).toString("utf8"));if(e.limits.responseBytes!==a||!Number.isSafeInteger(e.limits.timeoutMs)||e.limits.timeoutMs<=0)throw Error("invalid process limits");if(process.hrtime.bigint()>=u)n("declared timeout exhausted");else{t=y(e.command,[...e.args],{cwd:e.cwd,env:e.env,detached:h,stdio:["pipe","pipe","pipe"]});const c=d((r,m)=>{o||(g+=m.length,g>a?n("response exceeds declared byte bound"):(!i||r===l)&&r.push(m))},"receive");t.stdout.on("data",r=>c(f,r)),t.stderr.on("data",r=>c(l,r)),t.stdin.on("error",r=>B("input failed: "+r.message)),t.once("error",r=>n(r.message)),t.once("close",p),t.stdin.end(e.input)}}}catch{o||n("input unavailable or malformed")}
|
package/dist/host/located.js
CHANGED
|
@@ -1,105 +1,6 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
import { readFileSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
import { pathToFileURL } from 'node:url';
|
|
12
|
-
import { pinExport, provide } from "./provide.js";
|
|
13
|
-
const wanted = (role) => role === 'reader' ? ['observe', 'run'] : [role];
|
|
14
|
-
const foreign = (role) => ['render', 'receipt', 'observe', 'run'].filter((name) => !wanted(role).includes(name));
|
|
15
|
-
const isFn = (module, name) => typeof module[name] === 'function';
|
|
16
|
-
const text = (error) => (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').trim();
|
|
17
|
-
/**
|
|
18
|
-
* Wire and capsule of the world, from `dep/topos` (or the lock's `uses/topos` pin). The host never spells where that
|
|
19
|
-
* package lies.
|
|
20
|
-
*/
|
|
21
|
-
const sdk = async () => {
|
|
22
|
-
provide();
|
|
23
|
-
const [wireAt, capsuleAt] = [pinExport('topos', './wire'), pinExport('topos', './capsule')];
|
|
24
|
-
if (!wireAt || !capsuleAt)
|
|
25
|
-
throw new Error('REFUSE·pin dep/topos names no wire or capsule');
|
|
26
|
-
const [wire, capsule] = await Promise.all([import(__rewriteRelativeImportExtension(pathToFileURL(wireAt).href)), import(__rewriteRelativeImportExtension(pathToFileURL(capsuleAt).href))]);
|
|
27
|
-
return {
|
|
28
|
-
CAPSULE: String(wire['CAPSULE'] ?? ''),
|
|
29
|
-
PROTOCOL: String(wire['PROTOCOL'] ?? ''),
|
|
30
|
-
parse: wire['parse'],
|
|
31
|
-
declarationOf: capsule['declarationOf'],
|
|
32
|
-
readers: capsule['readers'],
|
|
33
|
-
receiptShell: capsule['receiptShell'],
|
|
34
|
-
shell: capsule['shell'],
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* The host loads a world from its lock: the lock read once, each region imported from its one file by name and role into
|
|
39
|
-
* the shell of its kind. A region that fails is one line on stderr and is left out; the regions that loaded still form
|
|
40
|
-
* the shell. A world with no region of a kind has no shell of it. `load` is the host's child entry, the same load with
|
|
41
|
-
* its places read from the arguments the host spawned it with.
|
|
42
|
-
*/
|
|
43
|
-
/** A capsule that ships one render module: the host calls it per region and hands it the lines. The module does not read. */
|
|
44
|
-
const pagesOf = async (root, file) => {
|
|
45
|
-
const { CAPSULE, PROTOCOL, declarationOf, shell } = await sdk();
|
|
46
|
-
const lines = readFileSync(join(root, CAPSULE), 'utf8').split('\n').filter((line) => line.startsWith(PROTOCOL));
|
|
47
|
-
const { regions } = declarationOf(lines);
|
|
48
|
-
const module = await import(__rewriteRelativeImportExtension(pathToFileURL(join(root, file)).href));
|
|
49
|
-
const render = module['render'] ?? module['default']?.['render'];
|
|
50
|
-
if (typeof render !== 'function')
|
|
51
|
-
return {};
|
|
52
|
-
const held = Object.fromEntries(Object.entries(regions).map(([name, reads]) => [name, {
|
|
53
|
-
reads,
|
|
54
|
-
region: (asked) => render({ ...asked, region: name }),
|
|
55
|
-
}]));
|
|
56
|
-
return held && Object.keys(held).length ? { render: shell(held) } : {};
|
|
57
|
-
};
|
|
58
|
-
export const locate = async (root, dir, ext) => {
|
|
59
|
-
if (ext === 'render')
|
|
60
|
-
return pagesOf(root, dir);
|
|
61
|
-
const { CAPSULE, PROTOCOL, parse, declarationOf, readers, receiptShell, shell } = await sdk();
|
|
62
|
-
const lines = readFileSync(join(root, CAPSULE), 'utf8').split('\n').filter((line) => line.startsWith(PROTOCOL));
|
|
63
|
-
const field = (line, name) => ((got) => (got.kind === 'fact' ? got.value.fields[name] ?? '' : ''))(parse(line));
|
|
64
|
-
const { regions, writes } = declarationOf(lines);
|
|
65
|
-
const reader = new Set(lines.filter((line) => field(line, 'measure') === 'writes').map((line) => field(line, 'scope').slice('region/'.length)));
|
|
66
|
-
const kind = (name) => (reader.has(name) ? 'reader' : writes[name] ?? '');
|
|
67
|
-
const region = async (name, reads, role) => {
|
|
68
|
-
let module;
|
|
69
|
-
try {
|
|
70
|
-
module = await import(__rewriteRelativeImportExtension(pathToFileURL(join(root, dir, `${name}${ext}`)).href));
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
process.stderr.write(`LOAD ${name} · module error · ${text(error)}\n`);
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
const has = wanted(role).filter((key) => isFn(module, key));
|
|
77
|
-
const exported = foreign(role).filter((key) => isFn(module, key));
|
|
78
|
-
if (!has.length && exported.length) {
|
|
79
|
-
process.stderr.write(`LOAD ${name} · role mismatch · ${role} exports ${exported.join('|')}\n`);
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
if (!has.length) {
|
|
83
|
-
process.stderr.write(`LOAD ${name} · missing export · ${wanted(role)[0]}\n`);
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
const picked = role === 'reader' ? { observe: module['observe'], run: module['run'] } : { region: module[role] };
|
|
87
|
-
return [name, { reads, ...picked }];
|
|
88
|
-
};
|
|
89
|
-
const loaded = async (role) => {
|
|
90
|
-
const held = [];
|
|
91
|
-
for (const [name, reads] of Object.entries(regions)) {
|
|
92
|
-
if (kind(name) !== role)
|
|
93
|
-
continue;
|
|
94
|
-
const row = await region(name, reads, role);
|
|
95
|
-
if (row)
|
|
96
|
-
held.push(row);
|
|
97
|
-
}
|
|
98
|
-
return held.length ? Object.fromEntries(held) : undefined;
|
|
99
|
-
};
|
|
100
|
-
const render = await loaded('render');
|
|
101
|
-
const receipt = await loaded('receipt');
|
|
102
|
-
const reading = await loaded('reader');
|
|
103
|
-
return { render: render && shell(render), receipt: receipt && receiptShell(receipt), ...(reading ? readers(reading) : {}) };
|
|
104
|
-
};
|
|
105
|
-
export const load = () => locate(process.argv[3] ?? '', process.argv[4] ?? '', process.argv[5] ?? '');
|
|
1
|
+
var W=Object.defineProperty;var n=(e,t)=>W(e,"name",{value:t,configurable:!0});var w=function(e,t){return typeof e=="string"&&/^\.\.?\//.test(e)?e.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i,function(i,o,p,d,f){return o?t?".jsx":".js":p&&(!d||!f)?i:p+d+"."+f.toLowerCase()+"js"}):e};import{hostOutput as j}from"./call-context.js";import{readFileSync as R}from"node:fs";import{join as E}from"node:path";import{pathToFileURL as L}from"node:url";import{pinExport as U,provide as q}from"./provide.js";const b=n(e=>e==="reader"?["observe","run"]:[e],"wanted"),z=n(e=>["render","receipt","observe","run"].filter(t=>!b(e).includes(t)),"foreign"),k=n((e,t)=>typeof e[t]=="function","isFn"),B=n(e=>(e instanceof Error?e.message:String(e)).replace(/\s+/g," ").trim(),"text"),T=n(async()=>{q();const[e,t]=[U("topos","./wire"),U("topos","./capsule")];if(!e||!t)throw new Error("REFUSE\xB7pin dep/topos names no wire or capsule");const[i,o]=await Promise.all([import(w(L(e).href)),import(w(L(t).href))]);return{CAPSULE:String(i.CAPSULE??""),PROTOCOL:String(i.PROTOCOL??""),parse:i.parse,declarationOf:o.declarationOf,readers:o.readers,receiptShell:o.receiptShell,shell:o.shell}},"sdk"),G=n(async(e,t)=>{const{CAPSULE:i,PROTOCOL:o,declarationOf:p,shell:d}=await T(),f=R(E(e,i),"utf8").split(`
|
|
2
|
+
`).filter(a=>a.startsWith(o)),{regions:S}=p(f),O=await import(w(L(E(e,t)).href)),g=O.render??O.default?.render;if(typeof g!="function")return{};const u=Object.fromEntries(Object.entries(S).map(([a,v])=>[a,{reads:v,region:n(y=>g({...y,region:a}),"region")}]));return u&&Object.keys(u).length?{render:d(u)}:{}},"pagesOf"),H=n(async(e,t,i)=>{if(i==="render")return G(e,t);const{CAPSULE:o,PROTOCOL:p,parse:d,declarationOf:f,readers:S,receiptShell:O,shell:g}=await T(),u=R(E(e,o),"utf8").split(`
|
|
3
|
+
`).filter(r=>r.startsWith(p)),a=n((r,l)=>(s=>s.kind==="fact"?s.value.fields[l]??"":"")(d(r)),"field"),{regions:v,writes:y}=f(u),F=new Set(u.filter(r=>a(r,"measure")==="writes").map(r=>a(r,"scope").slice(7))),_=n(r=>F.has(r)?"reader":y[r]??"","kind"),D=n(async(r,l,s)=>{let c;try{c=await import(w(L(E(e,t,`${r}${i}`)).href))}catch(m){j("error",`LOAD ${r} \xB7 module error \xB7 ${B(m)}
|
|
4
|
+
`);return}const h=b(s).filter(m=>k(c,m)),P=z(s).filter(m=>k(c,m));if(!h.length&&P.length){j("error",`LOAD ${r} \xB7 role mismatch \xB7 ${s} exports ${P.join("|")}
|
|
5
|
+
`);return}if(!h.length){j("error",`LOAD ${r} \xB7 missing export \xB7 ${b(s)[0]}
|
|
6
|
+
`);return}const I=s==="reader"?{observe:c.observe,run:c.run}:{region:c[s]};return[r,{reads:l,...I}]},"region"),$=n(async r=>{const l=[];for(const[s,c]of Object.entries(v)){if(_(s)!==r)continue;const h=await D(s,c,r);h&&l.push(h)}return l.length?Object.fromEntries(l):void 0},"loaded"),x=await $("render"),A=await $("receipt"),C=await $("reader");return{render:x&&g(x),receipt:A&&O(A),...C?S(C):{}}},"locate"),X=n(()=>H(process.argv[3]??"",process.argv[4]??"",process.argv[5]??""),"load");export{X as load,H as locate};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var v=Object.defineProperty;var u=(r,a)=>v(r,"name",{value:a,configurable:!0});import{authenticatedStanding as S}from"../admitted-place.js";import{withoutEffects as F}from"../read-only.js";import{withHostCall as R}from"../call-context.js";import{sha256 as m,isSha256 as x}from"../hash.js";import{BoundRefusal as e}from"../refusals.js";import{fieldOf as o}from"../../fold/claims.js";import{storeOf as A,blobAt as C}from"../../land/ledger.js";import{readBoundedFile as N}from"./bounded-file.js";import{parseNativeAuthorityCardRequest as j,prepareNativeAuthorityCard as B}from"./authority-card.js";async function K(r,a,i){const f={instrumentName:a.name,runtime:structuredClone(a.runtime),output:u(()=>{},"output")},w=R(f,()=>F(()=>{if(typeof i!="string"||!i||/[\s@]/.test(i))throw new e("authority-card/selection","REFUSE\xB7authority-card/selection requires an explicit request scope");const g=S(r),E=u((t,n,h)=>{const q=g.filter(s=>o(s,"scope")===t&&o(s,"measure")===n&&o(s,"role")==="writes"&&o(s,"form")===h&&o(s,"value")!=="withdraw");if(q.length!==1)throw new e("authority-card/selection","REFUSE\xB7authority-card/selection requires one admitted "+n+" at "+t);return o(q[0],"value")},"select"),c=E(i,"authority-card-request","alphabet");if(!x(c))throw new e("authority-card/selection","REFUSE\xB7authority-card/selection requires a SHA-256 request");const b=u(t=>{const n=/^0\.\.([1-9][0-9]*)$/.exec(E(i+"/"+t,t,"interval")),h=Number(n?.[1]);if(!n||!Number.isSafeInteger(h))throw new e("authority-card/budget","REFUSE\xB7authority-card/budget requires a finite "+t+" bound");return h},"limit"),d={bytes:b("bytes"),items:b("items")};let y;try{y=N(C(A(r),c),d.bytes,i)}catch(t){throw new e("authority-card/content","REFUSE\xB7authority-card/content "+(t instanceof Error?t.message:"request bytes unavailable"))}if(m(y)!==c)throw new e("authority-card/integrity","REFUSE\xB7authority-card/integrity request bytes differ from admitted selection");let p;try{p=new TextDecoder("utf-8",{fatal:!0}).decode(y)}catch{throw new e("authority-card/integrity","REFUSE\xB7authority-card/integrity request is not UTF-8")}const l=j(p,c,d);if(l.limits.bytes>d.bytes||l.limits.items>d.items)throw new e("authority-card/budget","REFUSE\xB7authority-card/budget request exceeds admitted selector limits");return{request:l,standing:m(g.join(`
|
|
2
|
+
`)+`
|
|
3
|
+
`)}})),U=await B({...w.request,root:r,instrument:a});if(R(f,()=>F(()=>m(S(r).join(`
|
|
4
|
+
`)+`
|
|
5
|
+
`)))!==w.standing)throw new e("authority-card/selection","REFUSE\xB7authority-card/selection changed during preflight");return U}u(K,"nativeAuthorityCardView");export{K as nativeAuthorityCardView};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var me=Object.defineProperty;var h=(i,y)=>me(i,"name",{value:y,configurable:!0});import{readFileSync as P,realpathSync as Q}from"node:fs";import{join as V}from"node:path";import{library14_authorityAtEpoch as le,library14_parse as pe,library14_LOCK as Y}from"../../vendor/runtime.js";import{library5_encodeAuthorityCard as ge,library5_authorityCardStatement as ye,library5_authorityCardAnswer as ke}from"../../vendor/runtime.js";import{admissionHistoryAt as Z,admissionAnchor as be}from"../../fold/admission-history.js";import{projectAuthenticatedStanding as ve}from"../standing-projection.js";import{authorityFor as we}from"../../fold/signers.js";import{ownRecordAdmitted as Ae,appliedHistoryIn as x}from"../../fold/signed.js";import{signatureEraOf as Se}from"../signature-era.js";import{epochOf as ee,localActLines as Ee}from"../../cli/owner.js";import{fieldOf as k,foldClaims as Ce,isConfig as Ne,sayingOf as te}from"../../fold/claims.js";import{admissionChecks as qe,admissionWideningChecks as Oe,configurationAdmissionGuards as Ie,configurationEpochCheck as je,admissionEvidenceResult as ze}from"../../cli/accept.js";import{wordOf as re}from"../../fold/wire.js";import{isCeiling as ie}from"../../fold/configures.js";import{installedReferenceContracts as Re}from"../reference-contracts.js";import{placesIn as _e}from"../../fold/places.js";import{sha256 as b}from"../hash.js";import{underRootAuthority as Je}from"../../land/act.js";import{storeOf as se}from"../../land/ledger.js";import{withContentStore as Fe}from"../content-store.js";import{withoutEffects as Le}from"../read-only.js";import{BoundRefusal as oe}from"../refusals.js";import{withHostCall as We}from"../call-context.js";const m=h(i=>b(JSON.stringify(i)),"hash"),u=h(i=>{throw new oe("authority-preflight/"+i,"REFUSE\xB7authority-preflight/"+i)},"fail"),Be=h(i=>m(["native-authority-request/1",i.kind,i.records]),"nativeAuthorityRequestIdentity");async function Ge(i,y){try{const n=Q(i.root);return await We({instrumentName:i.instrument.name,runtime:i.instrument.runtime,output:h(()=>{},"output")},()=>Fe(se(n),()=>Je(se(n),"authority card preflight",()=>Le(()=>{const{place:e,key:f,clock:r,limits:d}=i,a=Q(i.root);e!=="."&&(!e||e.startsWith("/")||e.includes("\\")||e.split("/").some(t=>!t||t==="."||t===".."))&&u("place"),e!=="."&&!_e(a).includes(e)&&u("place"),(!Number.isSafeInteger(r.now)||!Number.isSafeInteger(i.authorityEpoch)||!Number.isSafeInteger(d.bytes)||d.bytes<1||!Number.isSafeInteger(d.items)||d.items<1)&&u("context"),i.actions.length>d.items&&u("budget");const l=Z(a,Number.MAX_SAFE_INTEGER),q=be(a);(l.length+i.actions.reduce((t,o)=>t+o.records.length,0)>d.items||Buffer.byteLength(JSON.stringify([l,i.actions]))>d.bytes)&&u("budget"),ee(l)!==i.authorityEpoch&&u("stale-epoch");const p=ve(l,q),R=Se(p).admitted,w=we(l,q,R),O=w.admitted.find(t=>t.id===f),_=b(P(V(a,e==="."?"":e,Y))),A=i.actions.map(t=>{const o=Be(t),c=[],M=[];if(t.kind!=="record-coverage"&&t.kind!=="admission")return{request:o,decision:"deny",evidence:[{check:"unsupported-action",kind:t.kind}],needs:[]};if(!t.records.length)return{request:o,decision:"ask",evidence:[],needs:["authority-preflight/records"]};let S=!1;for(const s of t.records){const g=pe(s);if(g.kind!=="fact"){S=!0,c.push({check:"wire-parse",line:s,result:g});continue}const U=e==="."?g.value.fields.scope??"":`${re(p,"families","region")}/${e}/${g.value.fields.scope??""}`,X=O?le({...g.value.fields,by:f},O,i.authorityEpoch,U,"authority"):{kind:"refuse",why:"requested key is not admitted"};if(c.push({check:"authorityAtEpoch",line:s,scope:U,result:X}),X.kind!=="admitted"&&(S=!0),g.value.fields.sig){const D=e==="."?w.of(s).kind==="admitted":Ae(s,e,re(p,"families","region"),w.admitted,R);c.push({check:"authenticated-record",line:s,admitted:D}),(!D||g.value.fields.by!==f)&&(S=!0)}}if(S)return{request:o,decision:"deny",evidence:c,needs:M};const I=e==="."?p:Ce(x(a,e)).standing,E=Oe(a,t.records,I,p,e);if(c.push({check:"admissionWideningChecks",result:E,standing:m(I)}),E.kind==="ask")return{request:o,decision:"ask",evidence:c,needs:E.needs};if(E.kind==="refuse")return{request:o,decision:"deny",evidence:c,needs:[]};if(t.kind==="record-coverage")return{request:o,decision:"allow",evidence:c,needs:M};if(t.records.some(s=>!Ne(s)))return{request:o,decision:"deny",evidence:[...c,{check:"configuration-only",result:"unsupported-object-action"}],needs:[]};const j=t.records.filter(s=>!k(s,"sig"));if(j.length)return{request:o,decision:"ask",evidence:[...c,{check:"signature",records:j}],needs:j.map(s=>"signature:"+k(s,"scope"))};const ue=new Set((e==="."?l:x(a,e)).map(te)),H=ee(Ee(a,e==="."?[]:[e]))+1,$=je(t.records,H,s=>ue.has(te(s)));if($.length)return{request:o,decision:"deny",evidence:[...c,{check:"prospective-epoch",records:$,expected:H}],needs:[]};const C=Ie(a,t.records,e==="."?[]:[e]);if(c.push({check:"configurationAdmissionGuards",result:C}),C.kind==="refuse")return{request:o,decision:"deny",evidence:c,needs:[]};const N=qe(a,C.ownership.ledger,p);if(c.push({check:"admissionChecks",result:N}),N.kind==="ask")return{request:o,decision:"ask",evidence:c,needs:N.needs};if(N.kind==="refuse")return{request:o,decision:"deny",evidence:c,needs:[]};const v=ze(a,e==="."?C.ownership.ledger:t.records,I,r.now);if(v.kind==="refuse"){c.push({check:"admissionEvidence",result:v});const s=v.code==="admission/inert"||v.code==="admission/evidence";return{request:o,decision:s?"ask":"deny",evidence:c,needs:s?t.records.filter(ie).length?t.records.filter(ie).map(g=>"reading:"+k(g,"scope")):["admission-evidence:"+o]:[]}}const{coordinates:fe,judged:K,fold:z}=v.evidence,he=K.map(s=>s.kind==="judged"?{...s,started:void 0}:s);c.push({check:"admissionEvidence",coordinates:fe,judged:he,fold:{standing:z.standing,observed:z.observed,own:z.own}});const T=K.filter(s=>s.kind==="owed");return{request:o,decision:T.length?"ask":"allow",evidence:c,needs:T.map(s=>"demand:"+s.scope)}}),J=JSON.parse(JSON.stringify(A,(t,o)=>o instanceof Map?[...o]:o)),ne=m([q,w.admitted,l]),F=m(["native-authority-coverage/1",a,e,f,O??null,A.map(t=>t.request)]),ce=p.filter(t=>k(t,"scope").startsWith("keys/")),de=l.filter(t=>k(t,"scope").startsWith("keys/")&&k(t,"value")==="withdraw"),L=Re(),W={place:e,snapshot:_,key:f,authority:ne,coverage:F,policy:m(p),budget:m(d),catalog:L.refusals,standingContract:L.standing,grants:ce.map(b),retirements:de.map(b),demands:p.filter(t=>k(t,"role")==="demands").map(b),horizon:i.horizon,issuedAt:y??r.now,actions:A.filter(t=>t.decision!=="open").map(t=>({request:t.request,decision:t.decision})),evidence:m(J)};(m(Z(a,Number.MAX_SAFE_INTEGER))!==m(l)||b(P(V(a,e==="."?"":e,Y)))!==_)&&u("context-changed");const B=ge(W,d),ae=[...new Set(A.flatMap(t=>t.needs))],G={card:W,line:B,identity:m([a,e,F,B]),judgments:J,needs:ae};return Buffer.byteLength(JSON.stringify(G))>d.bytes&&u("budget"),{kind:"fact",value:G}}))))}catch(n){return{kind:"refuse",code:n instanceof oe?n.code:"authority-preflight/failed",diagnostic:n instanceof Error?n.message:"REFUSE\xB7authority preflight failed"}}}h(Ge,"prepareNativeAuthorityCard");async function ut(i,y,n){const e=await Ge(n,i.issuedAt);if(e.kind==="refuse")return e.code==="authority-preflight/stale-epoch"||e.code==="authority-preflight/context-changed"?{status:"refused",code:"card-stale",next:"refresh-card"}:e;const f=e.value,r=f.card;return ke(i,y,{place:r.place,snapshot:r.snapshot,key:r.key,authority:r.authority,coverage:r.coverage,policy:r.policy,budget:r.budget,catalog:r.catalog,standingContract:r.standingContract,clock:n.clock,verifiesJudgment:h((d,a)=>d===ye(r,n.limits)&&a===r.evidence,"verifiesJudgment")},n.limits)}h(ut,"answerNativeAuthorityCard");function ft(i,y,n){(!Number.isSafeInteger(n.bytes)||n.bytes<1||Buffer.byteLength(i)>n.bytes||b(i)!==y)&&u("request-integrity");const e=JSON.parse(i);(!e||Object.keys(e).sort().join(",")!=="actions,authorityEpoch,clock,horizon,key,limits,place,profile"||e.profile!=="native-authority-card-request/1"||!Array.isArray(e.actions)||e.actions.length>n.items)&&u("request-format");const f=h((r,d)=>r!==null&&typeof r=="object"&&!Array.isArray(r)&&Object.keys(r).sort().join(",")===d,"exact");(!f(e.clock,"name,now,unit")||!f(e.horizon,"clock,from,through,unit")||!f(e.limits,"bytes,items"))&&u("request-context");for(const r of[n.items,e.limits.bytes,e.limits.items])(!Number.isSafeInteger(r)||r<1)&&u("request-budget");(e.limits.bytes>n.bytes||e.limits.items>n.items||!Number.isSafeInteger(e.authorityEpoch)||e.authorityEpoch<0||!Number.isSafeInteger(e.clock.now)||!Number.isSafeInteger(e.horizon.from)||!Number.isSafeInteger(e.horizon.through)||e.horizon.from>e.horizon.through)&&u("request-context");for(const r of[e.place,e.key,e.clock.name,e.clock.unit,e.horizon.clock,e.horizon.unit])(typeof r!="string"||!r||/[\u0000-\u0020\u007f]/.test(r))&&u("request-context");return(e.clock.name!==e.horizon.clock||e.clock.unit!==e.horizon.unit||e.clock.now<e.horizon.from||e.clock.now>e.horizon.through)&&u("request-clock"),e.actions.some(r=>!r||Object.keys(r).sort().join(",")!=="kind,records"||!["record-coverage","admission"].includes(r.kind)||!Array.isArray(r.records)||r.records.some(d=>typeof d!="string"))&&u("request-action"),e.actions.reduce((r,d)=>r+d.records.length,0)>e.limits.items&&u("request-budget"),structuredClone(e)}h(ft,"parseNativeAuthorityCardRequest");export{ut as answerNativeAuthorityCard,Be as nativeAuthorityRequestIdentity,ft as parseNativeAuthorityCardRequest,Ge as prepareNativeAuthorityCard};
|
|
@@ -1,32 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/** The opened descriptor is bounded before allocation/read. Growth after fstat
|
|
3
|
-
* is detected with one sentinel byte; no oversized buffer reaches decode/hash.
|
|
4
|
-
*/
|
|
5
|
-
export function readBoundedFile(path, remaining, coordinate) {
|
|
6
|
-
if (!Number.isSafeInteger(remaining) || remaining < 0)
|
|
7
|
-
throw Error('REFUSE·native file missing byte budget ' + coordinate);
|
|
8
|
-
const fd = fs.openSync(path, 'r');
|
|
9
|
-
try {
|
|
10
|
-
const stat = fs.fstatSync(fd);
|
|
11
|
-
if (!stat.isFile() || !Number.isSafeInteger(stat.size) || stat.size > remaining)
|
|
12
|
-
throw Error('REFUSE·native file byte budget before read ' + coordinate);
|
|
13
|
-
const bytes = Buffer.alloc(stat.size);
|
|
14
|
-
let offset = 0;
|
|
15
|
-
while (offset < bytes.length) {
|
|
16
|
-
const count = fs.readSync(fd, bytes, offset, bytes.length - offset, null);
|
|
17
|
-
if (!count)
|
|
18
|
-
throw Error('REFUSE·native file changed during read ' + coordinate);
|
|
19
|
-
offset += count;
|
|
20
|
-
}
|
|
21
|
-
if (fs.readSync(fd, Buffer.alloc(1), 0, 1, null) !== 0)
|
|
22
|
-
throw Error('REFUSE·native file grew during read ' + coordinate);
|
|
23
|
-
return bytes;
|
|
24
|
-
}
|
|
25
|
-
finally {
|
|
26
|
-
fs.closeSync(fd);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export function readBoundedBatch(paths, budget) {
|
|
30
|
-
let bytes = 0;
|
|
31
|
-
return paths.map(file => { const data = readBoundedFile(file.path, budget - bytes, file.coordinate); bytes += data.length; return data; });
|
|
32
|
-
}
|
|
1
|
+
var u=Object.defineProperty;var a=(o,e)=>u(o,"name",{value:e,configurable:!0});import f from"node:fs";function c(o,e,t){if(!Number.isSafeInteger(e)||e<0)throw Error("REFUSE\xB7native file missing byte budget "+t);const r=f.openSync(o,"r");try{const n=f.fstatSync(r);if(!n.isFile()||!Number.isSafeInteger(n.size)||n.size>e)throw Error("REFUSE\xB7native file byte budget before read "+t);const i=Buffer.alloc(n.size);let l=0;for(;l<i.length;){const s=f.readSync(r,i,l,i.length-l,null);if(!s)throw Error("REFUSE\xB7native file changed during read "+t);l+=s}if(f.readSync(r,Buffer.alloc(1),0,1,null)!==0)throw Error("REFUSE\xB7native file grew during read "+t);return i}finally{f.closeSync(r)}}a(c,"readBoundedFile");function h(o,e){let t=0;return o.map(r=>{const n=c(r.path,e-t,r.coordinate);return t+=n.length,n})}a(h,"readBoundedBatch");export{h as readBoundedBatch,c as readBoundedFile};
|
|
@@ -1,91 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { channel } from 'node:diagnostics_channel';
|
|
3
|
-
import { isAbsolute } from 'node:path';
|
|
4
|
-
import { checkCommandPort, commandRequest, commandResponse } from '@lapxo/topos/contract';
|
|
5
|
-
import { requireEffects } from "../read-only.js";
|
|
6
|
-
import { assertNativeRuntime } from "./runtime.js";
|
|
7
|
-
import { scheduleDeadline } from "./deadline.js";
|
|
8
|
-
const rejectedCommand = channel('bound.command.refusal');
|
|
9
|
-
/** macOS prototype: bounded async invocation with POSIX child-group cancellation.
|
|
10
|
-
* No acquisition or persistence on prospective paths; no partial success frames.
|
|
11
|
-
* Host callbacks are trusted admission/verification operations, never signatures
|
|
12
|
-
* or authority assertions supplied by the command's own output. */
|
|
13
|
-
export async function executeLot(options) {
|
|
14
|
-
const start = process.hrtime.bigint();
|
|
15
|
-
const { host, signal, phase, accepts } = options;
|
|
16
|
-
// Admitted semantic configuration is snapshotted for the whole invocation;
|
|
17
|
-
// mutations of a caller's delivery object cannot alter checks mid-flight.
|
|
18
|
-
const port = Object.freeze({ ...options.port, closure: Object.freeze([...options.port.closure]), environment: Object.freeze([...options.port.environment]) });
|
|
19
|
-
const lot = Object.freeze({ ...options.lot, requests: Object.freeze([...options.lot.requests]) });
|
|
20
|
-
const environment = Object.freeze({ ...options.environment });
|
|
21
|
-
assertNativeRuntime(environment, []);
|
|
22
|
-
const refuse = (why) => { throw Error(`REFUSE·port ${port.coordinate} ${why}`); };
|
|
23
|
-
if (phase !== 'act')
|
|
24
|
-
refuse('invocation forbidden during ' + phase);
|
|
25
|
-
requireEffects('command ' + port.coordinate);
|
|
26
|
-
if (signal?.aborted)
|
|
27
|
-
refuse('interrupted before invocation');
|
|
28
|
-
checkCommandPort(port, host);
|
|
29
|
-
const deadline = start + BigInt(port.timeoutMs) * 1000000n;
|
|
30
|
-
const resolved = host.resolve(port), invocation = Object.freeze({ ...resolved, args: Object.freeze([...resolved.args]) });
|
|
31
|
-
if (!isAbsolute(invocation.command) || !isAbsolute(invocation.cwd))
|
|
32
|
-
refuse('host invocation needs absolute coordinates');
|
|
33
|
-
if (host.loaderClosure(port, invocation) !== 'verified')
|
|
34
|
-
refuse('runtime-loader closure unmeasured');
|
|
35
|
-
const names = Object.keys(environment).sort(), declared = [...new Set(port.environment)].sort();
|
|
36
|
-
if (names.length !== declared.length || names.some((name, index) => name !== declared[index]) || names.some(name => typeof environment[name] !== 'string' || environment[name].includes('\0')))
|
|
37
|
-
refuse('environment differs from declared handoff');
|
|
38
|
-
const frame = commandRequest(port, lot);
|
|
39
|
-
const envelope = invocation.hostEnvelope;
|
|
40
|
-
if (envelope && (!Number.isSafeInteger(invocation.envelopeBytes) || invocation.envelopeBytes < 1 || envelope.byteLength > invocation.envelopeBytes))
|
|
41
|
-
refuse('host envelope byte resources');
|
|
42
|
-
if (process.hrtime.bigint() >= deadline)
|
|
43
|
-
refuse('timeout before invocation');
|
|
44
|
-
if (signal?.aborted)
|
|
45
|
-
refuse('interrupted before invocation');
|
|
46
|
-
const result = await new Promise((resolve, reject) => {
|
|
47
|
-
const child = spawn(invocation.command, [...invocation.args], { cwd: invocation.cwd, env: { ...environment }, shell: false, detached: false, stdio: envelope ? ['pipe', 'pipe', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'] });
|
|
48
|
-
const stdout = [], stderr = [];
|
|
49
|
-
let bytes = 0, why, done = false;
|
|
50
|
-
const stop = (cause) => { if (why)
|
|
51
|
-
return; why = cause; try {
|
|
52
|
-
child.kill('SIGKILL');
|
|
53
|
-
}
|
|
54
|
-
catch { } child.stdin.destroy(); if (envelope)
|
|
55
|
-
child.stdio[3]?.destroy(); };
|
|
56
|
-
const cancelTimer = scheduleDeadline(deadline, () => stop('timeout'));
|
|
57
|
-
const abort = () => stop('interrupted');
|
|
58
|
-
signal?.addEventListener('abort', abort, { once: true });
|
|
59
|
-
if (signal?.aborted)
|
|
60
|
-
abort();
|
|
61
|
-
const collect = (target, chunk, channel) => { if (why)
|
|
62
|
-
return; bytes += chunk.length; if (bytes > port.responseBytes) {
|
|
63
|
-
stop('response-bytes exceeded');
|
|
64
|
-
return;
|
|
65
|
-
} if (channel === 'stderr' && port.stderr === 'reject') {
|
|
66
|
-
rejectedCommand.publish({ coordinate: port.coordinate, stderr: chunk.subarray(0, Math.min(port.responseBytes, 1024)).toString('utf8') });
|
|
67
|
-
stop('stderr forbidden');
|
|
68
|
-
return;
|
|
69
|
-
} target.push(chunk); };
|
|
70
|
-
child.stdout.on('data', (chunk) => collect(stdout, chunk, 'stdout'));
|
|
71
|
-
child.stderr.on('data', (chunk) => collect(stderr, chunk, 'stderr'));
|
|
72
|
-
child.stdin.on('error', () => stop('request handoff failed'));
|
|
73
|
-
const finish = (status, error) => { if (done)
|
|
74
|
-
return; done = true; cancelTimer(); signal?.removeEventListener('abort', abort); if (error || why) {
|
|
75
|
-
reject(Error(`REFUSE·port ${port.coordinate} ${why ?? error.message}`));
|
|
76
|
-
return;
|
|
77
|
-
} resolve({ status, interrupted: false, stdout: Buffer.concat(stdout).toString('utf8'), stderr: Buffer.concat(stderr).toString('utf8') }); };
|
|
78
|
-
child.once('error', error => finish(null, error));
|
|
79
|
-
child.once('close', status => finish(status));
|
|
80
|
-
if (envelope) {
|
|
81
|
-
const pipe = child.stdio[3];
|
|
82
|
-
const header = Buffer.alloc(8);
|
|
83
|
-
header.writeBigUInt64BE(BigInt(envelope.byteLength));
|
|
84
|
-
pipe.on('error', () => stop('host envelope handoff failed'));
|
|
85
|
-
pipe.end(Buffer.concat([header, Buffer.from(envelope)]));
|
|
86
|
-
}
|
|
87
|
-
child.stdin.end(frame);
|
|
88
|
-
});
|
|
89
|
-
const members = commandResponse(port, lot, result, accepts);
|
|
90
|
-
return { members, stderr: result.stderr, invocations: 1 };
|
|
91
|
-
}
|
|
1
|
+
var _=Object.defineProperty;var a=(n,p)=>_(n,"name",{value:p,configurable:!0});import{spawn as k}from"node:child_process";import{channel as F}from"node:diagnostics_channel";import{isAbsolute as C}from"node:path";import{library5_checkCommandPort as M,library5_commandRequest as N,library5_commandResponse as A}from"../../vendor/runtime.js";import{requireEffects as D}from"../read-only.js";import{assertNativeRuntime as G}from"./runtime.js";import{scheduleDeadline as K}from"./deadline.js";const T=F("bound.command.refusal");async function te(n){const p=process.hrtime.bigint(),{host:b,signal:m,phase:y,accepts:$}=n,t=Object.freeze({...n.port,closure:Object.freeze([...n.port.closure]),environment:Object.freeze([...n.port.environment])}),B=Object.freeze({...n.lot,requests:Object.freeze([...n.lot.requests])}),l=Object.freeze({...n.environment});G(l,[]);const i=a(c=>{throw Error(`REFUSE\xB7port ${t.coordinate} ${c}`)},"refuse");y!=="act"&&i("invocation forbidden during "+y),D("command "+t.coordinate),m?.aborted&&i("interrupted before invocation"),M(t,b);const j=p+BigInt(t.timeoutMs)*1000000n,E=b.resolve(t),r=Object.freeze({...E,args:Object.freeze([...E.args])});(!C(r.command)||!C(r.cwd))&&i("host invocation needs absolute coordinates"),b.loaderClosure(t,r)!=="verified"&&i("runtime-loader closure unmeasured");const h=Object.keys(l).sort(),w=[...new Set(t.environment)].sort();(h.length!==w.length||h.some((c,v)=>c!==w[v])||h.some(c=>typeof l[c]!="string"||l[c].includes("\0")))&&i("environment differs from declared handoff");const x=N(t,B),d=r.hostEnvelope;d&&(!Number.isSafeInteger(r.envelopeBytes)||r.envelopeBytes<1||d.byteLength>r.envelopeBytes)&&i("host envelope byte resources"),process.hrtime.bigint()>=j&&i("timeout before invocation"),m?.aborted&&i("interrupted before invocation");const O=await new Promise((c,v)=>{const o=k(r.command,[...r.args],{cwd:r.cwd,env:{...l},shell:!1,detached:!1,stdio:d?["pipe","pipe","pipe","pipe"]:["pipe","pipe","pipe"]}),z=[],S=[];let L=0,u,R=!1;const f=a(e=>{if(!u){u=e;try{o.kill("SIGKILL")}catch{}o.stdin.destroy(),d&&o.stdio[3]?.destroy()}},"stop"),P=K(j,()=>f("timeout")),g=a(()=>f("interrupted"),"abort");m?.addEventListener("abort",g,{once:!0}),m?.aborted&&g();const q=a((e,s,U)=>{if(!u){if(L+=s.length,L>t.responseBytes){f("response-bytes exceeded");return}if(U==="stderr"&&t.stderr==="reject"){T.publish({coordinate:t.coordinate,stderr:s.subarray(0,Math.min(t.responseBytes,1024)).toString("utf8")}),f("stderr forbidden");return}e.push(s)}},"collect");o.stdout.on("data",e=>q(z,e,"stdout")),o.stderr.on("data",e=>q(S,e,"stderr")),o.stdin.on("error",()=>f("request handoff failed"));const I=a((e,s)=>{if(!R){if(R=!0,P(),m?.removeEventListener("abort",g),s||u){v(Error(`REFUSE\xB7port ${t.coordinate} ${u??s.message}`));return}c({status:e,interrupted:!1,stdout:Buffer.concat(z).toString("utf8"),stderr:Buffer.concat(S).toString("utf8")})}},"finish");if(o.once("error",e=>I(null,e)),o.once("close",e=>I(e)),d){const e=o.stdio[3],s=Buffer.alloc(8);s.writeBigUInt64BE(BigInt(d.byteLength)),e.on("error",()=>f("host envelope handoff failed")),e.end(Buffer.concat([s,Buffer.from(d)]))}o.stdin.end(x)});return{members:A(t,B,O,$),stderr:O.stderr,invocations:1}}a(te,"executeLot");export{te as executeLot};
|
|
@@ -1,88 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { createConnection } from 'node:net';
|
|
4
|
-
const { endpoint, deadlineMs, shared, inputBytes, outputBytes } = workerData;
|
|
5
|
-
const port = parentPort;
|
|
6
|
-
if (!port)
|
|
7
|
-
throw Error('REFUSE·Root control transport parent absent');
|
|
8
|
-
const header = new Int32Array(shared, 0, 8), input = new Uint8Array(shared, 32, inputBytes), output = new Uint8Array(shared, 32 + inputBytes, outputBytes);
|
|
9
|
-
const initial = Math.min(endpoint.timeoutMs, deadlineMs - Date.now());
|
|
10
|
-
const deadline = process.hrtime.bigint() + BigInt(Math.max(0, initial)) * 1000000n;
|
|
11
|
-
const remaining = () => Math.max(0, Number((deadline - process.hrtime.bigint() + 999999n) / 1000000n));
|
|
12
|
-
let busy = false, red, active;
|
|
13
|
-
function finish(state, bytes) {
|
|
14
|
-
if (bytes.byteLength > outputBytes) {
|
|
15
|
-
state = 3;
|
|
16
|
-
bytes = Buffer.from('REFUSE·reply bytes').subarray(0, outputBytes);
|
|
17
|
-
}
|
|
18
|
-
output.set(bytes);
|
|
19
|
-
Atomics.store(header, 3, bytes.byteLength);
|
|
20
|
-
Atomics.store(header, 0, state);
|
|
21
|
-
Atomics.notify(header, 0);
|
|
22
|
-
}
|
|
23
|
-
// Timer slices never widen the original monotonic deadline.
|
|
24
|
-
let timer;
|
|
25
|
-
function arm() {
|
|
26
|
-
const left = remaining();
|
|
27
|
-
if (left > 0) {
|
|
28
|
-
timer = setTimeout(arm, Math.min(left, 2147483647));
|
|
29
|
-
timer.unref();
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
red ?? (red = 'REFUSE·parent deadline');
|
|
33
|
-
active?.destroy(Error(red));
|
|
34
|
-
if (Atomics.load(header, 0) === 1)
|
|
35
|
-
finish(3, Buffer.from(red).subarray(0, outputBytes));
|
|
36
|
-
port.close();
|
|
37
|
-
}
|
|
38
|
-
arm();
|
|
39
|
-
port.on('close', () => { clearTimeout(timer); active?.destroy(); });
|
|
40
|
-
port.on('message', async (message) => {
|
|
41
|
-
if (busy || red || message !== Atomics.load(header, 1) || Atomics.load(header, 0) !== 1) {
|
|
42
|
-
red ?? (red = 'REFUSE·sequence/state');
|
|
43
|
-
finish(3, Buffer.from(red).subarray(0, outputBytes));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
busy = true;
|
|
47
|
-
try {
|
|
48
|
-
const length = Atomics.load(header, 2);
|
|
49
|
-
if (length < 1 || length > inputBytes)
|
|
50
|
-
throw Error('REFUSE·request bytes');
|
|
51
|
-
if (remaining() <= 0)
|
|
52
|
-
throw Error('REFUSE·parent deadline');
|
|
53
|
-
const frame = Buffer.from(input.slice(0, length));
|
|
54
|
-
const bytes = await new Promise((resolve, reject) => {
|
|
55
|
-
const socket = createConnection(endpoint.socket);
|
|
56
|
-
active = socket;
|
|
57
|
-
const chunks = [];
|
|
58
|
-
let size = 0, done = false;
|
|
59
|
-
const end = (error) => { if (done)
|
|
60
|
-
return; done = true; if (error)
|
|
61
|
-
reject(error);
|
|
62
|
-
else
|
|
63
|
-
resolve(Buffer.concat(chunks)); };
|
|
64
|
-
socket.once('connect', () => socket.write(Buffer.concat([frame, Buffer.from('\n')])));
|
|
65
|
-
socket.on('data', chunk => { size += chunk.length; if (size > outputBytes)
|
|
66
|
-
socket.destroy(Error('REFUSE·reply bytes'));
|
|
67
|
-
else
|
|
68
|
-
chunks.push(chunk); });
|
|
69
|
-
socket.once('error', end);
|
|
70
|
-
socket.once('close', () => end());
|
|
71
|
-
});
|
|
72
|
-
active = undefined;
|
|
73
|
-
if (!bytes.length)
|
|
74
|
-
throw Error('REFUSE·interrupted before reply');
|
|
75
|
-
if (remaining() <= 0)
|
|
76
|
-
throw Error('REFUSE·parent deadline');
|
|
77
|
-
busy = false;
|
|
78
|
-
finish(2, bytes);
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
active = undefined;
|
|
82
|
-
red ?? (red = error instanceof Error ? error.message : String(error));
|
|
83
|
-
busy = false;
|
|
84
|
-
finish(3, Buffer.from(red).subarray(0, outputBytes));
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
Atomics.store(header, 5, 1);
|
|
88
|
-
Atomics.notify(header, 5);
|
|
1
|
+
var F=Object.defineProperty;var a=(r,e)=>F(r,"name",{value:e,configurable:!0});import{parentPort as M,workerData as k}from"node:worker_threads";import{createConnection as q}from"node:net";const{endpoint:A,deadlineMs:x,shared:l,inputBytes:d,outputBytes:s}=k,c=M;if(!c)throw Error("REFUSE\xB7Root control transport parent absent");const o=new Int32Array(l,0,8),D=new Uint8Array(l,32,d),I=new Uint8Array(l,32+d,s),L=Math.min(A.timeoutMs,x-Date.now()),P=process.hrtime.bigint()+BigInt(Math.max(0,L))*1000000n,E=a(()=>Math.max(0,Number((P-process.hrtime.bigint()+999999n)/1000000n)),"remaining");let u=!1,t,f;function m(r,e){e.byteLength>s&&(r=3,e=Buffer.from("REFUSE\xB7reply bytes").subarray(0,s)),I.set(e),Atomics.store(o,3,e.byteLength),Atomics.store(o,0,r),Atomics.notify(o,0)}a(m,"finish");let y;function B(){const r=E();if(r>0){y=setTimeout(B,Math.min(r,2147483647)),y.unref();return}t??(t="REFUSE\xB7parent deadline"),f?.destroy(Error(t)),Atomics.load(o,0)===1&&m(3,Buffer.from(t).subarray(0,s)),c.close()}a(B,"arm"),B(),c.on("close",()=>{clearTimeout(y),f?.destroy()}),c.on("message",async r=>{if(u||t||r!==Atomics.load(o,1)||Atomics.load(o,0)!==1){t??(t="REFUSE\xB7sequence/state"),m(3,Buffer.from(t).subarray(0,s));return}u=!0;try{const e=Atomics.load(o,2);if(e<1||e>d)throw Error("REFUSE\xB7request bytes");if(E()<=0)throw Error("REFUSE\xB7parent deadline");const U=Buffer.from(D.slice(0,e)),h=await new Promise((R,S)=>{const n=q(A.socket);f=n;const p=[];let b=0,w=!1;const g=a(i=>{w||(w=!0,i?S(i):R(Buffer.concat(p)))},"end");n.once("connect",()=>n.write(Buffer.concat([U,Buffer.from(`
|
|
2
|
+
`)]))),n.on("data",i=>{b+=i.length,b>s?n.destroy(Error("REFUSE\xB7reply bytes")):p.push(i)}),n.once("error",g),n.once("close",()=>g())});if(f=void 0,!h.length)throw Error("REFUSE\xB7interrupted before reply");if(E()<=0)throw Error("REFUSE\xB7parent deadline");u=!1,m(2,h)}catch(e){f=void 0,t??(t=e instanceof Error?e.message:String(e)),u=!1,m(3,Buffer.from(t).subarray(0,s))}}),Atomics.store(o,5,1),Atomics.notify(o,5);
|
|
@@ -1,110 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/** Root-fixed synchronous transport. It retains no grants or command results. */
|
|
3
|
-
import { Worker, isMainThread } from 'node:worker_threads';
|
|
4
|
-
import { readBoundedFile } from "./bounded-file.js";
|
|
5
|
-
import { remainingDeadlineMs } from "./deadline.js";
|
|
6
|
-
const FixedWorker = Worker;
|
|
7
|
-
const HEADER_BYTES = 32;
|
|
8
|
-
let controlWorkerOccupied = false;
|
|
9
|
-
export function createFixedControlBridge(inputBinding) {
|
|
10
|
-
// @1 is one main-thread control worker per OS process, never a WorldWorker grant.
|
|
11
|
-
if (!isMainThread)
|
|
12
|
-
throw Error('REFUSE·Root control transport requires main-thread realm');
|
|
13
|
-
const refuse = (why) => { throw Error('REFUSE·Root control transport ' + why); };
|
|
14
|
-
const integer = (value) => Number.isSafeInteger(value) && value > 0;
|
|
15
|
-
const sourceBytes = inputBinding.workerBytes;
|
|
16
|
-
const workerSelection = Object.freeze({ ...inputBinding.worker });
|
|
17
|
-
if (!(sourceBytes instanceof Uint8Array) || !workerSelection.coordinate || !integer(workerSelection.bytes) || sourceBytes.byteLength !== workerSelection.bytes)
|
|
18
|
-
refuse('selected worker bytes/type bound differs before copy');
|
|
19
|
-
const binding = Object.freeze({ ...inputBinding,
|
|
20
|
-
endpoint: Object.freeze({ ...inputBinding.endpoint }), resources: Object.freeze({ ...inputBinding.resources }),
|
|
21
|
-
runtime: Object.freeze({ ...inputBinding.runtime }), worker: workerSelection,
|
|
22
|
-
workerBytes: Buffer.from(sourceBytes) });
|
|
23
|
-
const { endpoint, resources, runtime, worker: selected } = binding;
|
|
24
|
-
if (!selected.coordinate || !integer(selected.bytes) || binding.workerBytes.byteLength !== selected.bytes || digest(binding.workerBytes) !== selected.digest)
|
|
25
|
-
refuse('selected worker bytes differ');
|
|
26
|
-
if (runtime.path !== process.execPath || !integer(runtime.bytes) || digest(readBoundedFile(runtime.path, runtime.bytes, runtime.path)) !== runtime.digest)
|
|
27
|
-
refuse('selected runtime bytes differ');
|
|
28
|
-
if (![endpoint.timeoutMs, endpoint.responseBytes, endpoint.deadlineMs, binding.deadlineMs].every(integer) || binding.deadlineMs > endpoint.deadlineMs)
|
|
29
|
-
refuse('parent time/byte resource absent or widened');
|
|
30
|
-
// host-control-transport@1 encodes each byte length in a signed Int32 cell.
|
|
31
|
-
// Refuse an incompatible declaration; never wrap or clamp its bound.
|
|
32
|
-
if (resources.inputBytes > 2147483647 || resources.outputBytes > 2147483647)
|
|
33
|
-
refuse('host-control-transport@1 signed Int32 length representation');
|
|
34
|
-
const sharedBytes = HEADER_BYTES + resources.inputBytes + resources.outputBytes;
|
|
35
|
-
if (!Object.values(resources).every(integer) || !Number.isSafeInteger(sharedBytes) || resources.inputBytes > endpoint.responseBytes || resources.outputBytes > endpoint.responseBytes || sharedBytes > resources.sharedBytes)
|
|
36
|
-
refuse('shared/input/output resource absent or exceeded');
|
|
37
|
-
const remainingMs = Math.min(endpoint.timeoutMs, binding.deadlineMs - Date.now());
|
|
38
|
-
if (remainingMs <= 0)
|
|
39
|
-
refuse('parent deadline');
|
|
40
|
-
const deadline = process.hrtime.bigint() + BigInt(remainingMs) * 1000000n;
|
|
41
|
-
const remaining = remainingDeadlineMs.bind(undefined, deadline);
|
|
42
|
-
if (controlWorkerOccupied)
|
|
43
|
-
refuse('one live main-thread control worker resource exhausted');
|
|
44
|
-
let shared;
|
|
45
|
-
try {
|
|
46
|
-
shared = new SharedArrayBuffer(sharedBytes);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return refuse('shared resource allocation');
|
|
50
|
-
}
|
|
51
|
-
const header = new Int32Array(shared, 0, 8), input = new Uint8Array(shared, HEADER_BYTES, resources.inputBytes), output = new Uint8Array(shared, HEADER_BYTES + resources.inputBytes, resources.outputBytes);
|
|
52
|
-
// Independently sealed bytes are immutable here. A payload cannot select a path or verifier.
|
|
53
|
-
const code = Buffer.from(binding.workerBytes).toString('base64');
|
|
54
|
-
controlWorkerOccupied = true;
|
|
55
|
-
let worker;
|
|
56
|
-
try {
|
|
57
|
-
worker = new FixedWorker(new URL('data:text/javascript;base64,' + code), { execArgv: [], env: {}, workerData: { endpoint: { ...endpoint }, deadlineMs: binding.deadlineMs, shared, inputBytes: resources.inputBytes, outputBytes: resources.outputBytes } });
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
controlWorkerOccupied = false;
|
|
61
|
-
return refuse('worker construction interrupted');
|
|
62
|
-
}
|
|
63
|
-
let termination;
|
|
64
|
-
const stop = () => termination ?? (termination = worker.terminate().finally(() => { controlWorkerOccupied = false; }));
|
|
65
|
-
worker.once('exit', () => { controlWorkerOccupied = false; });
|
|
66
|
-
worker.unref();
|
|
67
|
-
let red, closed = false, sequence = 0;
|
|
68
|
-
const fail = (why) => { red ?? (red = 'REFUSE·Root control transport ' + why); void stop(); throw Error(red); };
|
|
69
|
-
// Error events still need a listener when a synchronous wait prevents JS callbacks.
|
|
70
|
-
worker.on('error', error => { red ?? (red = 'REFUSE·Root control transport worker interrupted: ' + error.message); });
|
|
71
|
-
if (Atomics.load(header, 5) !== 1 && Atomics.wait(header, 5, 0, remaining()) === 'timed-out')
|
|
72
|
-
fail('startup deadline/interruption');
|
|
73
|
-
return {
|
|
74
|
-
exchange(frame) {
|
|
75
|
-
if (red)
|
|
76
|
-
throw Error(red);
|
|
77
|
-
if (closed)
|
|
78
|
-
fail('closed');
|
|
79
|
-
if (remaining() <= 0)
|
|
80
|
-
fail('parent deadline');
|
|
81
|
-
if (typeof frame !== 'string' || !frame.length || Buffer.byteLength(frame, 'utf8') > resources.inputBytes)
|
|
82
|
-
fail('request bytes before copy');
|
|
83
|
-
const bytes = Buffer.from(frame, 'utf8');
|
|
84
|
-
if (Atomics.load(header, 0) !== 0 || sequence >= 2147483647)
|
|
85
|
-
fail('overlapping request/sequence');
|
|
86
|
-
input.set(bytes);
|
|
87
|
-
Atomics.store(header, 2, bytes.length);
|
|
88
|
-
Atomics.store(header, 1, ++sequence);
|
|
89
|
-
Atomics.store(header, 0, 1);
|
|
90
|
-
worker.postMessage(sequence);
|
|
91
|
-
if (Atomics.wait(header, 0, 1, remaining()) === 'timed-out' || remaining() <= 0)
|
|
92
|
-
fail('deadline/interruption');
|
|
93
|
-
const state = Atomics.load(header, 0), length = Atomics.load(header, 3);
|
|
94
|
-
if (length < 1 || length > resources.outputBytes)
|
|
95
|
-
fail('reply bytes');
|
|
96
|
-
let reply;
|
|
97
|
-
try {
|
|
98
|
-
reply = new TextDecoder('utf-8', { fatal: true }).decode(output.slice(0, length));
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
return fail('reply encoding');
|
|
102
|
-
}
|
|
103
|
-
if (state !== 2)
|
|
104
|
-
fail(reply);
|
|
105
|
-
Atomics.store(header, 0, 0);
|
|
106
|
-
return reply;
|
|
107
|
-
},
|
|
108
|
-
async close() { closed = true; await stop(); }
|
|
109
|
-
};
|
|
110
|
-
}
|
|
1
|
+
var D=Object.defineProperty;var f=(n,r)=>D(n,"name",{value:r,configurable:!0});import{sha256 as U}from"../hash.js";import{Worker as I,isMainThread as L}from"node:worker_threads";import{readBoundedFile as T}from"./bounded-file.js";import{remainingDeadlineMs as W}from"./deadline.js";const N=I,E=32;let p=!1;function G(n){if(!L)throw Error("REFUSE\xB7Root control transport requires main-thread realm");const r=f(t=>{throw Error("REFUSE\xB7Root control transport "+t)},"refuse"),l=f(t=>Number.isSafeInteger(t)&&t>0,"integer"),w=n.workerBytes,m=Object.freeze({...n.worker});(!(w instanceof Uint8Array)||!m.coordinate||!l(m.bytes)||w.byteLength!==m.bytes)&&r("selected worker bytes/type bound differs before copy");const i=Object.freeze({...n,endpoint:Object.freeze({...n.endpoint}),resources:Object.freeze({...n.resources}),runtime:Object.freeze({...n.runtime}),worker:m,workerBytes:Buffer.from(w)}),{endpoint:a,resources:e,runtime:d,worker:b}=i;(!b.coordinate||!l(b.bytes)||i.workerBytes.byteLength!==b.bytes||U(i.workerBytes)!==b.digest)&&r("selected worker bytes differ"),(d.path!==process.execPath||!l(d.bytes)||U(T(d.path,d.bytes,d.path))!==d.digest)&&r("selected runtime bytes differ"),(![a.timeoutMs,a.responseBytes,a.deadlineMs,i.deadlineMs].every(l)||i.deadlineMs>a.deadlineMs)&&r("parent time/byte resource absent or widened"),(e.inputBytes>2147483647||e.outputBytes>2147483647)&&r("host-control-transport@1 signed Int32 length representation");const B=E+e.inputBytes+e.outputBytes;(!Object.values(e).every(l)||!Number.isSafeInteger(B)||e.inputBytes>a.responseBytes||e.outputBytes>a.responseBytes||B>e.sharedBytes)&&r("shared/input/output resource absent or exceeded");const M=Math.min(a.timeoutMs,i.deadlineMs-Date.now());M<=0&&r("parent deadline");const j=process.hrtime.bigint()+BigInt(M)*1000000n,h=W.bind(void 0,j);p&&r("one live main-thread control worker resource exhausted");let y;try{y=new SharedArrayBuffer(B)}catch{return r("shared resource allocation")}const o=new Int32Array(y,0,8),F=new Uint8Array(y,E,e.inputBytes),O=new Uint8Array(y,E+e.inputBytes,e.outputBytes),q=Buffer.from(i.workerBytes).toString("base64");p=!0;let u;try{u=new N(new URL("data:text/javascript;base64,"+q),{execArgv:[],env:{},workerData:{endpoint:{...a},deadlineMs:i.deadlineMs,shared:y,inputBytes:e.inputBytes,outputBytes:e.outputBytes}})}catch{return p=!1,r("worker construction interrupted")}let x;const R=f(()=>x??(x=u.terminate().finally(()=>{p=!1})),"stop");u.once("exit",()=>{p=!1}),u.unref();let c,S=!1,g=0;const s=f(t=>{throw c??(c="REFUSE\xB7Root control transport "+t),R(),Error(c)},"fail");return u.on("error",t=>{c??(c="REFUSE\xB7Root control transport worker interrupted: "+t.message)}),Atomics.load(o,5)!==1&&Atomics.wait(o,5,0,h())==="timed-out"&&s("startup deadline/interruption"),{exchange(t){if(c)throw Error(c);S&&s("closed"),h()<=0&&s("parent deadline"),(typeof t!="string"||!t.length||Buffer.byteLength(t,"utf8")>e.inputBytes)&&s("request bytes before copy");const v=Buffer.from(t,"utf8");(Atomics.load(o,0)!==0||g>=2147483647)&&s("overlapping request/sequence"),F.set(v),Atomics.store(o,2,v.length),Atomics.store(o,1,++g),Atomics.store(o,0,1),u.postMessage(g),(Atomics.wait(o,0,1,h())==="timed-out"||h()<=0)&&s("deadline/interruption");const z=Atomics.load(o,0),k=Atomics.load(o,3);(k<1||k>e.outputBytes)&&s("reply bytes");let A;try{A=new TextDecoder("utf-8",{fatal:!0}).decode(O.slice(0,k))}catch{return s("reply encoding")}return z!==2&&s(A),Atomics.store(o,0,0),A},async close(){S=!0,await R()}}}f(G,"createFixedControlBridge");export{G as createFixedControlBridge};
|