@lapxo/bound 0.1.8 → 0.2.0
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/README.md +56 -20
- package/TARGET.bound +70 -27
- package/assets/wasm/guest.js +4 -0
- package/assets/wasm/manifest.json +27 -0
- package/assets/wasm/runtime.js +6 -0
- package/assets/wasm/runtime.wasm +0 -0
- package/dist/cli/accept.js +83 -59
- package/dist/cli/act-result.d.ts +9 -0
- package/dist/cli/act-result.js +68 -13
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +5 -2
- package/dist/cli/custodian.d.ts +1 -0
- package/dist/cli/custodian.js +29 -0
- package/dist/cli/native-descendant.d.ts +1 -0
- package/dist/cli/native-descendant.js +19 -0
- package/dist/cli/native.d.ts +1 -0
- package/dist/cli/native.js +60 -0
- package/dist/cli/observe.js +9 -2
- package/dist/cli/pass.js +9 -4
- package/dist/cli/place.d.ts +2 -0
- package/dist/cli/place.js +7 -4
- package/dist/cli/standing-result.js +7 -26
- package/dist/cli/verb.js +65 -9
- package/dist/engine/index.d.ts +11 -0
- package/dist/engine/index.js +9 -0
- package/dist/engine/object.d.ts +17 -0
- package/dist/engine/object.js +38 -0
- package/dist/fold/admission-history.js +2 -2
- package/dist/fold/claims.d.ts +4 -0
- package/dist/fold/claims.js +23 -3
- package/dist/fold/closed.d.ts +1 -1
- package/dist/fold/closed.js +36 -7
- package/dist/fold/digests.d.ts +1 -1
- package/dist/fold/digests.js +6 -4
- package/dist/fold/evidence.d.ts +1 -1
- package/dist/fold/kept.js +2 -1
- package/dist/fold/object.d.ts +3 -17
- package/dist/fold/object.js +2 -38
- package/dist/fold/observed.d.ts +1 -0
- package/dist/fold/observed.js +54 -9
- package/dist/fold/readers.d.ts +4 -3
- package/dist/fold/readers.js +18 -13
- package/dist/fold/resolved.js +12 -2
- package/dist/fold/signers.js +2 -2
- package/dist/host/adapters/modules/javascript.d.ts +2 -0
- package/dist/host/adapters/modules/javascript.js +8 -6
- package/dist/host/admitted-place.d.ts +5 -0
- package/dist/host/admitted-place.js +57 -0
- package/dist/host/capsule-process.d.ts +1 -1
- package/dist/host/capsule-process.js +4 -1
- package/dist/host/capsule.d.ts +4 -0
- package/dist/host/capsule.js +16 -2
- package/dist/host/cell-receipts.js +1 -1
- package/dist/host/contextual-place.d.ts +59 -0
- package/dist/host/contextual-place.js +77 -0
- package/dist/host/hash.d.ts +7 -0
- package/dist/host/hash.js +6 -0
- package/dist/host/io.d.ts +2 -0
- package/dist/host/io.js +2 -0
- package/dist/host/lifetime.js +61 -22
- package/dist/host/native/bounded-file.d.ts +8 -0
- package/dist/host/native/bounded-file.js +32 -0
- package/dist/host/native/command.d.ts +36 -0
- package/dist/host/native/command.js +91 -0
- package/dist/host/native/control-bridge-worker.d.ts +1 -0
- package/dist/host/native/control-bridge-worker.js +88 -0
- package/dist/host/native/control-bridge.d.ts +30 -0
- package/dist/host/native/control-bridge.js +110 -0
- package/dist/host/native/control-selection.d.ts +29 -0
- package/dist/host/native/control-selection.js +32 -0
- package/dist/host/native/custody.d.ts +5 -0
- package/dist/host/native/custody.js +91 -0
- package/dist/host/native/deadline.d.ts +12 -0
- package/dist/host/native/deadline.js +28 -0
- package/dist/host/native/descendants.d.ts +1 -0
- package/dist/host/native/descendants.js +528 -0
- package/dist/host/native/generated-custodian.d.ts +122 -0
- package/dist/host/native/generated-custodian.js +267 -0
- package/dist/host/native/generated.d.ts +217 -0
- package/dist/host/native/generated.js +379 -0
- package/dist/host/native/image.d.ts +36 -0
- package/dist/host/native/image.js +45 -0
- package/dist/host/native/jobs.d.ts +75 -0
- package/dist/host/native/jobs.js +148 -0
- package/dist/host/native/loader.d.ts +80 -0
- package/dist/host/native/loader.js +269 -0
- package/dist/host/native/materialize.d.ts +22 -0
- package/dist/host/native/materialize.js +175 -0
- package/dist/host/native/module-closure.d.ts +27 -0
- package/dist/host/native/module-closure.js +99 -0
- package/dist/host/native/module-syntax.d.ts +8 -0
- package/dist/host/native/module-syntax.js +63 -0
- package/dist/host/native/native-binary.d.ts +54 -0
- package/dist/host/native/native-binary.js +115 -0
- package/dist/host/native/native-program.d.ts +56 -0
- package/dist/host/native/native-program.js +72 -0
- package/dist/host/native/node-jobs.d.ts +84 -0
- package/dist/host/native/node-jobs.js +187 -0
- package/dist/host/native/node-launch.d.ts +72 -0
- package/dist/host/native/node-launch.js +159 -0
- package/dist/host/native/preload.d.ts +1 -0
- package/dist/host/native/preload.js +45 -0
- package/dist/host/native/reading.d.ts +91 -0
- package/dist/host/native/reading.js +249 -0
- package/dist/host/native/receipt-environment.d.ts +3 -0
- package/dist/host/native/receipt-environment.js +41 -0
- package/dist/host/native/references.d.ts +5 -0
- package/dist/host/native/references.js +30 -0
- package/dist/host/native/root.d.ts +17 -0
- package/dist/host/native/root.js +37 -0
- package/dist/host/native/route.d.ts +66 -0
- package/dist/host/native/route.js +216 -0
- package/dist/host/native/runtime.d.ts +4 -0
- package/dist/host/native/runtime.js +9 -0
- package/dist/host/native-reader-context.d.ts +3 -0
- package/dist/host/native-reader-context.js +8 -0
- package/dist/host/objects.d.ts +2 -1
- package/dist/host/objects.js +24 -4
- package/dist/host/place-clock-selection.d.ts +55 -0
- package/dist/host/place-clock-selection.js +124 -0
- package/dist/host/ports/local-act.d.ts +10 -0
- package/dist/host/ports/local-act.js +78 -6
- package/dist/host/ports/place-act.d.ts +46 -0
- package/dist/host/ports/place-act.js +169 -0
- package/dist/host/process-lifetime.js +8 -3
- package/dist/host/provide.d.ts +25 -0
- package/dist/host/provide.js +115 -13
- package/dist/host/reading-closures.d.ts +17 -1
- package/dist/host/reading-closures.js +98 -18
- package/dist/host/reading-context.d.ts +29 -0
- package/dist/host/reading-context.js +53 -0
- package/dist/host/reference-authenticated.d.ts +28 -0
- package/dist/host/reference-authenticated.js +84 -0
- package/dist/host/reference-native.d.ts +10 -0
- package/dist/host/reference-native.js +50 -0
- package/dist/host/reference-profile.d.ts +23 -0
- package/dist/host/reference-profile.js +62 -0
- package/dist/host/reference.d.ts +6 -0
- package/dist/host/reference.js +24 -0
- package/dist/host/root-clock-source.d.ts +85 -0
- package/dist/host/root-clock-source.js +305 -0
- package/dist/host/selected-capsules.js +3 -2
- package/dist/host/selected-topos.d.ts +1 -1
- package/dist/host/selected-topos.js +9 -1
- package/dist/land/act.d.ts +26 -0
- package/dist/land/act.js +96 -3
- package/dist/land/ledger.d.ts +4 -0
- package/dist/land/ledger.js +20 -2
- package/dist/observe/regions.d.ts +4 -0
- package/dist/observe/regions.js +109 -1
- package/dist/observe/runner.d.ts +4 -0
- package/dist/observe/runner.js +9 -2
- package/dist/wasm/adapter.d.ts +51 -0
- package/dist/wasm/adapter.js +180 -0
- package/dist/wasm/node-worker.d.ts +1 -0
- package/dist/wasm/node-worker.js +13 -0
- package/dist/wasm/object.d.ts +8 -0
- package/dist/wasm/object.js +76 -0
- package/dist/wasm/worker.d.ts +1 -0
- package/dist/wasm/worker.js +7 -0
- package/docs/contracts/authenticated-reference.publication.json +25 -0
- package/docs/contracts/cli.md +13 -0
- package/docs/contracts/engine-object-projection.publication.json +25 -0
- package/docs/contracts/engine.md +39 -0
- package/docs/contracts/native-publication.inventory.json +8 -0
- package/docs/contracts/optional-wasm-adapter.publication.json +25 -0
- package/docs/contracts/publication-additions.inventory.json +8 -0
- package/docs/contracts/reference-port.md +33 -0
- package/docs/contracts/reference-port.publication.json +25 -0
- package/licenses/wasm/bound.txt +21 -0
- package/licenses/wasm/fast-text-encoding.txt +201 -0
- package/licenses/wasm/jitl-quickjs-ffi-types.txt +21 -0
- package/licenses/wasm/jitl-quickjs-wasmfile-release-sync.txt +47 -0
- package/licenses/wasm/obligations.txt +21 -0
- package/licenses/wasm/quickjs-emscripten-core.txt +21 -0
- package/licenses/wasm/topos.txt +21 -0
- package/package.json +94 -4
- package/receipts.bound +7672 -573
- package/samples/authenticated-reference/no.json +31 -0
- package/samples/authenticated-reference/yes/editorial-state/TARGET.bound +57 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/arrival.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/coverage.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/era.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/order.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/signature.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.arrival.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.coverage.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.era.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.order.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.signature.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/transport.yes.json +1 -0
- package/samples/authenticated-reference/yes/editorial-state/inputs/yes.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/TARGET.bound +57 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/arrival.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/coverage.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/era.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/order.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/signature.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.arrival.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.coverage.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.era.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.order.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.signature.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/transport.yes.json +1 -0
- package/samples/authenticated-reference/yes/weather-reading/inputs/yes.json +1 -0
- package/samples/authenticated-reference/yes.json +19 -0
- package/samples/engine-alphabet/TARGET.bound +29 -0
- package/samples/engine-alphabet/inputs/sample.json +323 -0
- package/samples/engine-alphabet/package.json +1 -0
- package/samples/engine-alphabet/reader.js +1457 -0
- package/samples/engine-alphabet/receipts.bound +12 -0
- package/samples/engine-interval/TARGET.bound +29 -0
- package/samples/engine-interval/inputs/sample.json +277 -0
- package/samples/engine-interval/package.json +1 -0
- package/samples/engine-interval/reader.js +1457 -0
- package/samples/engine-interval/receipts.bound +12 -0
- package/samples/no/engine-order/TARGET.bound +29 -0
- package/samples/no/engine-order/inputs/sample.json +277 -0
- package/samples/no/engine-order/package.json +1 -0
- package/samples/no/engine-order/reader.js +1457 -0
- package/samples/reference-port/no/catalog/standing.json +1 -0
- package/samples/reference-port/no/catalog/transport.json +1 -0
- package/samples/reference-port/no/route-notes/standing.json +1 -0
- package/samples/reference-port/no/route-notes/transport.json +1 -0
- package/samples/reference-port/yes/catalog/TARGET.bound +33 -0
- package/samples/reference-port/yes/catalog/standing.json +1 -0
- package/samples/reference-port/yes/catalog/transport.json +1 -0
- package/samples/reference-port/yes/route-notes/TARGET.bound +33 -0
- package/samples/reference-port/yes/route-notes/standing.json +1 -0
- package/samples/reference-port/yes/route-notes/transport.json +1 -0
- package/samples/wasm/editorial-notes/TARGET.bound +65 -0
- package/samples/wasm/editorial-notes/esm.bound +59 -0
- package/samples/wasm/editorial-notes/no-deadline.bound +65 -0
- package/samples/wasm/editorial-notes/no-memory.bound +65 -0
- package/samples/wasm/editorial-notes/no-missing-limit.bound +64 -0
- package/samples/wasm/editorial-notes/receipts.bound +1 -0
- package/samples/wasm/water-level/TARGET.bound +65 -0
- package/samples/wasm/water-level/esm.bound +59 -0
- package/samples/wasm/water-level/no-deadline.bound +65 -0
- package/samples/wasm/water-level/no-memory.bound +65 -0
- package/samples/wasm/water-level/no-missing-limit.bound +64 -0
- package/samples/wasm/water-level/receipts.bound +1 -0
- package/vectors/engine-object.cli.json +548 -0
- package/vectors/native/authenticated-reference-formation.vector.json +53 -0
- package/vectors/native/authenticated-reference.cli.json +157 -0
- package/vectors/native/authenticated-reference.vector.json +440 -0
- package/vectors/reference-port.cli.json +688 -0
- package/vectors/wasm-object.cli.json +452 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type TrustedClockContract, type PlaceReceipt, type RootContext } from '@lapxo/topos/core';
|
|
2
|
+
import { type PublicationStorage } from './publication.ts';
|
|
3
|
+
export interface PlaceActBudget {
|
|
4
|
+
readonly acts: number;
|
|
5
|
+
readonly bytes: number;
|
|
6
|
+
}
|
|
7
|
+
export interface PlaceActHead {
|
|
8
|
+
readonly place: string;
|
|
9
|
+
readonly identity: string;
|
|
10
|
+
readonly epoch: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PlaceActSnapshot {
|
|
13
|
+
readonly root: RootContext;
|
|
14
|
+
readonly heads: readonly PlaceActHead[];
|
|
15
|
+
}
|
|
16
|
+
/** Fixed instrument construction, never supplied by world JSON. Historical
|
|
17
|
+
* authentication permits inspection only; current() is read under Root guards. */
|
|
18
|
+
export interface PlaceActTrust {
|
|
19
|
+
readonly contract: TrustedClockContract;
|
|
20
|
+
readonly initialHeads: readonly PlaceActHead[];
|
|
21
|
+
readonly current: () => PlaceActSnapshot;
|
|
22
|
+
readonly historical: (encoded: string) => TrustedClockContract;
|
|
23
|
+
}
|
|
24
|
+
export interface PlaceActBundle {
|
|
25
|
+
readonly context: string;
|
|
26
|
+
readonly receipts: readonly PlaceReceipt[];
|
|
27
|
+
readonly originals: readonly (readonly string[])[];
|
|
28
|
+
}
|
|
29
|
+
/** Historical result reads never produce a current authorization permit. */
|
|
30
|
+
export declare function placeActResult(store: string, id: string, trust: Pick<PlaceActTrust, 'historical'>, limits: PlaceActBudget): PlaceActBundle | undefined;
|
|
31
|
+
/** Each receiver chain advances independently. Initial heads are explicit,
|
|
32
|
+
* authenticated host inputs, not inferred zero or legacy global epochs. */
|
|
33
|
+
export declare function placeActHeads(store: string, initial: readonly PlaceActHead[], trust: Pick<PlaceActTrust, 'historical' | 'contract'>, limits: PlaceActBudget): readonly PlaceActHead[];
|
|
34
|
+
/** Context, exact original bytes and complete signed receipts have one visibility
|
|
35
|
+
* point. Real Root guard exclusion spans final authority/head comparison and
|
|
36
|
+
* publication; this does not activate a CLI profile or reinterpret local-act@1. */
|
|
37
|
+
export declare function commitPlaceAct(store: string, encoded: string, receipts: readonly PlaceReceipt[], trust: PlaceActTrust, limits: PlaceActBudget, storage?: PublicationStorage): Promise<PlaceActBundle & {
|
|
38
|
+
readonly kind: 'landed' | 'once';
|
|
39
|
+
readonly identity: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Enumerates only committed, historically authenticated carriers. Namespace
|
|
42
|
+
* projection is a caller's separate pure operation, never current permission. */
|
|
43
|
+
export declare function committedPlaceActResults(store: string, trust: Pick<PlaceActTrust, 'historical'>, limits: PlaceActBudget): readonly {
|
|
44
|
+
identity: string;
|
|
45
|
+
bundle: PlaceActBundle;
|
|
46
|
+
}[];
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync, openSync, fstatSync, readSync, closeSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readPlaceActContext, verifyPlaceActContext, placeActReceiptsOf } from '@lapxo/topos/core';
|
|
4
|
+
import { underAuthorityRegions, underTheRegions, underRootProjection } from "../../land/act.js";
|
|
5
|
+
import { requireEffects } from "../read-only.js";
|
|
6
|
+
import { publishBundle, nativePublicationStorage } from "./publication.js";
|
|
7
|
+
const fail = (why) => { throw Error('REFUSE·place act ' + why); };
|
|
8
|
+
const clock = (n) => { if (!/^(0|[1-9][0-9]*)$/.test(n) || !Number.isSafeInteger(Number(n)))
|
|
9
|
+
fail('unknown receiver clock'); return Number(n); };
|
|
10
|
+
const rootBytes = (r) => JSON.stringify([r.namespace, r.snapshot, r.epoch, r.prefix, r.head, r.policies]);
|
|
11
|
+
const budget = (b) => { if (!b || !Number.isSafeInteger(b.acts) || b.acts < 1 || !Number.isSafeInteger(b.bytes) || b.bytes < 1)
|
|
12
|
+
fail('missing storage budget'); };
|
|
13
|
+
const base = (store) => join(store, 'ledger', 'place-acts');
|
|
14
|
+
const at = (store, id) => { if (!id || id.includes('\0'))
|
|
15
|
+
fail('missing identity'); return join(base(store), 'committed-' + encodeURIComponent(id)); };
|
|
16
|
+
function storedInventory(store, b) {
|
|
17
|
+
budget(b);
|
|
18
|
+
if (!existsSync(base(store)))
|
|
19
|
+
return { names: [], bytes: 0 };
|
|
20
|
+
const names = readdirSync(base(store)).filter(n => n.startsWith('committed-'));
|
|
21
|
+
if (names.length > b.acts)
|
|
22
|
+
fail('stored act budget');
|
|
23
|
+
let bytes = 0;
|
|
24
|
+
for (const name of names)
|
|
25
|
+
for (const member of ['context.json', 'receipts.json', 'originals.json']) {
|
|
26
|
+
const stat = statSync(join(base(store), name, member));
|
|
27
|
+
if (!stat.isFile() || (bytes += stat.size) > b.bytes)
|
|
28
|
+
fail('stored aggregate byte budget');
|
|
29
|
+
}
|
|
30
|
+
return { names, bytes };
|
|
31
|
+
}
|
|
32
|
+
function readBundle(path, b) {
|
|
33
|
+
budget(b);
|
|
34
|
+
let bytes = 0;
|
|
35
|
+
const read = (name) => {
|
|
36
|
+
const fd = openSync(join(path, name), 'r');
|
|
37
|
+
try {
|
|
38
|
+
const size = fstatSync(fd);
|
|
39
|
+
if (!size.isFile() || size.size > b.bytes - bytes)
|
|
40
|
+
fail('stored byte budget');
|
|
41
|
+
const data = Buffer.alloc(size.size);
|
|
42
|
+
let read = 0;
|
|
43
|
+
while (read < data.length) {
|
|
44
|
+
const count = readSync(fd, data, read, data.length - read, null);
|
|
45
|
+
if (!count)
|
|
46
|
+
fail('stored member changed while reading');
|
|
47
|
+
read += count;
|
|
48
|
+
}
|
|
49
|
+
if (readSync(fd, Buffer.alloc(1), 0, 1, null) !== 0)
|
|
50
|
+
fail('stored member changed while reading');
|
|
51
|
+
bytes += read;
|
|
52
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(data);
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
closeSync(fd);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
const context = read('context.json'), receipts = JSON.parse(read('receipts.json')), originals = JSON.parse(read('originals.json'));
|
|
60
|
+
return { context, receipts, originals };
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
if (e instanceof Error && e.message.startsWith('REFUSE·'))
|
|
64
|
+
throw e;
|
|
65
|
+
return fail('committed bundle unreadable');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function authenticated(bundle, id, contract) {
|
|
69
|
+
const result = placeActReceiptsOf(bundle.context, bundle.receipts, contract), decoded = readPlaceActContext(bundle.context, contract.budget);
|
|
70
|
+
if (result.identity !== id || JSON.stringify(bundle.originals) !== JSON.stringify(decoded.places.map(p => p.records)))
|
|
71
|
+
fail('stored original/context identity mismatch');
|
|
72
|
+
return decoded;
|
|
73
|
+
}
|
|
74
|
+
/** Historical result reads never produce a current authorization permit. */
|
|
75
|
+
export function placeActResult(store, id, trust, limits) {
|
|
76
|
+
const path = at(store, id);
|
|
77
|
+
if (!existsSync(path))
|
|
78
|
+
return undefined;
|
|
79
|
+
const bundle = readBundle(path, limits);
|
|
80
|
+
authenticated(bundle, id, trust.historical(bundle.context));
|
|
81
|
+
return bundle;
|
|
82
|
+
}
|
|
83
|
+
/** Each receiver chain advances independently. Initial heads are explicit,
|
|
84
|
+
* authenticated host inputs, not inferred zero or legacy global epochs. */
|
|
85
|
+
export function placeActHeads(store, initial, trust, limits) {
|
|
86
|
+
budget(limits);
|
|
87
|
+
if (!initial.length || new Set(initial.map(h => h.place)).size !== initial.length)
|
|
88
|
+
fail('missing/ambiguous initial heads');
|
|
89
|
+
const heads = new Map(initial.map(h => { clock(h.epoch); if (!h.identity)
|
|
90
|
+
fail('missing initial head'); return [h.place, { ...h }]; }));
|
|
91
|
+
if (!existsSync(base(store)))
|
|
92
|
+
return [...heads.values()];
|
|
93
|
+
const { names } = storedInventory(store, limits);
|
|
94
|
+
const pending = names.map(name => { const id = decodeURIComponent(name.slice(10)), bundle = readBundle(join(base(store), name), limits); const context = authenticated(bundle, id, trust.historical(bundle.context)); if (context.root.namespace !== trust.contract.root.namespace)
|
|
95
|
+
fail('stored clock namespace mismatch'); return { id, context }; });
|
|
96
|
+
while (pending.length) {
|
|
97
|
+
const next = pending.findIndex(a => a.context.places.every(p => heads.get(p.place)?.identity === p.previous));
|
|
98
|
+
if (next < 0)
|
|
99
|
+
fail('receiver chain fork or missing predecessor');
|
|
100
|
+
const act = pending.splice(next, 1)[0];
|
|
101
|
+
for (const p of act.context.places) {
|
|
102
|
+
const prior = heads.get(p.place);
|
|
103
|
+
const epoch = clock(prior.epoch) + 1;
|
|
104
|
+
if (!Number.isSafeInteger(epoch) || clock(p.epoch) !== epoch)
|
|
105
|
+
fail('receiver chain clock mismatch');
|
|
106
|
+
heads.set(p.place, { place: p.place, identity: act.id, epoch: p.epoch });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return [...heads.values()];
|
|
110
|
+
}
|
|
111
|
+
/** Context, exact original bytes and complete signed receipts have one visibility
|
|
112
|
+
* point. Real Root guard exclusion spans final authority/head comparison and
|
|
113
|
+
* publication; this does not activate a CLI profile or reinterpret local-act@1. */
|
|
114
|
+
export async function commitPlaceAct(store, encoded, receipts, trust, limits, storage = nativePublicationStorage) {
|
|
115
|
+
requireEffects('place act commitment');
|
|
116
|
+
budget(limits);
|
|
117
|
+
const decoded = readPlaceActContext(encoded, trust.contract.budget), id = trust.contract.digest(encoded);
|
|
118
|
+
const regions = decoded.places.map(p => { if (p.place === '.')
|
|
119
|
+
return ''; const parts = p.place.split('/'); if (parts.length < 2 || parts.pop() !== 'TARGET.bound' || parts.some(s => s === '' || s === '.' || s === '..'))
|
|
120
|
+
fail('unknown place namespace'); return parts.join('/'); });
|
|
121
|
+
const enter = regions.includes('') ? underTheRegions : underAuthorityRegions;
|
|
122
|
+
return enter(store, regions, 'place act', () => underRootProjection(store, 'place act publication', () => {
|
|
123
|
+
const prior = placeActResult(store, id, trust, limits);
|
|
124
|
+
if (prior) {
|
|
125
|
+
if (prior.context !== encoded)
|
|
126
|
+
fail('replay bytes differ');
|
|
127
|
+
// Replay authenticates the supplied evidence too. Historical authentication
|
|
128
|
+
// grants no current authority and leaves the committed result unchanged.
|
|
129
|
+
placeActReceiptsOf(encoded, receipts, trust.historical(encoded));
|
|
130
|
+
return { ...prior, identity: id, kind: 'once' };
|
|
131
|
+
}
|
|
132
|
+
const verified = verifyPlaceActContext(encoded, trust.contract);
|
|
133
|
+
placeActReceiptsOf(encoded, receipts, trust.contract);
|
|
134
|
+
const originals = decoded.places.map(p => p.records), files = { 'context.json': encoded, 'receipts.json': JSON.stringify(receipts) + '\n', 'originals.json': JSON.stringify(originals) + '\n' };
|
|
135
|
+
const inventory = storedInventory(store, limits);
|
|
136
|
+
if (inventory.bytes + Object.values(files).reduce((n, v) => n + Buffer.byteLength(v), 0) > limits.bytes)
|
|
137
|
+
fail('publication byte budget');
|
|
138
|
+
if (inventory.names.length >= limits.acts)
|
|
139
|
+
fail('publication act budget');
|
|
140
|
+
const current = trust.current();
|
|
141
|
+
if (rootBytes(current.root) !== rootBytes(verified.dependencies))
|
|
142
|
+
fail('authority changed before publication');
|
|
143
|
+
if (new Set(current.heads.map(h => h.place)).size !== current.heads.length)
|
|
144
|
+
fail('ambiguous receiver heads');
|
|
145
|
+
const storedHeads = placeActHeads(store, trust.initialHeads, trust, limits);
|
|
146
|
+
for (const p of decoded.places) {
|
|
147
|
+
const actual = storedHeads.find(h => h.place === p.place), selected = current.heads.find(h => h.place === p.place);
|
|
148
|
+
if (!actual || !selected || actual.identity !== selected.identity || actual.epoch !== selected.epoch)
|
|
149
|
+
fail('receiver snapshot is not committed head ' + p.place);
|
|
150
|
+
}
|
|
151
|
+
for (const p of decoded.places) {
|
|
152
|
+
const head = current.heads.find(h => h.place === p.place) ?? fail('missing receiver head ' + p.place);
|
|
153
|
+
if (head.identity !== p.previous)
|
|
154
|
+
fail('receiver head moved ' + p.place);
|
|
155
|
+
const next = clock(head.epoch) + 1;
|
|
156
|
+
if (!Number.isSafeInteger(next) || clock(p.epoch) !== next)
|
|
157
|
+
fail('receiver epoch not next ' + p.place);
|
|
158
|
+
}
|
|
159
|
+
publishBundle(at(store, id), files, storage);
|
|
160
|
+
return { context: encoded, receipts, originals, identity: id, kind: 'landed' };
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
/** Enumerates only committed, historically authenticated carriers. Namespace
|
|
164
|
+
* projection is a caller's separate pure operation, never current permission. */
|
|
165
|
+
export function committedPlaceActResults(store, trust, limits) {
|
|
166
|
+
const { names } = storedInventory(store, limits);
|
|
167
|
+
return names.map(name => { const identity = decodeURIComponent(name.slice(10)), bundle = placeActResult(store, identity, trust, limits); if (!bundle)
|
|
168
|
+
fail('committed carrier vanished'); return { identity, bundle: bundle }; });
|
|
169
|
+
}
|
|
@@ -2,11 +2,16 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { processOf } from "../observe/runner.js";
|
|
3
3
|
/** A synchronous fold waits on a supervising host process; the supervisor owns cancellation of its child group. */
|
|
4
4
|
export function boundedProcess(command, args, input, limits, cwd, env) {
|
|
5
|
-
|
|
5
|
+
if (!Number.isSafeInteger(limits.timeoutMs) || limits.timeoutMs <= 0 || !Number.isSafeInteger(limits.responseBytes) || limits.responseBytes <= 0)
|
|
6
|
+
throw Error('REFUSE·reader invalid process limits');
|
|
7
|
+
// The same monotonic deadline covers handoff, input and execution. Starting a
|
|
8
|
+
// fresh timer after stdin closes would leave the handoff outside the policy.
|
|
9
|
+
const deadline = process.hrtime.bigint() + BigInt(limits.timeoutMs) * 1000000n;
|
|
10
|
+
const run = spawnSync(process.execPath, [processOf('lifetime', 'host'), String(deadline), String(limits.responseBytes)], { input: JSON.stringify({ command, args, input, limits, cwd, env }), encoding: 'utf8', maxBuffer: limits.responseBytes * 12 + 4096 });
|
|
6
11
|
if (run.error || run.status !== 0)
|
|
7
|
-
throw Error(`REFUSE·reader lifetime supervisor ${run.error?.message ?? run.status}`);
|
|
12
|
+
throw Error(`REFUSE·reader lifetime supervisor ${run.error?.message ?? run.signal ?? run.status}`);
|
|
8
13
|
const result = JSON.parse(run.stdout);
|
|
9
14
|
if (result.failure)
|
|
10
|
-
throw Error(`REFUSE·reader ${result.failure} · status ${result.status ?? result.signal ?? 'cancelled'}`);
|
|
15
|
+
throw Error(`REFUSE·reader ${result.failure} · status ${result.status ?? result.signal ?? 'cancelled'}${result.stderr ? ' · child stderr: ' + result.stderr : ''}`);
|
|
11
16
|
return result;
|
|
12
17
|
}
|
package/dist/host/provide.d.ts
CHANGED
|
@@ -19,5 +19,30 @@ export declare function resolvePinned(specifier: string, context: ResolveContext
|
|
|
19
19
|
readonly json: string;
|
|
20
20
|
readonly exports: boolean;
|
|
21
21
|
}): ResolveResult;
|
|
22
|
+
/** Fixed execution selects libraries by the instrument's explicit local mount,
|
|
23
|
+
* before opening any artifact. An alias or package-name guess grants nothing. */
|
|
24
|
+
export declare function selectNativeDependencies(root: string, dependencies: readonly string[], manifest: string): readonly {
|
|
25
|
+
line: string;
|
|
26
|
+
library: {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly manifest: string;
|
|
29
|
+
readonly coordinate?: string;
|
|
30
|
+
};
|
|
31
|
+
}[] | undefined;
|
|
32
|
+
/** A declared artifact is not automatically an active Node library. This fixed
|
|
33
|
+
* assertion reuses only the packages selected by the actual host boundary. */
|
|
34
|
+
export declare function reuseNativeResolution(pins: readonly {
|
|
35
|
+
pkg: string;
|
|
36
|
+
at: string;
|
|
37
|
+
}[]): boolean;
|
|
38
|
+
/** Read one resolved archive's manifest; a selected fixed library cannot vanish
|
|
39
|
+
* into the optional historical provider path when its archive is absent. */
|
|
40
|
+
export declare function readProvidedPin(name: string, at: string, file: string, requiredName?: string): {
|
|
41
|
+
at: string;
|
|
42
|
+
pkg: string;
|
|
43
|
+
exports: boolean;
|
|
44
|
+
href: string;
|
|
45
|
+
json: string;
|
|
46
|
+
} | undefined;
|
|
22
47
|
export declare function provide(): void;
|
|
23
48
|
export {};
|
package/dist/host/provide.js
CHANGED
|
@@ -6,7 +6,9 @@ import { fieldOf } from "../fold/claims.js";
|
|
|
6
6
|
import { observeText } from "../observe/files.js";
|
|
7
7
|
import { ownLock, ownStore, ownRoot } from "../observe/runner.js";
|
|
8
8
|
import { runtimeTreeAt } from "./release.js";
|
|
9
|
+
import { ContentNeeded } from "./content-needed.js";
|
|
9
10
|
import { theWord } from "../fold/wire.js";
|
|
11
|
+
const findPackageJSON = nodeModule.findPackageJSON;
|
|
10
12
|
const registerHooks = nodeModule.registerHooks;
|
|
11
13
|
const taken = (exp) => (typeof exp === 'string' ? exp : exp && typeof exp === 'object' && 'default' in exp && typeof exp.default === 'string' ? exp.default : '');
|
|
12
14
|
/**
|
|
@@ -53,29 +55,129 @@ export function resolvePinned(specifier, context, nextResolve, pin) {
|
|
|
53
55
|
* blob's folder.
|
|
54
56
|
*/
|
|
55
57
|
let hooked = false;
|
|
58
|
+
function nativeResolution() {
|
|
59
|
+
const installed = Object.getOwnPropertyDescriptor(globalThis, Symbol.for('bound.native.resolution@1'));
|
|
60
|
+
if (!installed)
|
|
61
|
+
return undefined;
|
|
62
|
+
const capability = installed.value;
|
|
63
|
+
if (installed.configurable !== false || installed.writable !== false || !capability || !Object.isFrozen(capability) || capability.profile !== 'fixed-native-resolution@1' || !Array.isArray(capability.libraryNames) || !Object.isFrozen(capability.libraryNames) || capability.libraryNames.some(name => typeof name !== 'string') || !Array.isArray(capability.libraries) || !Object.isFrozen(capability.libraries) || capability.libraries.some(lib => !lib || !Object.isFrozen(lib) || typeof lib.name !== 'string' || typeof lib.manifest !== 'string') || typeof capability.assertPins !== 'function')
|
|
64
|
+
throw Error('REFUSE·pin fixed resolution capability invalid');
|
|
65
|
+
return capability;
|
|
66
|
+
}
|
|
67
|
+
/** Fixed execution selects libraries by the instrument's explicit local mount,
|
|
68
|
+
* before opening any artifact. An alias or package-name guess grants nothing. */
|
|
69
|
+
export function selectNativeDependencies(root, dependencies, manifest) {
|
|
70
|
+
const capability = nativeResolution();
|
|
71
|
+
if (!capability)
|
|
72
|
+
return undefined;
|
|
73
|
+
if (dependencies.length && capability.libraries.some(library => typeof library.coordinate !== 'string' || !library.coordinate))
|
|
74
|
+
throw Error('REFUSE·pin fixed resolution logical library coordinate missing');
|
|
75
|
+
const selected = new Map(capability.libraries.map(library => [library.coordinate, library]));
|
|
76
|
+
return dependencies.flatMap(line => {
|
|
77
|
+
const mount = fieldOf(line, 'needs');
|
|
78
|
+
if (!mount)
|
|
79
|
+
return [];
|
|
80
|
+
const library = selected.get(mount.replace(/\/+$/, '') + '/' + manifest);
|
|
81
|
+
if (!library)
|
|
82
|
+
return [];
|
|
83
|
+
let actual;
|
|
84
|
+
try {
|
|
85
|
+
actual = realpathSync(join(root, mount, manifest));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw Error('REFUSE·pin selected library mount absent ' + library.name);
|
|
89
|
+
}
|
|
90
|
+
if (actual !== library.manifest)
|
|
91
|
+
throw Error('REFUSE·pin selected library mount differs ' + library.name);
|
|
92
|
+
return [{ line, library }];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** A declared artifact is not automatically an active Node library. This fixed
|
|
96
|
+
* assertion reuses only the packages selected by the actual host boundary. */
|
|
97
|
+
export function reuseNativeResolution(pins) {
|
|
98
|
+
const capability = nativeResolution();
|
|
99
|
+
if (!capability)
|
|
100
|
+
return false;
|
|
101
|
+
const selected = new Set(capability.libraryNames);
|
|
102
|
+
// The fixed preload owns this closed assertion. Package names select which
|
|
103
|
+
// declared pins participate; exact manifests and member bytes still decide.
|
|
104
|
+
capability.assertPins(pins.filter(pin => selected.has(pin.pkg)).map(pin => ({ name: pin.pkg, at: pin.at })));
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/** Read one resolved archive's manifest; a selected fixed library cannot vanish
|
|
108
|
+
* into the optional historical provider path when its archive is absent. */
|
|
109
|
+
export function readProvidedPin(name, at, file, requiredName) {
|
|
110
|
+
if (!at) {
|
|
111
|
+
if (requiredName !== undefined)
|
|
112
|
+
throw Error('REFUSE·pin fixed resolution requires declared library ' + name);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const manifest = JSON.parse(observeText(join(at, file)) ?? '{}'), pkg = typeof manifest.name === 'string' ? manifest.name : '';
|
|
116
|
+
if (requiredName !== undefined && pkg !== requiredName)
|
|
117
|
+
throw Error('REFUSE·pin fixed resolution library name differs ' + name);
|
|
118
|
+
return pkg ? { at, pkg, exports: Object.prototype.hasOwnProperty.call(manifest, 'exports'), href: pathToFileURL(at).href, json: pathToFileURL(join(at, file)).href } : undefined;
|
|
119
|
+
}
|
|
56
120
|
export function provide() {
|
|
57
121
|
if (hooked)
|
|
58
122
|
return;
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
123
|
+
const dependencies = ownLock().filter((line) => fieldOf(line, 'scope').startsWith('dep/') && fieldOf(line, 'role') === 'reads');
|
|
124
|
+
const file = manifestOf(), selected = selectNativeDependencies(ownRoot(), dependencies, file);
|
|
125
|
+
const load = (line, requiredName) => {
|
|
126
|
+
const name = fieldOf(line, 'scope').slice('dep/'.length), at = pinPlace(name);
|
|
127
|
+
if (!at)
|
|
128
|
+
throw new ContentNeeded(fieldOf(line, 'value'), 'REFUSE·pin declared library unavailable ' + name);
|
|
129
|
+
const pin = readProvidedPin(name, at, file, requiredName);
|
|
130
|
+
if (!pin)
|
|
131
|
+
throw Error('REFUSE·pin declared library unavailable ' + name);
|
|
132
|
+
return pin;
|
|
133
|
+
};
|
|
134
|
+
if (selected !== undefined) {
|
|
135
|
+
reuseNativeResolution(selected.map(({ line, library }) => load(line, library.name)));
|
|
136
|
+
hooked = true;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// A mount selects a library, but its manifest is only a routing hint until
|
|
140
|
+
// an import requests it. The selected archive alone supplies executable bytes.
|
|
141
|
+
const mounts = dependencies.filter(line => fieldOf(line, 'needs') !== '').map(line => {
|
|
142
|
+
const mount = join(ownRoot(), fieldOf(line, 'needs'));
|
|
143
|
+
const manifest = JSON.parse(observeText(join(mount, file)) ?? '{}');
|
|
144
|
+
return { line, mount, pkg: typeof manifest.name === 'string' ? manifest.name : '' };
|
|
69
145
|
});
|
|
146
|
+
const verified = new Map();
|
|
70
147
|
registerHooks({
|
|
71
148
|
resolve(specifier, context, nextResolve) {
|
|
72
149
|
if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.includes(':'))
|
|
73
150
|
return nextResolve(specifier, context);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
151
|
+
let selected = mounts.find(one => one.pkg && (specifier === one.pkg || specifier.startsWith(one.pkg + '/')));
|
|
152
|
+
if (!selected) {
|
|
153
|
+
// Identify the installed package before following its executable entry:
|
|
154
|
+
// a renamed manifest and an escaping entry cannot bypass the declared mount.
|
|
155
|
+
let manifest;
|
|
156
|
+
try {
|
|
157
|
+
manifest = findPackageJSON(specifier, pathToFileURL(join(ownRoot(), file)).href);
|
|
158
|
+
}
|
|
159
|
+
catch { }
|
|
160
|
+
if (manifest)
|
|
161
|
+
selected = mounts.find(one => {
|
|
162
|
+
try {
|
|
163
|
+
return realpathSync(join(one.mount, file)) === realpathSync(manifest);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (!selected)
|
|
77
171
|
return nextResolve(specifier, context);
|
|
172
|
+
let pin = verified.get(selected.line);
|
|
173
|
+
if (!pin) {
|
|
174
|
+
pin = load(selected.line);
|
|
175
|
+
verified.set(selected.line, pin);
|
|
176
|
+
}
|
|
177
|
+
if (specifier !== pin.pkg && !specifier.startsWith(pin.pkg + '/'))
|
|
178
|
+
throw Error('REFUSE·pin selected library name differs');
|
|
78
179
|
return resolvePinned(specifier, context, nextResolve, pin);
|
|
79
180
|
},
|
|
80
181
|
});
|
|
182
|
+
hooked = true;
|
|
81
183
|
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import type { ReaderLifetime, ReaderReference } from '@lapxo/topos/wire';
|
|
2
2
|
import type { Capsule } from './capsule.ts';
|
|
3
|
+
import { snapshotAccess } from '@lapxo/topos/contract';
|
|
4
|
+
import type { ReadingIdentityContext, Snapshot, ResponseContext } from '@lapxo/topos/contract';
|
|
5
|
+
import type { ReadingExecution, ReadingReference } from './reading-context.ts';
|
|
6
|
+
export interface CurrentReading {
|
|
7
|
+
readonly snapshot: Snapshot;
|
|
8
|
+
readonly kept: ResponseContext;
|
|
9
|
+
readonly current: ResponseContext;
|
|
10
|
+
readonly permission: Parameters<typeof snapshotAccess>[3];
|
|
11
|
+
}
|
|
3
12
|
type File = {
|
|
4
13
|
readonly place: string;
|
|
5
14
|
readonly text: string;
|
|
6
15
|
};
|
|
7
16
|
export declare const inputLines: (files: readonly File[]) => readonly string[];
|
|
17
|
+
/** One portable native receipt carrier shared by file plans and native capsule batches. */
|
|
18
|
+
export declare function portableReadingReceipt(scope: string, by: string, closure: string, output: string, hash: (text: string) => string, modern?: boolean, costs?: readonly string[]): readonly string[];
|
|
8
19
|
/** The selected world owns input projection and reading. The host only verifies closures and keeps native count/bytes receipts. */
|
|
9
20
|
export declare function readingClosures(options: {
|
|
10
21
|
root: string;
|
|
@@ -18,7 +29,12 @@ export declare function readingClosures(options: {
|
|
|
18
29
|
readerReference?: ReaderReference;
|
|
19
30
|
lifetime?: ReaderLifetime;
|
|
20
31
|
allowsEmpty?: boolean;
|
|
32
|
+
context?: ReadingIdentityContext;
|
|
33
|
+
execution?: ReadingExecution;
|
|
34
|
+
current?: CurrentReading;
|
|
21
35
|
files: readonly File[];
|
|
36
|
+
livePlace?: () => readonly string[];
|
|
37
|
+
assertCurrent?: () => void;
|
|
22
38
|
emit?: (line: string) => void;
|
|
23
39
|
}): {
|
|
24
40
|
lines: readonly string[];
|
|
@@ -27,5 +43,5 @@ export declare function readingClosures(options: {
|
|
|
27
43
|
executed: number;
|
|
28
44
|
};
|
|
29
45
|
/** Verify the native reading carriers before a file seal may reuse them. These are semantic receipts, not file-region seals. */
|
|
30
|
-
export declare function verifiedReadingReceipts(store: string, lines: readonly string[], digest: (text: string) => string): number | undefined;
|
|
46
|
+
export declare function verifiedReadingReceipts(store: string, lines: readonly string[], digest: (text: string) => string, environment?: readonly ReadingReference[] | ((context: ReadingIdentityContext) => readonly ReadingReference[] | undefined), requireContext?: boolean): number | undefined;
|
|
31
47
|
export {};
|