@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
|
@@ -1,227 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { readingIdentityBytes, readingReceiptIdentity, responseIdentity, snapshotAccess, placeInputs, withPlaceInputs } from '@lapxo/topos/contract';
|
|
9
|
-
import { withReadingBoundary } from "./reading-context.js";
|
|
10
|
-
const receiptScope = (scope) => [scope, 'identity'].join('/');
|
|
11
|
-
const fields = (line) => { const got = parse(line); if (got.kind !== 'fact')
|
|
12
|
-
throw Error('REFUSE·input projection contains a non-wire line'); return got.value.fields; };
|
|
13
|
-
export const inputLines = (files) => files.map(file => canonical({ scope: file.place, form: 'alphabet', measure: 'text', role: 'writes', value: 'lock', shape: file.place, about: file.text, by: 'host', at: 'policy:handed-input' }));
|
|
14
|
-
/** One portable native receipt carrier shared by file plans and native capsule batches. */
|
|
15
|
-
export function portableReadingReceipt(scope, by, closure, output, hash, modern = true, costs = []) {
|
|
16
|
-
const input = hash(closure), payload = hash(output), rows = output.split('\n').filter(Boolean);
|
|
17
|
-
return [...(modern ? [canonical({ scope: receiptScope(scope), role: 'writes', form: 'alphabet', measure: 'digest', value: readingReceiptIdentity(input, payload, bytes => hash(new TextDecoder().decode(bytes))), by, at: 'place:' + input })] : []), canonical({ scope, role: 'writes', form: 'alphabet', measure: 'observed', value: payload, by, at: 'place:' + payload }), canonical({ scope, role: 'writes', form: 'alphabet', measure: 'bytes', value: input, by, at: 'place:' + input, about: closure }), canonical({ scope, role: 'writes', form: 'interval', measure: 'count', value: `${rows.length}..${rows.length}`, by, at: 'place:' + payload, about: output }), ...costs];
|
|
18
|
-
}
|
|
19
|
-
/** The selected world owns input projection and reading. The host only verifies closures and keeps native count/bytes receipts. */
|
|
20
|
-
export function readingClosures(options) {
|
|
21
|
-
const { root, store, reader, speaker, digest: hash, capsule, region, projection, files } = options, emit = options.emit ?? (() => { });
|
|
22
|
-
options.assertCurrent?.();
|
|
23
|
-
if (options.context)
|
|
24
|
-
readingIdentityBytes(options.context);
|
|
25
|
-
let access = [];
|
|
26
|
-
if (options.current) {
|
|
27
|
-
if (!options.context)
|
|
28
|
-
throw Error('REFUSE·current reading requires a declared identity');
|
|
29
|
-
const { snapshot, kept, current, permission } = options.current;
|
|
30
|
-
if (current.implementation !== options.context.implementation)
|
|
31
|
-
throw Error('REFUSE·current reading implementation differs from its verified context');
|
|
32
|
-
const response = responseIdentity(snapshot, current, hash), allowed = snapshotAccess(snapshot, kept, current, permission, hash);
|
|
33
|
-
if (!allowed.inspect || !allowed.reuse)
|
|
34
|
-
throw Error('REFUSE·current reading is not admitted for reuse');
|
|
35
|
-
access = [['response', response], ['permission', hash(JSON.stringify([permission.inspect, permission.reuse, permission.freshness]))]];
|
|
36
|
-
}
|
|
37
|
-
if (options.execution && (!options.context || JSON.stringify(readingIdentityBytes(options.execution.context)) !== JSON.stringify(readingIdentityBytes(options.context))))
|
|
38
|
-
throw Error('REFUSE·reading execution differs from declared context');
|
|
39
|
-
const projectionFor = (name) => {
|
|
40
|
-
if (!capsule.selectedWorld)
|
|
41
|
-
return undefined;
|
|
42
|
-
const { standing, offer } = capsule.selectedWorld;
|
|
43
|
-
const selectors = standing.parts.some(part => part.scope === `${offer}/place-inputs/${name}` && part.measure === 'reads');
|
|
44
|
-
if (!selectors)
|
|
45
|
-
return undefined;
|
|
46
|
-
if (!options.context || !options.execution || !options.livePlace)
|
|
47
|
-
throw Error('REFUSE·place inputs require authenticated shadow and reading context ' + name);
|
|
48
|
-
return placeInputs(standing, offer, name, options.livePlace(), bytes => hash(new TextDecoder().decode(bytes)));
|
|
49
|
-
};
|
|
50
|
-
const projectedPlace = projectionFor(projection), readingPlace = projectionFor(region);
|
|
51
|
-
const commit = (context, name, place) => place && context ? withPlaceInputs(context, JSON.stringify(['place-inputs', capsule.selectedWorld.offer, name]), place) : context;
|
|
52
|
-
const ask = (identity, requests, context = options.context) => withReadingBoundary(identity, options.execution && context ? { ...options.execution, context } : options.execution, () => capsule.ask(requests, options.readerReference));
|
|
53
|
-
const projectionContext = commit(options.context, projection, projectedPlace);
|
|
54
|
-
const base = projectionContext ? hash(new TextDecoder().decode(readingIdentityBytes(projectionContext))) : '';
|
|
55
|
-
const algorithm = hash('').split(':')[0], handed = new Map(files.map(file => [file.place, file.text]));
|
|
56
|
-
if (handed.size !== files.length)
|
|
57
|
-
throw Error('REFUSE·input projection has duplicate handed coordinates');
|
|
58
|
-
const verify = () => { for (const file of files)
|
|
59
|
-
if (!readFileSync(join(root, file.place)).equals(Buffer.from(file.text)))
|
|
60
|
-
throw Error('REFUSE·input changed ' + file.place); };
|
|
61
|
-
verify();
|
|
62
|
-
const projected = ask(base, [{ ...(projectedPlace ? { place: projectedPlace } : {}), protocol: PROTOCOL, verb: 'render', rootScope: '', region: projection, at: 1, reads: ['**'], lines: inputLines(files), files: [] }], projectionContext)[0];
|
|
63
|
-
if (projected?.kind !== 'fact' || !Array.isArray(projected.lines))
|
|
64
|
-
throw Error('REFUSE·input projection ' + (projected?.why ?? 'no lines'));
|
|
65
|
-
const plans = new Map();
|
|
66
|
-
for (const line of projected.lines) {
|
|
67
|
-
const f = fields(line), scope = f.scope ?? '', resolution = /^receipt:(\d+)$/.exec(f.at ?? '');
|
|
68
|
-
if (!scope || plans.has(scope) || f.form !== 'alphabet' || f.measure !== 'reads' || f.role !== 'reads' || !resolution)
|
|
69
|
-
throw Error('REFUSE·input projection ambiguous reading ' + scope);
|
|
70
|
-
const inputs = (f.value ?? '').split('|').filter(Boolean), needs = (f.needs ?? '').split('|').filter(Boolean);
|
|
71
|
-
// An aggregate reads its declared receipts; it need not reread a leaf file.
|
|
72
|
-
if ((!inputs.length && !needs.length) || new Set(inputs).size !== inputs.length || inputs.some(input => !handed.has(input)) || new Set(needs).size !== needs.length)
|
|
73
|
-
throw Error('REFUSE·input projection has unhanded or duplicate dependencies ' + scope);
|
|
74
|
-
const n = Number(resolution[1]);
|
|
75
|
-
if (!Number.isSafeInteger(n))
|
|
76
|
-
throw Error('REFUSE·input projection invalid resolution ' + scope);
|
|
77
|
-
plans.set(scope, { scope, inputs, needs, resolution: n, line: canonical({ scope, form: f.form, measure: f.measure, role: f.role, value: inputs.join('|'), needs: needs.join('|'), at: f.at }) });
|
|
78
|
-
}
|
|
79
|
-
if (!plans.size)
|
|
80
|
-
throw Error('REFUSE·input projection returned no readings');
|
|
81
|
-
const ordered = [], visiting = new Set(), done = new Set();
|
|
82
|
-
const visit = (scope) => { if (done.has(scope))
|
|
83
|
-
return; if (visiting.has(scope))
|
|
84
|
-
throw Error('REFUSE·input projection dependency cycle ' + scope); const p = plans.get(scope); if (!p)
|
|
85
|
-
throw Error('REFUSE·input projection missing reading ' + scope); visiting.add(scope); for (const need of p.needs)
|
|
86
|
-
visit(need); visiting.delete(scope); done.add(scope); ordered.push(p); };
|
|
87
|
-
for (const scope of plans.keys())
|
|
88
|
-
visit(scope);
|
|
89
|
-
const identity = hash([capsule.selection ?? '', capsule.digest, region, projection, ...(options.lifetime === undefined ? [] : [JSON.stringify(options.lifetime)])].join('\n')), by = speaker + ':' + identity.split(':')[1].slice(0, 12), journal = 'reading-closures/' + identity.split(':')[1];
|
|
90
|
-
const prior = shardLines(store, speaker, journal), results = new Map(), out = [];
|
|
91
|
-
const carried = (digest) => join(store, 'cas', 'carried', digest.split(':')[1] ?? '');
|
|
92
|
-
const saved = (text) => { const digest = hash(text), path = carried(digest); mkdirSync(dirname(path), { recursive: true }); if (!existsSync(path) || hash(readFileSync(path, 'utf8')) !== digest)
|
|
93
|
-
replaceWhole(path, text); return digest; };
|
|
94
|
-
const load = (digest) => { if (!digest.startsWith(algorithm + ':') || !/^[a-f0-9]+$/.test(digest.split(':')[1] ?? ''))
|
|
95
|
-
return; const path = carried(digest); if (!existsSync(path))
|
|
96
|
-
return; const text = readFileSync(path, 'utf8'); return hash(text) === digest ? text : undefined; };
|
|
97
|
-
let read = 0, opened = 0, executed = 0;
|
|
98
|
-
for (const plan of ordered) {
|
|
99
|
-
actCosts.measure(() => {
|
|
100
|
-
// Retained evidence does not authorize a current answer.
|
|
101
|
-
options.assertCurrent?.();
|
|
102
|
-
const legacyClosure = [plan.line, canonical({ scope: 'implementation', role: 'writes', form: 'alphabet', measure: 'digest', value: identity, by: 'host', at: 'policy:input-identity' }), ...plan.inputs.map(input => canonical({ scope: input, role: 'writes', form: 'alphabet', measure: 'digest', value: hash(handed.get(input)), by: 'host', at: 'policy:input-identity' })), ...plan.needs.map(need => canonical({ scope: need, role: 'writes', form: 'alphabet', measure: 'digest', value: results.get(need).digest, by: 'host', at: 'policy:input-identity' }))].sort(byBytes).join('\n') + '\n';
|
|
103
|
-
const leafContext = commit(options.context, region, readingPlace);
|
|
104
|
-
const context = leafContext && { ...leafContext, policy: hash(JSON.stringify([leafContext.policy, options.allowsEmpty === true, options.lifetime ?? null, options.readerReference ?? null, capsule.selection ?? null, capsule.lines])), inputs: [...[...leafContext.inputs, ...access].map(([name, digest]) => [JSON.stringify(['context', name]), digest]), [JSON.stringify(['plan']), hash(plan.line)], ...plan.inputs.map(input => [JSON.stringify(['file', input]), hash(handed.get(input))]), ...plan.needs.map(need => [JSON.stringify(['receipt', need]), results.get(need).digest])] };
|
|
105
|
-
const closure = context ? new TextDecoder().decode(readingIdentityBytes(context)) : legacyClosure;
|
|
106
|
-
const input = hash(closure), scope = 'receipts/readings/' + hash(plan.scope).split(':')[1] + '/' + input.split(':')[1], matching = prior.filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'measure') === 'bytes' && fieldOf(line, 'value') === input).at(-1);
|
|
107
|
-
const count = matching && prior.filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'measure') === 'count' && fieldOf(line, 'by') === by).at(-1);
|
|
108
|
-
const heldOutput = count && load(fieldOf(count, 'at').replace(/^place:/, ''));
|
|
109
|
-
const heldLines = heldOutput?.split('\n').filter(Boolean);
|
|
110
|
-
const output = heldLines !== undefined && fieldOf(count, 'value') === `${heldLines.length}..${heldLines.length}` && heldLines.every(line => { const f = fields(line); return f.by === by && f.at === 'place:' + input; }) ? heldOutput : undefined;
|
|
111
|
-
let lines, payload;
|
|
112
|
-
if (output !== undefined && load(input) === closure) {
|
|
113
|
-
if (output === '' && !options.allowsEmpty)
|
|
114
|
-
throw Error('REFUSE·reading empty response is not admitted ' + plan.scope);
|
|
115
|
-
lines = output.split('\n').filter(Boolean);
|
|
116
|
-
for (const line of lines)
|
|
117
|
-
fields(line);
|
|
118
|
-
payload = fieldOf(count, 'at').replace(/^place:/, '');
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
if (readOnly())
|
|
122
|
-
throw Error('REFUSE·preview reading closure is unpaid ' + plan.scope);
|
|
123
|
-
const started = process.hrtime.bigint();
|
|
124
|
-
const dependencies = plan.needs.flatMap(need => results.get(need).lines), answer = ask(input, [{ ...(readingPlace ? { place: readingPlace } : {}), protocol: PROTOCOL, verb: 'read', rootScope: plan.scope, name: plan.scope, region, at: plan.resolution, reads: ['**'], lines: [...inputLines(plan.inputs.map(place => ({ place, text: handed.get(place) }))), ...dependencies], files: [] }], context)[0];
|
|
125
|
-
if (answer?.kind !== 'fact' || !Array.isArray(answer.claims))
|
|
126
|
-
throw Error('REFUSE·reading ' + plan.scope + ' · ' + (answer?.why ?? 'no claims'));
|
|
127
|
-
lines = answer.claims.map((unknown) => { if (typeof unknown?.scope !== 'string' || typeof unknown?.measure !== 'string' || typeof unknown?.role !== 'string' || !unknown.bound || !['interval', 'enumerated'].includes(unknown.bound.kind))
|
|
128
|
-
throw Error('REFUSE·reading malformed claim ' + plan.scope); const span = unknown.bound; const text = canonical({ scope: unknown.scope, measure: unknown.measure, role: unknown.role, form: span.kind === 'interval' ? 'interval' : 'alphabet', value: span.kind === 'interval' ? `${span.lo}..${span.hi}` : Array.isArray(span.values) ? span.values.join('|') : '', by, at: 'place:' + input }); fields(text); return text; });
|
|
129
|
-
if (!lines.length && !options.allowsEmpty)
|
|
130
|
-
throw Error('REFUSE·reading empty response is not admitted ' + plan.scope);
|
|
131
|
-
executed++;
|
|
132
|
-
verify();
|
|
133
|
-
options.assertCurrent?.();
|
|
134
|
-
payload = saved(lines.length ? [...lines].sort(byBytes).join('\n') + '\n' : '');
|
|
135
|
-
if (saved(closure) !== input)
|
|
136
|
-
throw Error('REFUSE·receipt input closure mismatch');
|
|
137
|
-
const seconds = (Number(process.hrtime.bigint() - started) / 1e9).toFixed(6);
|
|
138
|
-
landShard(store, speaker, journal, [canonical({ scope, role: 'writes', form: 'interval', measure: 'count', value: `${lines.length}..${lines.length}`, by, at: 'place:' + payload }), canonical({ scope, role: 'writes', form: 'alphabet', measure: 'bytes', value: input, by, at: 'place:' + input }), canonical({ scope, role: 'writes', form: 'interval', measure: 's', value: seconds + '..' + seconds, by, at: 'place:' + input })]);
|
|
139
|
-
if (plan.resolution === 1)
|
|
140
|
-
opened++;
|
|
141
|
-
}
|
|
142
|
-
const receipt = context ? readingReceiptIdentity(input, payload, bytes => hash(new TextDecoder().decode(bytes))) : payload;
|
|
143
|
-
if (context && plan.resolution !== 1)
|
|
144
|
-
out.push(canonical({ scope: receiptScope(scope), role: 'writes', form: 'alphabet', measure: 'digest', value: receipt, by, at: 'place:' + input }));
|
|
145
|
-
results.set(plan.scope, { digest: receipt, lines });
|
|
146
|
-
out.push(canonical({ scope: plan.scope, role: 'writes', form: 'alphabet', measure: 'observed', value: input, by, at: 'place:' + input }),
|
|
147
|
-
// A logical reading name is not a physical input coordinate. Preserve the
|
|
148
|
-
// host-verified handoff beside the result so portable file seals retain
|
|
149
|
-
// every input, including explicitly read foreign files.
|
|
150
|
-
// Input witnesses do not locate the result at each input. Result attribution
|
|
151
|
-
// remains the logical reading's place; two inputs are not two new origins.
|
|
152
|
-
...plan.inputs.map(coordinate => canonical({ scope: coordinate, role: 'writes', form: 'alphabet', measure: 'observed', value: hash(handed.get(coordinate)), by, at: 'witness:' + input })), ...lines);
|
|
153
|
-
if (plan.resolution === 1) {
|
|
154
|
-
read++;
|
|
155
|
-
const output = load(payload);
|
|
156
|
-
if (output === undefined)
|
|
157
|
-
throw Error('REFUSE·receipt missing verified carrier ' + payload);
|
|
158
|
-
const costs = shardLines(store, speaker, journal).filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'measure') === 's' && fieldOf(line, 'at') === 'place:' + input);
|
|
159
|
-
out.push(...portableReadingReceipt(scope, by, closure, output, hash, !!context, costs));
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
verify();
|
|
164
|
-
emit(`RECEIPTS ${read} read · ${opened} opened · reading closures`);
|
|
165
|
-
return { lines: out, read, opened, executed };
|
|
166
|
-
}
|
|
167
|
-
/** Verify the native reading carriers before a file seal may reuse them. These are semantic receipts, not file-region seals. */
|
|
168
|
-
export function verifiedReadingReceipts(store, lines, digest, environment, requireContext = false) {
|
|
169
|
-
const units = lines.filter(line => fieldOf(line, 'scope').startsWith('receipts/readings/') && fieldOf(line, 'measure') === 'bytes' && fieldOf(line, 'role') === 'writes');
|
|
170
|
-
const load = (id, row) => {
|
|
171
|
-
const hex = id.split(':')[1];
|
|
172
|
-
if (!hex || !/^[a-f0-9]+$/.test(hex))
|
|
173
|
-
return;
|
|
174
|
-
const supplied = fields(row).about;
|
|
175
|
-
// A supplied carrier must authenticate even if an old local copy still exists.
|
|
176
|
-
if (supplied !== undefined)
|
|
177
|
-
return digest(supplied) === id ? supplied : undefined;
|
|
178
|
-
const path = join(store, 'cas', 'carried', hex);
|
|
179
|
-
if (!existsSync(path))
|
|
180
|
-
return;
|
|
181
|
-
const bytes = readFileSync(path, 'utf8');
|
|
182
|
-
return digest(bytes) === id ? bytes : undefined;
|
|
183
|
-
};
|
|
184
|
-
for (const unit of units) {
|
|
185
|
-
const input = fieldOf(unit, 'value'), scope = fieldOf(unit, 'scope'), by = fieldOf(unit, 'by'), counts = lines.filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'measure') === 'count' && fieldOf(line, 'by') === by && fieldOf(line, 'role') === 'writes');
|
|
186
|
-
if (counts.length !== 1 || load(input, unit) === undefined)
|
|
187
|
-
return;
|
|
188
|
-
const count = counts[0], output = load(fieldOf(count, 'at').replace(/^place:/, ''), count);
|
|
189
|
-
if (output === undefined)
|
|
190
|
-
return;
|
|
191
|
-
const inputBytes = load(input, unit);
|
|
192
|
-
if (inputBytes.trimStart().startsWith('[')) {
|
|
193
|
-
let context;
|
|
194
|
-
try {
|
|
195
|
-
context = JSON.parse(inputBytes);
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (!Array.isArray(context) || context.length !== 6 || context[0] !== 'reading-context@1')
|
|
201
|
-
return;
|
|
202
|
-
const canonicalContext = { instrument: context[1], implementation: context[2], policy: context[3], inputs: context[4], environment: context[5] };
|
|
203
|
-
try {
|
|
204
|
-
if (new TextDecoder().decode(readingIdentityBytes(canonicalContext)) !== inputBytes)
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
catch {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (environment) {
|
|
211
|
-
const expected = typeof environment === 'function' ? environment(canonicalContext) : environment;
|
|
212
|
-
if (expected === undefined || JSON.stringify([...expected].sort((a, b) => byBytes(a[0], b[0]))) !== JSON.stringify(context[5]))
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const identity = lines.filter(line => fieldOf(line, 'scope') === receiptScope(scope) && fieldOf(line, 'measure') === 'digest' && fieldOf(line, 'by') === by && fieldOf(line, 'role') === 'writes' && fieldOf(line, 'at') === 'place:' + input);
|
|
216
|
-
const expected = readingReceiptIdentity(input, fieldOf(count, 'at').replace(/^place:/, ''), bytes => digest(new TextDecoder().decode(bytes)));
|
|
217
|
-
if (identity.length !== 1 || fieldOf(identity[0], 'value') !== expected)
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
else if (requireContext || !inputBytes.endsWith('\n') || inputBytes.split('\n').filter(Boolean).some(line => parse(line).kind !== 'fact'))
|
|
221
|
-
return;
|
|
222
|
-
const rows = output.split('\n').filter(Boolean);
|
|
223
|
-
if (fieldOf(count, 'value') !== `${rows.length}..${rows.length}` || rows.some(line => fieldOf(line, 'by') !== by || fieldOf(line, 'at') !== 'place:' + input))
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
return units.length;
|
|
227
|
-
}
|
|
1
|
+
var q=Object.defineProperty;var R=(o,g)=>q(o,"name",{value:g,configurable:!0});import{actCosts as ee}from"./act-cost.js";import{readFileSync as te,existsSync as re}from"node:fs";import{join as se}from"node:path";import{library14_canonical as u,library14_parse as ie,library14_PROTOCOL as ae,library14_byBytes as Y}from"../vendor/runtime.js";import{landShard as oe,shardLines as ne}from"../land/ledger.js";import{fieldOf as s}from"../fold/claims.js";import{readOnly as le}from"./read-only.js";import{library5_readingIdentityBytes as ce,library5_readingReceiptIdentity as G}from"../vendor/runtime.js";import{readingPreparation as pe,inputLines as de,readingFields as Z}from"./reading-preparation.js";import{inputLines as Fe}from"./reading-preparation.js";const W=R(o=>[o,"identity"].join("/"),"receiptScope");function ue(o,g,f,m,h,b=!0,j=[]){const y=h(f),p=h(m),B=m.split(`
|
|
2
|
+
`).filter(Boolean);return[...b?[u({scope:W(o),role:"writes",form:"alphabet",measure:"digest",value:G(y,p,F=>h(new TextDecoder().decode(F))),by:g,at:"place:"+y})]:[],u({scope:o,role:"writes",form:"alphabet",measure:"observed",value:p,by:g,at:"place:"+p}),u({scope:o,role:"writes",form:"alphabet",measure:"bytes",value:y,by:g,at:"place:"+y,about:f}),u({scope:o,role:"writes",form:"interval",measure:"count",value:`${B.length}..${B.length}`,by:g,at:"place:"+p,about:m}),...j]}R(ue,"portableReadingReceipt");function Re(o){const{root:g,store:f,reader:m,speaker:h,digest:b,capsule:j,region:y,projection:p,files:B}=o,F=o.emit??(()=>{}),{ordered:t,identity:r,by:e,journal:i,prior:I,prepare:d,retained:E,verify:A,ask:z,load:T,saved:U,handed:a,readingPlace:L}=pe(o),_=new Map,C=[];let x=0,D=0,P=0;for(const n of t)ee.measure(()=>{o.assertCurrent?.();const{context:J,closure:K,input:c,scope:O}=d(n,_),V=E({context:J,closure:K,input:c,scope:O});let v,$;if(V)v=V.lines,$=V.payload;else{if(le())throw Error("REFUSE\xB7preview reading closure is unpaid "+n.scope);const w=process.hrtime.bigint(),k=n.needs.flatMap(l=>_.get(l).lines),S=z(c,[{...L?{place:L}:{},protocol:ae,verb:"read",rootScope:n.scope,name:n.scope,region:y,at:n.resolution,reads:["**"],lines:[...de(n.inputs.map(l=>({place:l,text:a.get(l)}))),...k],files:[]}],J)[0];if(S?.kind!=="fact"||!Array.isArray(S.claims))throw Error("REFUSE\xB7reading "+n.scope+" \xB7 "+(S?.why??"no claims"));if(v=S.claims.map(l=>{if(typeof l?.scope!="string"||typeof l?.measure!="string"||typeof l?.role!="string"||!l.bound||!["interval","enumerated"].includes(l.bound.kind))throw Error("REFUSE\xB7reading malformed claim "+n.scope);const N=l.bound,X=u({scope:l.scope,measure:l.measure,role:l.role,form:N.kind==="interval"?"interval":"alphabet",value:N.kind==="interval"?`${N.lo}..${N.hi}`:Array.isArray(N.values)?N.values.join("|"):"",by:e,at:"place:"+c});return Z(X),X}),!v.length&&!o.allowsEmpty)throw Error("REFUSE\xB7reading empty response is not admitted "+n.scope);if(P++,A(),o.assertCurrent?.(),$=U(v.length?[...v].sort(Y).join(`
|
|
3
|
+
`)+`
|
|
4
|
+
`:""),U(K)!==c)throw Error("REFUSE\xB7receipt input closure mismatch");const Q=(Number(process.hrtime.bigint()-w)/1e9).toFixed(6);oe(f,h,i,[u({scope:O,role:"writes",form:"interval",measure:"count",value:`${v.length}..${v.length}`,by:e,at:"place:"+$}),u({scope:O,role:"writes",form:"alphabet",measure:"bytes",value:c,by:e,at:"place:"+c}),u({scope:O,role:"writes",form:"interval",measure:"s",value:Q+".."+Q,by:e,at:"place:"+c})]),n.resolution===1&&D++}const H=J?G(c,$,w=>b(new TextDecoder().decode(w))):$;if(J&&n.resolution!==1&&C.push(u({scope:W(O),role:"writes",form:"alphabet",measure:"digest",value:H,by:e,at:"place:"+c})),_.set(n.scope,{digest:H,lines:v}),C.push(u({scope:n.scope,role:"writes",form:"alphabet",measure:"observed",value:c,by:e,at:"place:"+c}),...n.inputs.map(w=>u({scope:w,role:"writes",form:"alphabet",measure:"observed",value:b(a.get(w)),by:e,at:"witness:"+c})),...v),n.resolution===1){x++;const w=T($);if(w===void 0)throw Error("REFUSE\xB7receipt missing verified carrier "+$);const k=ne(f,h,i).filter(S=>s(S,"scope")===O&&s(S,"measure")==="s"&&s(S,"at")==="place:"+c);C.push(...ue(O,e,K,w,b,!!J,k))}});return A(),F(`RECEIPTS ${x} read \xB7 ${D} opened \xB7 reading closures`),{lines:C,read:x,opened:D,executed:P}}R(Re,"readingClosures");const M=new Set,fe=1024;function Oe(o,g,f,m,h=!1){const b=[],j=new Map,y=new Map,p=R((t,r,e)=>JSON.stringify(e===void 0?[t,r]:[t,r,e]),"key"),B=R((t,r,e)=>{const i=t.get(r);i?i.push(e):t.set(r,[e])},"add");for(const t of g){const r=s(t,"scope");if(!r.startsWith("receipts/readings/")||s(t,"role")!=="writes")continue;const e=s(t,"measure"),i=s(t,"by");e==="bytes"?b.push(t):e==="count"?B(j,p(r,i),t):e==="digest"&&B(y,p(r,i,s(t,"at")),t)}const F=R((t,r)=>{const e=t.split(":")[1];if(!e||!/^[a-f0-9]+$/.test(e))return;const i=Z(r).about;if(i!==void 0)return f(i)===t?i:void 0;const I=se(o,"cas","carried",e);if(!re(I))return;const d=te(I,"utf8");return f(d)===t?d:void 0},"load");for(const t of b){const r=s(t,"value"),e=s(t,"scope"),i=s(t,"by"),I=j.get(p(e,i))??[],d=F(r,t);if(I.length!==1||d===void 0)return;const E=I[0],A=F(s(E,"at").replace(/^place:/,""),E);if(A===void 0)return;const z=y.get(p(W(e),i,"place:"+r))??[],T=m?void 0:f(JSON.stringify([r,s(E,"at"),e,i,s(E,"value"),z,h]));if(T&&M.has(T))continue;if(d.trimStart().startsWith("[")){let a;try{a=JSON.parse(d)}catch{return}if(!Array.isArray(a)||a.length!==6||a[0]!=="reading-context@1")return;const L={instrument:a[1],implementation:a[2],policy:a[3],inputs:a[4],environment:a[5]};try{if(new TextDecoder().decode(ce(L))!==d)return}catch{return}if(m){const x=typeof m=="function"?m(L):m;if(x===void 0||JSON.stringify([...x].sort((D,P)=>Y(D[0],P[0])))!==JSON.stringify(a[5]))return}const _=y.get(p(W(e),i,"place:"+r))??[],C=G(r,s(E,"at").replace(/^place:/,""),x=>f(new TextDecoder().decode(x)));if(_.length!==1||s(_[0],"value")!==C)return}else if(h||!d.endsWith(`
|
|
5
|
+
`)||d.split(`
|
|
6
|
+
`).filter(Boolean).some(a=>ie(a).kind!=="fact"))return;const U=A.split(`
|
|
7
|
+
`).filter(Boolean);if(s(E,"value")!==`${U.length}..${U.length}`||U.some(a=>s(a,"by")!==i||s(a,"at")!=="place:"+r))return;T&&(M.size>=fe&&M.delete(M.values().next().value),M.add(T))}return b.length}R(Oe,"verifiedReadingReceipts");export{Fe as inputLines,ue as portableReadingReceipt,Re as readingClosures,Oe as verifiedReadingReceipts};
|
|
@@ -1,53 +1 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { readFileSync, realpathSync } from 'node:fs';
|
|
4
|
-
import { release } from 'node:os';
|
|
5
|
-
import { readingIdentityBytes } from '@lapxo/topos/contract';
|
|
6
|
-
const active = new AsyncLocalStorage();
|
|
7
|
-
export const readingBoundary = active.getStore.bind(active);
|
|
8
|
-
export function withReadingBoundary(identity, execution, run) {
|
|
9
|
-
if (execution) {
|
|
10
|
-
const algorithm = identity.split(':')[0];
|
|
11
|
-
const hash = (text) => algorithm + ':' + createHash(algorithm).update(text).digest('hex');
|
|
12
|
-
const expected = readingEnvironment(hash, execution.env, execution.cwd, execution.flags);
|
|
13
|
-
const named = [...execution.context.environment].sort(([a], [b]) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
|
|
14
|
-
if (JSON.stringify([...expected].sort(([a], [b]) => Buffer.compare(Buffer.from(a), Buffer.from(b)))) !== JSON.stringify(named))
|
|
15
|
-
throw Error('REFUSE·reading execution environment differs from its identity');
|
|
16
|
-
}
|
|
17
|
-
return active.run({ identity, execution }, run);
|
|
18
|
-
}
|
|
19
|
-
/** References commit the actual inherited boundary. Values are hashed, never
|
|
20
|
-
* carried as environment secrets. No empty map is substituted for unknown input. */
|
|
21
|
-
// The running runtime is resolved once for this act; mutable input bytes are not
|
|
22
|
-
// stored in this slot. Later processes establish their own runtime reference.
|
|
23
|
-
const runtimes = new Map();
|
|
24
|
-
const runtimeOf = (algorithm) => { const held = runtimes.get(algorithm); if (held)
|
|
25
|
-
return held; const digest = algorithm + ':' + createHash(algorithm).update(readFileSync(process.execPath)).digest('hex'); runtimes.set(algorithm, digest); return digest; };
|
|
26
|
-
export function readingEnvironment(hash, env = process.env, cwd = process.cwd(), flags = process.execArgv) {
|
|
27
|
-
const inherited = Object.entries(env).filter((entry) => entry[1] !== undefined);
|
|
28
|
-
return [
|
|
29
|
-
['variables', hash(JSON.stringify(inherited.sort(([a], [b]) => Buffer.compare(Buffer.from(a), Buffer.from(b)))))],
|
|
30
|
-
['cwd', hash(realpathSync(cwd))], ['flags', hash(JSON.stringify(flags))],
|
|
31
|
-
['runtime', runtimeOf(hash('').split(':')[0])],
|
|
32
|
-
['platform', hash(JSON.stringify([process.platform, process.arch, process.versions, release()]))],
|
|
33
|
-
];
|
|
34
|
-
}
|
|
35
|
-
export function readingProfile(lines, field) {
|
|
36
|
-
const values = (scope) => lines.filter(line => field(line, 'scope') === scope && field(line, 'role') === 'writes' && field(line, 'value') !== 'withdraw');
|
|
37
|
-
const contexts = values('wire/reading-context'), receipts = values('wire/reading-receipt');
|
|
38
|
-
if (!contexts.length && !receipts.length)
|
|
39
|
-
return false;
|
|
40
|
-
if (contexts.length !== 1 || receipts.length !== 1 || field(contexts[0], 'value') !== 'reading-context@1' || field(receipts[0], 'value') !== 'reading-receipt@1')
|
|
41
|
-
throw Error('REFUSE·reading identity ambiguous or incomplete profiles');
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
/** The caller supplies verified implementation and transitive references. This
|
|
45
|
-
* constructor adds the process boundary that the host will actually hand over. */
|
|
46
|
-
export function readingExecution(input, hash, boundary = { env: process.env, cwd: process.cwd(), flags: process.execArgv }) {
|
|
47
|
-
const env = Object.fromEntries(Object.entries(boundary.env).filter((entry) => entry[1] !== undefined));
|
|
48
|
-
const cwd = boundary.cwd, flags = Object.freeze([...boundary.flags]);
|
|
49
|
-
Object.freeze(env);
|
|
50
|
-
const context = { ...input, environment: readingEnvironment(hash, env, cwd, flags) };
|
|
51
|
-
readingIdentityBytes(context);
|
|
52
|
-
return { context, env, cwd, flags };
|
|
53
|
-
}
|
|
1
|
+
var N=Object.defineProperty;var f=(e,t)=>N(e,"name",{value:t,configurable:!0});import{BoundRefusal as a}from"./refusals.js";import{sha256 as R}from"./hash.js";import{AsyncLocalStorage as y}from"node:async_hooks";import{createHash as v}from"node:crypto";import{readFileSync as J,realpathSync as j}from"node:fs";import{release as F}from"node:os";import{library5_readingIdentityBytes as A}from"../vendor/runtime.js";const w=new y,H=w.getStore.bind(w);function K(e,t,n){if(t){const r=e.split(":")[0],i=b(f(c=>r+":"+v(r).update(c).digest("hex"),"hash"),t.env,t.cwd,t.flags),s=[...t.context.environment].sort(([c],[u])=>Buffer.compare(Buffer.from(c),Buffer.from(u)));if(JSON.stringify([...i].sort(([c],[u])=>Buffer.compare(Buffer.from(c),Buffer.from(u))))!==JSON.stringify(s))throw Error("REFUSE\xB7reading execution environment differs from its identity")}return w.run({identity:e,execution:t},n)}f(K,"withReadingBoundary");const x=new Map,z=f(e=>{const t=x.get(e);if(t)return t;const n=e+":"+v(e).update(J(process.execPath)).digest("hex");return x.set(e,n),n},"runtimeOf");function b(e,t=process.env,n=process.cwd(),r=process.execArgv){const o=Object.entries(t).filter(i=>i[1]!==void 0);return[["variables",e(JSON.stringify(o.sort(([i],[s])=>Buffer.compare(Buffer.from(i),Buffer.from(s)))))],["cwd",e(j(n))],["flags",e(JSON.stringify(r))],["runtime",z(e("").split(":")[0])],["platform",e(JSON.stringify([process.platform,process.arch,process.versions,F()]))]]}f(b,"readingEnvironment");function W(e,t){const n=f(i=>e.filter(s=>t(s,"scope")===i&&t(s,"role")==="writes"&&t(s,"value")!=="withdraw"),"values"),r=n("wire/reading-context"),o=n("wire/reading-receipt");if(!r.length&&!o.length)return!1;if(r.length!==1||o.length!==1||t(r[0],"value")!=="reading-context@1"||t(o[0],"value")!=="reading-receipt@1")throw Error("REFUSE\xB7reading identity ambiguous or incomplete profiles");return!0}f(W,"readingProfile");function q(e,t,n={env:process.env,cwd:process.cwd(),flags:process.execArgv}){const r=Object.fromEntries(Object.entries(n.env).filter(c=>c[1]!==void 0)),o=n.cwd,i=Object.freeze([...n.flags]);Object.freeze(r);const s={...e,environment:b(t,r,o,i)};return A(s),{context:s,env:r,cwd:o,flags:i}}f(q,"readingExecution");const g=new y,p=new y,l=new y,E=new WeakMap,d=new Map;let m=0;const O=16*1024*1024,L=512;function h(e){return{encoded:e,digest:R(e),cost:Buffer.byteLength(e)}}f(h,"encoding");function B(e){if(typeof e=="string"){const t=d.get(e);if(t)return t;const n=h(JSON.stringify(e)),r=Buffer.byteLength(e)+n.cost;if(r<=O){for(;d.size>=L||m+r>O;){const o=d.keys().next().value;m-=Buffer.byteLength(o)+d.get(o).cost,d.delete(o)}d.set(e,n),m+=r}return n}if(Array.isArray(e)&&Object.isFrozen(e)&&Object.getPrototypeOf(e)===Array.prototype&&e.toJSON===void 0){const t=E.get(e);if(t)return t;if(Reflect.ownKeys(e).length===e.length+1&&Array.from({length:e.length},(n,r)=>Object.getOwnPropertyDescriptor(e,String(r))).every(n=>n&&"value"in n&&typeof n.value=="string")){const n=h(JSON.stringify(e));return E.set(e,n),n}}return h(JSON.stringify(e)??"null")}f(B,"encodeObserved");const M=f(e=>B(e()).encoded,"encodedInput"),I=f(e=>{if(g.exit(()=>p.exit(()=>M(e.read)))!==e.encoded)throw new a("reading-identity/context","REFUSE\xB7reading-identity/context view inputs changed during preparation")},"verifyInput");function Q(e){if(p.getStore())return e();const t=new Map;return p.run(t,()=>{const n=e();if(n&&typeof n.then=="function")throw new a("reading-identity/context","REFUSE\xB7reading-identity/context input observation must be synchronous");for(const r of t.values())I(r);return n})}f(Q,"withViewInputObservation");const X=f(()=>g.getStore()!==void 0,"preparingView");function U(e,t,n=!0){const r=g.getStore();if(!r)return t();l.getStore()?.add(e);const o=r.inputs.get(e);if(o)return o.value;const i=p.getStore(),s=i?.get(e),c=s?s.value:t(),u=s??B(c);if(r.inputs.size>=r.files||(r.used+=Buffer.byteLength(e)+u.cost)>r.bytes)throw new a("reading-identity/budget","REFUSE\xB7reading-identity/budget view inputs");const S={value:c,encoded:u.encoded,digest:u.digest,cost:u.cost,read:t,identity:n};return r.inputs.set(e,S),i&&!s&&i.set(e,S),c}f(U,"viewInput");function Y(e,t){const n={inputs:new Map,...e,used:0};return g.run(n,()=>{const r=t();if(!p.getStore())for(const o of n.inputs.values())I(o);return{value:r,inputs:[...n.inputs].filter(([,o])=>o.identity).map(([o,i])=>[o,i.digest]).sort(([o],[i])=>o<i?-1:o>i?1:0)}})}f(Y,"captureViewInputs");function Z(e){let t;return()=>{const n=g.getStore(),r=p.getStore();if(!n||!r)return e();if(t?.observation===r){for(const[c,u]of t.inputs)U(c,u.read,u.identity);return t.value}const o=new Set,i=l.getStore(),s=l.run(o,e);if(s&&typeof s.then=="function")throw new a("reading-identity/context","REFUSE\xB7reading-identity/context shared preparation must be synchronous");for(const c of o)i?.add(c);return t={value:s,inputs:[...o].map(c=>[c,n.inputs.get(c)]),observation:r},s}}f(Z,"sharedViewInputs");export{Y as captureViewInputs,X as preparingView,H as readingBoundary,b as readingEnvironment,q as readingExecution,W as readingProfile,Z as sharedViewInputs,U as viewInput,K as withReadingBoundary,Q as withViewInputObservation};
|
|
File without changes
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var V=Object.defineProperty;var c=(e,t)=>V(e,"name",{value:t,configurable:!0});import{readFileSync as j}from"node:fs";import{sha256 as w}from"./hash.js";import{captureViewInputs as q,viewInput as E,readingEnvironment as F,withViewInputObservation as B,sharedViewInputs as D}from"./reading-context.js";import{withoutEffects as K}from"./read-only.js";import{committedInventoryFingerprint as I,committedLocalActs as O,withCommittedLocalActObservation as z}from"./ports/local-act.js";import{ingressBundles as H}from"./ports/ingress.js";import{instrumentCoordinates as M}from"../fold/digests.js";import{viewsOf as W}from"../fold/views.js";import{standingOf as _}from"../cli/place.js";import{evidenceLines as $}from"../render/view.js";import{resolve as U,join as x}from"node:path";import{library14_LOCK as G}from"../vendor/runtime.js";import{fieldOf as y,isWire as P}from"../fold/claims.js";import{storeOf as N}from"../land/ledger.js";import{underRootAuthority as Q}from"../land/act.js";import{prepareObservedReadings as T}from"../fold/observed.js";import{authenticatedStanding as h}from"./admitted-place.js";import{withHostCall as k}from"./call-context.js";import{withContentStore as X}from"./content-store.js";import{BoundRefusal as i}from"./refusals.js";async function Se(e){try{if(!e||typeof e.place!="string"||!e.place||!e.instrument?.runtime)throw new i("host/context","REFUSE\xB7host/context");const t={...e.limits};for(const n of[t.bytes,t.files,t.readings,t.probes])if(!Number.isSafeInteger(n)||n<1)throw new i("host/budget","REFUSE\xB7host/budget");if(t.signal?.aborted)throw new i("host/cancelled","REFUSE\xB7host/cancelled");const d=U(e.place),r=N(d),s=structuredClone(e.instrument),a=e.reader;return await k({instrumentName:s.name,runtime:s.runtime,output:c(()=>{},"output")},()=>X(r,()=>Q(r,"prepare reading identity",()=>{const n=S(h(d));for(const u of["bytes","files","readings","probes"])t[u]=Math.min(t[u],n[u]);return{kind:"fact",preparations:T(d,r,t,a)}}),()=>h(d)))}catch(t){return{kind:"refuse",code:t instanceof i?t.code:"reading-identity/failed",diagnostic:t instanceof Error?t.message:"REFUSE\xB7reading identity failed"}}}c(Se,"prepareNativeReadingIdentity");function S(e){const t=c(d=>{const r=e.filter(n=>y(n,"scope")==="reading-identity/"+d&&y(n,"role")==="writes"&&y(n,"value")!=="withdraw");if(r.length!==1)throw new i("reading-identity/budget","REFUSE\xB7reading identity requires one declared "+d+" limit");const s=/^0\.\.([1-9][0-9]*)$/.exec(y(r[0],"value")),a=Number(s?.[1]);if(!s||!Number.isSafeInteger(a))throw new i("reading-identity/budget","REFUSE\xB7invalid reading identity "+d+" limit");return a},"limit");return{bytes:t("bytes"),files:t("files"),readings:t("readings"),probes:t("probes")}}c(S,"declaredReadingIdentityLimits");function be(e){const t=Y(e&&{place:e.place,instrument:e.instrument,views:[e.view],...e.expectedIdentity===void 0?{}:{expectedIdentities:{[e.view]:e.expectedIdentity}}});return t.kind==="refuse"?t:t.views[0]}c(be,"prepareNativeViewIdentity");function Y(e){try{if(!e||!e.instrument?.runtime||typeof e.place!="string"||!e.place)throw new i("host/context","REFUSE\xB7host/context");if(!Array.isArray(e.views)||!e.views.length||new Set(e.views).size!==e.views.length||e.views.some(n=>n!=="lines"&&n!=="evidence"))throw new i("reading-identity/selection","REFUSE\xB7reading-identity/selection requires distinct supported native views");if(e.expectedIdentities&&Object.keys(e.expectedIdentities).some(n=>!e.views.includes(n)))throw new i("reading-identity/selection","REFUSE\xB7reading-identity/selection expected identity names an unselected view");const t=[...e.views],d={...e.expectedIdentities},r=U(e.place),s=N(r),a=structuredClone(e.instrument);return k({instrumentName:a.name,runtime:a.runtime,output:c(()=>{},"output")},()=>K(()=>{const n=I(O(s)),p=S(h(r)),u=new Date().toISOString().slice(0,10),b=F(w);if(t.length>p.readings)throw new i("reading-identity/budget","REFUSE\xB7reading-identity/budget selected views exceed admitted readings");const L=D(()=>{const o=_(r).standing,m=[...M(s,a.runtime.entry,[],a.runtime.root,[],!0)].map(([g,v])=>[g,v]);if(!m.length)throw new i("reading-identity/failed","REFUSE\xB7reading-identity/failed is unavailable");const f=w(E("instrument-lock:"+a.runtime.root,()=>j(x(a.runtime.root,G),"utf8")));return{standing:o,implementation:m,instrumentLock:f}}),C=B(()=>z(s,()=>t.map(o=>{const m=q(p,()=>{const f=S(E("authenticated-configuration:"+r,()=>h(r)));if(JSON.stringify(f)!==JSON.stringify(p))throw new i("reading-identity/context","REFUSE\xB7reading-identity/context identity budget changed");if(o==="evidence"&&E("foreign-ingress:"+s,()=>H(s)).length)throw new i("reading-identity/selection","REFUSE\xB7reading-identity/selection foreign view identity requires a transport snapshot");const{standing:g,implementation:v,instrumentLock:J}=L();if(o==="evidence"){const l=W(g).get(o);if(!l||l.shape||l.regions.length!==1||l.regions[0]?.name!=="evidence")throw new i("reading-identity/selection","REFUSE\xB7reading-identity/selection requires the native evidence projection")}const R=o==="lines"?g.filter(P):$(r,s);return{text:R.length?R.join(`
|
|
2
|
+
`)+`
|
|
3
|
+
`:"",implementation:v,instrumentLock:J}});return{view:o,captured:m}}))),A=I(O(s));if(n!==A||u!==new Date().toISOString().slice(0,10)||JSON.stringify(b)!==JSON.stringify(F(w)))throw new i("reading-identity/context","REFUSE\xB7reading-identity/context committed inputs changed during preparation");return{kind:"fact",views:C.map(({view:o,captured:m})=>{const f=w(JSON.stringify(["native-view-inputs@1",r,o,n,m.inputs,m.value.implementation,m.value.instrumentLock,b,u]));if(d[o]!==void 0&&d[o]!==f)throw new i("reading-identity/context","REFUSE\xB7reading-identity/context view identity differs from the expected snapshot");return{kind:"fact",view:o,identity:f,text:m.value.text}})}}))}catch(t){return{kind:"refuse",code:t instanceof i?t.code:"reading-identity/failed",diagnostic:t instanceof Error?t.message:"REFUSE\xB7view identity failed"}}}c(Y,"prepareNativeViewIdentities");export{S as declaredReadingIdentityLimits,Se as prepareNativeReadingIdentity,Y as prepareNativeViewIdentities,be as prepareNativeViewIdentity};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var ae=Object.defineProperty;var d=(t,r)=>ae(t,"name",{value:r,configurable:!0});import{mkdirSync as ce,readFileSync as T,existsSync as $}from"node:fs";import{join as W,dirname as de}from"node:path";import{library14_canonical as j,library14_parse as le,library14_PROTOCOL as ue,library14_byBytes as fe}from"../vendor/runtime.js";import{replaceWhole as pe,shardLines as he}from"../land/ledger.js";import{fieldOf as g}from"../fold/claims.js";import{library5_readingIdentityBytes as O,library5_readingReceiptIdentity as ge,library5_responseIdentity as ye,library5_snapshotAccess as me,library5_placeInputs as Ee,library5_withPlaceInputs as we}from"../vendor/runtime.js";import{withReadingBoundary as be}from"./reading-context.js";import{BoundRefusal as y}from"./refusals.js";class ee extends Error{static{d(this,"ReadingNeeds")}constructor(r){super("Reading inputs missing"),this.coordinates=r}}const te=d(t=>t.reduce((r,m)=>r+Buffer.byteLength(m.text,"utf8"),0),"filesBytes"),q=d(t=>{const r=le(t);if(r.kind!=="fact")throw Error("REFUSE\xB7input projection contains a non-wire line");return r.value.fields},"readingFields"),xe=d(t=>t.map(r=>j({scope:r.place,form:"alphabet",measure:"text",role:"writes",value:"lock",shape:r.place,about:r.text,by:"host",at:"policy:handed-input"})),"inputLines");function Se(t){const r=t.preparationLimits;if(r){for(const e of[r.bytes,r.files,r.readings,r.probes])if(!Number.isSafeInteger(e)||e<1)throw new y("host/budget","REFUSE\xB7host/budget");if(te(t.files)>r.bytes||t.files.length>r.files||r.probes<1)throw new y("host/budget","REFUSE\xB7host/budget")}const{root:m,store:b,speaker:S,digest:s,capsule:f,region:E,projection:h,files:w}=t;t.assertCurrent?.(),t.context&&O(t.context);let z=[];if(t.current){if(!t.context)throw Error("REFUSE\xB7current reading requires a declared identity");const{snapshot:e,kept:n,current:i,permission:o}=t.current;if(i.implementation!==t.context.implementation)throw Error("REFUSE\xB7current reading implementation differs from its verified context");const a=ye(e,i,s),u=me(e,n,i,o,s);if(!u.inspect||!u.reuse)throw Error("REFUSE\xB7current reading is not admitted for reuse");z=[["response",a],["permission",s(JSON.stringify([o.inspect,o.reuse,o.freshness]))]]}if(t.execution&&(!t.context||JSON.stringify(O(t.execution.context))!==JSON.stringify(O(t.context))))throw Error("REFUSE\xB7reading execution differs from declared context");const A=d(e=>{if(!f.selectedWorld)return;const{standing:n,offer:i}=f.selectedWorld;if(n.parts.some(a=>a.scope===`${i}/place-inputs/${e}`&&a.measure==="reads")){if(!t.context||!t.execution||!t.livePlace)throw Error("REFUSE\xB7place inputs require authenticated shadow and reading context "+e);return Ee(n,i,e,t.livePlace(),a=>s(new TextDecoder().decode(a)))}},"projectionFor"),k=A(h),D=A(E),M=d((e,n,i)=>i&&e?we(e,JSON.stringify(["place-inputs",f.selectedWorld.offer,n]),i):e,"commit"),G=d((e,n,i=t.context)=>be(e,t.execution&&i?{...t.execution,context:i}:t.execution,()=>f.ask(n,t.readerReference)),"ask"),N=M(t.context,h,k),re=N?s(new TextDecoder().decode(O(N))):"",ne=s("").split(":")[0],R=new Map(w.map(e=>[e.place,e.text]));if(R.size!==w.length)throw Error("REFUSE\xB7input projection has duplicate handed coordinates");const I=d(()=>{if(r?.signal?.aborted)throw new y("host/cancelled","REFUSE\xB7host/cancelled");for(const e of w){if(!$(W(m,e.place)))throw new ee([e.place]);if(!T(W(m,e.place)).equals(Buffer.from(e.text)))throw new y("reading-identity/integrity","REFUSE\xB7input changed "+e.place)}},"verify");if(I(),r?.signal?.aborted)throw new y("host/cancelled","REFUSE\xB7host/cancelled");const F=G(re,[{...k?{place:k}:{},protocol:ue,verb:"render",rootScope:"",region:h,at:1,reads:["**"],lines:xe(w),files:[]}],N)[0];if(F?.kind!=="fact"||!Array.isArray(F.lines))throw Error("REFUSE\xB7input projection "+(F?.why??"no lines"));if(r&&F.lines.length>r.readings)throw new y("host/budget","REFUSE\xB7host/budget");const v=new Map;for(const e of F.lines){const n=q(e),i=n.scope??"",o=/^receipt:(\d+)$/.exec(n.at??"");if(!i||v.has(i)||n.form!=="alphabet"||n.measure!=="reads"||n.role!=="reads"||!o)throw Error("REFUSE\xB7input projection ambiguous reading "+i);const a=(n.value??"").split("|").filter(Boolean),u=(n.needs??"").split("|").filter(Boolean);if(!a.length&&!u.length||new Set(a).size!==a.length||a.some(x=>!R.has(x))||new Set(u).size!==u.length)throw Error("REFUSE\xB7input projection has unhanded or duplicate dependencies "+i);const p=Number(o[1]);if(!Number.isSafeInteger(p))throw Error("REFUSE\xB7input projection invalid resolution "+i);v.set(i,{scope:i,inputs:a,needs:u,resolution:p,line:j({scope:i,form:n.form,measure:n.measure,role:n.role,value:a.join("|"),needs:u.join("|"),at:n.at})})}if(!v.size)throw Error("REFUSE\xB7input projection returned no readings");const H=[],J=new Set,K=new Set,Q=d(e=>{if(K.has(e))return;if(J.has(e))throw Error("REFUSE\xB7input projection dependency cycle "+e);const n=v.get(e);if(!n)throw Error("REFUSE\xB7input projection missing reading "+e);J.add(e);for(const i of n.needs)Q(i);J.delete(e),K.add(e),H.push(n)},"visit");for(const e of v.keys())Q(e);const B=s([f.selection??"",f.digest,E,h,...t.lifetime===void 0?[]:[JSON.stringify(t.lifetime)]].join(`
|
|
2
|
+
`)),_=S+":"+B.split(":")[1].slice(0,12),V="reading-closures/"+B.split(":")[1],P=he(b,S,V),X=d(e=>W(b,"cas","carried",e.split(":")[1]??""),"carried"),ie=d(e=>{const n=s(e),i=X(n);return ce(de(i),{recursive:!0}),(!$(i)||s(T(i,"utf8"))!==n)&&pe(i,e),n},"saved"),C=d((e,n=!1)=>{if(!e.startsWith(ne+":")||!/^[a-f0-9]+$/.test(e.split(":")[1]??""))return;const i=X(e);if(!$(i))return;const o=T(i,"utf8");if(s(o)!==e){if(n)throw new y("reading-identity/integrity","REFUSE\xB7reading carrier bytes differ");return}return o},"load"),se=d((e,n)=>{const i=[e.line,j({scope:"implementation",role:"writes",form:"alphabet",measure:"digest",value:B,by:"host",at:"policy:input-identity"}),...e.inputs.map(c=>j({scope:c,role:"writes",form:"alphabet",measure:"digest",value:s(R.get(c)),by:"host",at:"policy:input-identity"})),...e.needs.map(c=>j({scope:c,role:"writes",form:"alphabet",measure:"digest",value:n.get(c).digest,by:"host",at:"policy:input-identity"}))].sort(fe).join(`
|
|
3
|
+
`)+`
|
|
4
|
+
`,o=M(t.context,E,D),a=o&&{...o,policy:s(JSON.stringify([o.policy,t.allowsEmpty===!0,t.lifetime??null,t.readerReference??null,f.selection??null,f.lines])),inputs:[...[...o.inputs,...z].map(([c,U])=>[JSON.stringify(["context",c]),U]),[JSON.stringify(["plan"]),s(e.line)],...e.inputs.map(c=>[JSON.stringify(["file",c]),s(R.get(c))]),...e.needs.map(c=>[JSON.stringify(["receipt",c]),n.get(c).digest])]},u=a?new TextDecoder().decode(O(a)):i,p=s(u),x="receipts/readings/"+s(e.scope).split(":")[1]+"/"+p.split(":")[1];return{context:a,closure:u,input:p,scope:x}},"prepare"),oe=d((e,n=!1)=>{const{scope:i,input:o,closure:a}=e,p=P.filter(l=>g(l,"scope")===i&&g(l,"measure")==="bytes"&&g(l,"value")===o).at(-1)&&P.filter(l=>g(l,"scope")===i&&g(l,"measure")==="count"&&g(l,"by")===_).at(-1),x=p&&C(g(p,"at").replace(/^place:/,""),n),c=x?.split(`
|
|
5
|
+
`).filter(Boolean),U=c!==void 0&&g(p,"value")===`${c.length}..${c.length}`&&c.every(l=>{const Z=q(l);return Z.by===_&&Z.at==="place:"+o})?x:void 0;if(U===void 0||C(o,n)!==a)return;if(U===""&&!t.allowsEmpty)throw Error("REFUSE\xB7reading empty response is not admitted");const Y=U.split(`
|
|
6
|
+
`).filter(Boolean);for(const l of Y)q(l);const L=g(p,"at").replace(/^place:/,"");return{lines:Y,payload:L,digest:e.context?ge(o,L,l=>s(new TextDecoder().decode(l))):L}},"retained");return I(),{ordered:H,identity:B,by:_,journal:V,prior:P,prepare:se,retained:oe,verify:I,ask:G,load:C,saved:ie,handed:R,readingPlace:D,verifiedBytes:te(w)}}d(Se,"readingPreparation");function Ie(t){try{if(!t.context||!t.execution)throw new y("reading-identity/context","REFUSE\xB7reading preparation requires its actual execution context");const r=Se(t),m=new Map,b=[];let S=0;for(const s of r.ordered){t.assertCurrent?.(),r.verify();const f=s.needs.filter(w=>!m.has(w));if(f.length){b.push({scope:s.scope,kind:"needs",needs:f});continue}const E=r.prepare(s,m),h=r.retained(E,!0);h&&(m.set(s.scope,h),S++),b.push({scope:s.scope,kind:"ready",identity:E.input,context:E.context,...h?{receipt:h.digest}:{}})}return r.verify(),t.assertCurrent?.(),{kind:"fact",readings:b,probes:1,verifiedBytes:r.verifiedBytes,retained:S}}catch(r){return r instanceof ee?{kind:"needs",needs:r.coordinates}:{kind:"refuse",code:r instanceof y?r.code:"reading-identity/failed",diagnostic:r instanceof Error?r.message:"REFUSE\xB7reading preparation failed"}}}d(Ie,"prepareReadingIdentities");export{xe as inputLines,Ie as prepareReadingIdentities,q as readingFields,Se as readingPreparation};
|
|
@@ -1,40 +1 @@
|
|
|
1
|
-
|
|
2
|
-
type ObjectContext = Parameters<typeof validateObjectContext>[1];
|
|
3
|
-
type Signer = Parameters<typeof authorityOf>[1][number];
|
|
4
|
-
export interface ReferenceTrust {
|
|
5
|
-
readonly snapshot: string;
|
|
6
|
-
readonly epoch: number;
|
|
7
|
-
readonly proofScope: string;
|
|
8
|
-
readonly policy: string;
|
|
9
|
-
readonly formImplementation: string;
|
|
10
|
-
readonly signers: readonly Signer[];
|
|
11
|
-
readonly algorithms: readonly string[];
|
|
12
|
-
readonly contracts?: {
|
|
13
|
-
readonly refusals: string;
|
|
14
|
-
readonly standing: string;
|
|
15
|
-
};
|
|
16
|
-
readonly objects: ObjectContext;
|
|
17
|
-
readonly maxBytes: number;
|
|
18
|
-
readonly maxRecords: number;
|
|
19
|
-
readonly digestBytes: (bytes: Uint8Array) => string | Promise<string>;
|
|
20
|
-
readonly verifyBytes: (bytes: string, key: string, signature: string, algorithms: readonly string[]) => boolean | Promise<boolean>;
|
|
21
|
-
readonly fetchBytes: (source: string, blob: string) => Promise<Uint8Array>;
|
|
22
|
-
}
|
|
23
|
-
/** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
|
|
24
|
-
export declare function inspectAuthenticatedReference(fragment: string, trust: ReferenceTrust): Promise<{
|
|
25
|
-
contracts?: {
|
|
26
|
-
refusals: string;
|
|
27
|
-
standing: string;
|
|
28
|
-
} | undefined;
|
|
29
|
-
reference: import("./reference-profile.ts").StandingReference;
|
|
30
|
-
records: readonly import("@lapxo/topos/wire").ObjectRecord[];
|
|
31
|
-
semanticIdentity: string;
|
|
32
|
-
authorityContextIdentity: string;
|
|
33
|
-
semanticStanding: string;
|
|
34
|
-
originalRecords: readonly string[];
|
|
35
|
-
}>;
|
|
36
|
-
export { encodeReference, decodeReference } from './reference-profile.ts';
|
|
37
|
-
export { BoundRefusal, readRefusalCatalog, describeRefusal } from './refusals.ts';
|
|
38
|
-
export type { RefusalEntry } from './refusals.ts';
|
|
39
|
-
export { readContent, retainContent, invokeSigningPort } from '../engine/ports.ts';
|
|
40
|
-
export type { PortLimits, ContentReadPort, PersistencePort, DigestPort, SigningPort, SigningLot, SignatureRecord } from '../engine/ports.ts';
|
|
1
|
+
export {public7_BoundRefusal as BoundRefusal,public7_ContentReadPort as ContentReadPort,public7_DigestPort as DigestPort,public7_PersistencePort as PersistencePort,public7_PortLimits as PortLimits,public7_ReferenceTrust as ReferenceTrust,public7_RefusalEntry as RefusalEntry,public7_SignatureRecord as SignatureRecord,public7_SigningLot as SigningLot,public7_SigningPort as SigningPort,public7_decodeReference as decodeReference,public7_describeRefusal as describeRefusal,public7_encodeReference as encodeReference,public7_inspectAuthenticatedReference as inspectAuthenticatedReference,public7_invokeSigningPort as invokeSigningPort,public7_readContent as readContent,public7_readRefusalCatalog as readRefusalCatalog,public7_retainContent as retainContent} from "../public-types.js";
|
|
@@ -1,100 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { referenceStanding, referenceContracts, referenceContextBytes } from "./reference-standing.js";
|
|
5
|
-
function fail(code, why) { throw new BoundRefusal('authenticated-reference/' + code, 'REFUSE·authenticated-reference ' + why); }
|
|
6
|
-
const utf8 = (s) => new TextEncoder().encode(s);
|
|
7
|
-
/** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
|
|
8
|
-
export async function inspectAuthenticatedReference(fragment, trust) {
|
|
9
|
-
if (new Set(trust.signers.map(s => s.id)).size !== trust.signers.length)
|
|
10
|
-
fail('context', 'duplicate host signer');
|
|
11
|
-
const reference = decodeReference(fragment);
|
|
12
|
-
if (!Number.isSafeInteger(trust.maxBytes) || trust.maxBytes < 1 || !Number.isSafeInteger(trust.maxRecords) || trust.maxRecords < 1)
|
|
13
|
-
fail('budget', 'resource budget');
|
|
14
|
-
if (trust.contracts !== undefined) {
|
|
15
|
-
if (!referenceContracts(trust.contracts))
|
|
16
|
-
fail('context', 'contract identities');
|
|
17
|
-
const snapshot = await trust.digestBytes(referenceContextBytes({ ...trust, semanticIdentity: 'sha256:' + reference.digest, contracts: trust.contracts }));
|
|
18
|
-
if (snapshot !== trust.snapshot)
|
|
19
|
-
fail('context', 'context snapshot');
|
|
20
|
-
}
|
|
21
|
-
let received;
|
|
22
|
-
try {
|
|
23
|
-
received = await trust.fetchBytes(reference.source, reference.blob);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return fail('transport', 'content port failed');
|
|
27
|
-
}
|
|
28
|
-
if (received.byteLength > trust.maxBytes)
|
|
29
|
-
fail('budget', 'bytes budget');
|
|
30
|
-
if (typeof SharedArrayBuffer !== 'undefined' && received.buffer instanceof SharedArrayBuffer)
|
|
31
|
-
fail('transport', 'shared transport bytes');
|
|
32
|
-
const bytes = new Uint8Array(received);
|
|
33
|
-
if (await trust.digestBytes(bytes) !== reference.blob)
|
|
34
|
-
fail('integrity', 'blob integrity');
|
|
35
|
-
let text;
|
|
36
|
-
try {
|
|
37
|
-
text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return fail('framing', 'carrier UTF-8');
|
|
41
|
-
}
|
|
42
|
-
let packet;
|
|
43
|
-
try {
|
|
44
|
-
packet = JSON.parse(text);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
fail('framing', 'carrier framing');
|
|
48
|
-
}
|
|
49
|
-
if (!Array.isArray(packet) || packet.length !== 3 || packet[0] !== 'authenticated-standing@1' || !Array.isArray(packet[1]) || packet[1].length < 1 || packet[1].length > trust.maxRecords || packet[1].some((x) => typeof x !== 'string') || typeof packet[2] !== 'string' || JSON.stringify(packet) + '\n' !== text)
|
|
50
|
-
fail('framing', 'carrier framing');
|
|
51
|
-
const originals = packet[1], proofLine = packet[2];
|
|
52
|
-
const parsed = [...originals, proofLine].map(line => { const p = parse(line); if (p.kind !== 'fact' || canonical(p.value.fields) !== line)
|
|
53
|
-
fail('wire', 'original canonical'); return p.value.fields; });
|
|
54
|
-
const proof = parsed[parsed.length - 1];
|
|
55
|
-
if (proof.scope !== trust.proofScope || proof.at !== 'witness:' + trust.snapshot || proof.role !== 'writes' || proof.form !== 'alphabet' || proof.measure !== 'digest' || proof.epoch !== String(trust.epoch))
|
|
56
|
-
fail('context', 'untrusted snapshot binding');
|
|
57
|
-
if (proof.value !== await trust.digestBytes(utf8(JSON.stringify(['authenticated-standing@1', originals]) + '\n')))
|
|
58
|
-
fail('integrity', 'snapshot commitment');
|
|
59
|
-
// Async crypto port is called on exact native signed bytes; authorityOf consumes only those results.
|
|
60
|
-
const verifiedCrypto = new Set();
|
|
61
|
-
const key = (f, pub) => JSON.stringify([signedBytes(f), pub, f.sig, trust.algorithms]);
|
|
62
|
-
for (const f of parsed)
|
|
63
|
-
for (const s of trust.signers)
|
|
64
|
-
if (s.id === f.by && await trust.verifyBytes(signedBytes(f), s.publicKey, f.sig, trust.algorithms))
|
|
65
|
-
verifiedCrypto.add(key(f, s.publicKey));
|
|
66
|
-
const verifies = (f, pub) => verifiedCrypto.has(key(f, pub));
|
|
67
|
-
const authentic = authorityOf(proof, trust.signers, verifies);
|
|
68
|
-
if (authentic.kind !== 'admitted')
|
|
69
|
-
fail('authority', 'proof ' + authentic.kind + ' ' + authentic.why);
|
|
70
|
-
const signer = trust.signers.find(s => s.id === proof.by);
|
|
71
|
-
if (!signer)
|
|
72
|
-
fail('authority', 'proof signer');
|
|
73
|
-
const current = authorityAtEpoch(proof, signer, trust.epoch, trust.proofScope, 'authority');
|
|
74
|
-
if (current.kind !== 'admitted')
|
|
75
|
-
fail('authority', 'current proof ' + current.kind + ' ' + current.why);
|
|
76
|
-
const verified = new Set();
|
|
77
|
-
for (let i = 0; i < originals.length; i++) {
|
|
78
|
-
const admitted = authorityOf(parsed[i], trust.signers, verifies);
|
|
79
|
-
if (admitted.kind !== 'admitted')
|
|
80
|
-
fail('authority', 'original ' + admitted.kind + ' ' + admitted.why);
|
|
81
|
-
verified.add(originals[i]);
|
|
82
|
-
}
|
|
83
|
-
const history = objectHistory(originals);
|
|
84
|
-
if (history.kind !== 'fact')
|
|
85
|
-
fail('wire', 'wire ' + history.why);
|
|
86
|
-
if (history.value.configuration.some(f => f.value === 'withdraw'))
|
|
87
|
-
fail('history', 'configuration history requires admitted standing fold');
|
|
88
|
-
const context = { ...trust.objects, admit: (r) => verified.has(canonical(r.fields)) && trust.objects.admit(r) };
|
|
89
|
-
const checked = validateObjectContext(history.value.objects, context);
|
|
90
|
-
if (checked.kind !== 'fact')
|
|
91
|
-
throw new BoundRefusal('authenticated-reference/object-context', 'REFUSE·' + checked.why);
|
|
92
|
-
const semantic = referenceStanding(originals, checked.value), semanticIdentity = await trust.digestBytes(utf8(semantic));
|
|
93
|
-
if (semanticIdentity !== 'sha256:' + reference.digest)
|
|
94
|
-
fail('identity', 'standing identity');
|
|
95
|
-
const authorityContextIdentity = await trust.digestBytes(utf8(JSON.stringify(trust.contracts ? ['reference-inspection-trust@2', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms, trust.contracts] : ['reference-inspection-trust@1', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms])));
|
|
96
|
-
return { reference, records: checked.value, semanticIdentity, authorityContextIdentity, semanticStanding: semantic, originalRecords: originals, ...(trust.contracts ? { contracts: { ...trust.contracts } } : {}) };
|
|
97
|
-
}
|
|
98
|
-
export { encodeReference, decodeReference } from "./reference-profile.js";
|
|
99
|
-
export { BoundRefusal, readRefusalCatalog, describeRefusal } from "./refusals.js";
|
|
100
|
-
export { readContent, retainContent, invokeSigningPort } from "../engine/ports.js";
|
|
1
|
+
var _=Object.defineProperty;var s=(r,e)=>_(r,"name",{value:e,configurable:!0});import{BoundRefusal as A}from"./refusals.js";import{library14_parse as J,library14_canonical as C,library14_authorityOf as O,library14_authorityAtEpoch as U,library14_signedBytes as j,library14_objectHistory as F,library14_validateObjectContext as T}from"../vendor/runtime.js";import{decodeReference as H}from"./reference-profile.js";import{referenceStanding as K,referenceContracts as L,referenceContextBytes as q}from"./reference-standing.js";function t(r,e){throw new A("authenticated-reference/"+r,"REFUSE\xB7authenticated-reference "+e)}s(t,"fail");const b=s(r=>new TextEncoder().encode(r),"utf8");async function Q(r,e){new Set(e.signers.map(i=>i.id)).size!==e.signers.length&&t("context","duplicate host signer");const f=H(r);(!Number.isSafeInteger(e.maxBytes)||e.maxBytes<1||!Number.isSafeInteger(e.maxRecords)||e.maxRecords<1)&&t("budget","resource budget"),e.contracts!==void 0&&(L(e.contracts)||t("context","contract identities"),await e.digestBytes(q({...e,semanticIdentity:"sha256:"+f.digest,contracts:e.contracts}))!==e.snapshot&&t("context","context snapshot"));let d;try{d=await e.fetchBytes(f.source,f.blob)}catch{return t("transport","content port failed")}d.byteLength>e.maxBytes&&t("budget","bytes budget"),typeof SharedArrayBuffer<"u"&&d.buffer instanceof SharedArrayBuffer&&t("transport","shared transport bytes");const w=new Uint8Array(d);await e.digestBytes(w)!==f.blob&&t("integrity","blob integrity");let p;try{p=new TextDecoder("utf-8",{fatal:!0}).decode(w)}catch{return t("framing","carrier UTF-8")}let o;try{o=JSON.parse(p)}catch{t("framing","carrier framing")}(!Array.isArray(o)||o.length!==3||o[0]!=="authenticated-standing@1"||!Array.isArray(o[1])||o[1].length<1||o[1].length>e.maxRecords||o[1].some(i=>typeof i!="string")||typeof o[2]!="string"||JSON.stringify(o)+`
|
|
2
|
+
`!==p)&&t("framing","carrier framing");const c=o[1],E=o[2],y=[...c,E].map(i=>{const n=J(i);return(n.kind!=="fact"||C(n.value.fields)!==i)&&t("wire","original canonical"),n.value.fields}),a=y[y.length-1];(a.scope!==e.proofScope||a.at!=="witness:"+e.snapshot||a.role!=="writes"||a.form!=="alphabet"||a.measure!=="digest"||a.epoch!==String(e.epoch))&&t("context","untrusted snapshot binding"),a.value!==await e.digestBytes(b(JSON.stringify(["authenticated-standing@1",c])+`
|
|
3
|
+
`))&&t("integrity","snapshot commitment");const x=new Set,S=s((i,n)=>JSON.stringify([j(i),n,i.sig,e.algorithms]),"key");for(const i of y)for(const n of e.signers)n.id===i.by&&await e.verifyBytes(j(i),n.publicKey,i.sig,e.algorithms)&&x.add(S(i,n.publicKey));const B=s((i,n)=>x.has(S(i,n)),"verifies"),l=O(a,e.signers,B);l.kind!=="admitted"&&t("authority","proof "+l.kind+" "+l.why);const u=e.signers.find(i=>i.id===a.by);u||t("authority","proof signer");const m=U(a,u,e.epoch,e.proofScope,"authority");m.kind!=="admitted"&&t("authority","current proof "+m.kind+" "+m.why);const v=new Set;for(let i=0;i<c.length;i++){const n=O(y[i],e.signers,B);n.kind!=="admitted"&&t("authority","original "+n.kind+" "+n.why),v.add(c[i])}const g=F(c);g.kind!=="fact"&&t("wire","wire "+g.why),g.value.configuration.some(i=>i.value==="withdraw")&&t("history","configuration history requires admitted standing fold");const I={...e.objects,admit:s(i=>v.has(C(i.fields))&&e.objects.admit(i),"admit")},h=T(g.value.objects,I);if(h.kind!=="fact")throw new A("authenticated-reference/object-context","REFUSE\xB7"+h.why);const R=K(c,h.value),k=await e.digestBytes(b(R));k!=="sha256:"+f.digest&&t("identity","standing identity");const N=await e.digestBytes(b(JSON.stringify(e.contracts?["reference-inspection-trust@2",e.snapshot,e.epoch,e.proofScope,e.policy,e.formImplementation,e.signers,e.algorithms,e.contracts]:["reference-inspection-trust@1",e.snapshot,e.epoch,e.proofScope,e.policy,e.formImplementation,e.signers,e.algorithms])));return{reference:f,records:h.value,semanticIdentity:k,authorityContextIdentity:N,semanticStanding:R,originalRecords:c,...e.contracts?{contracts:{...e.contracts}}:{}}}s(Q,"inspectAuthenticatedReference");import{encodeReference as X,decodeReference as Y}from"./reference-profile.js";import{BoundRefusal as $,readRefusalCatalog as ee,describeRefusal as ie}from"./refusals.js";import{readContent as ne,retainContent as oe,invokeSigningPort as ae}from"../engine/ports.js";export{$ as BoundRefusal,Y as decodeReference,ie as describeRefusal,X as encodeReference,Q as inspectAuthenticatedReference,ae as invokeSigningPort,ne as readContent,ee as readRefusalCatalog,oe as retainContent};
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { sha256 } from "./hash.js";
|
|
3
|
-
/** Exact shipped interpretation contracts; this helper grants no authority. */
|
|
4
|
-
export function installedReferenceContracts() {
|
|
5
|
-
return { refusals: sha256(readFileSync(new URL('../../docs/contracts/refusals.bound', import.meta.url))), standing: sha256(readFileSync(new URL('../../docs/contracts/standing.bound', import.meta.url))) };
|
|
6
|
-
}
|
|
1
|
+
var s=Object.defineProperty;var r=(e,o)=>s(e,"name",{value:o,configurable:!0});import{readFileSync as t}from"node:fs";import{sha256 as n}from"./hash.js";function d(){return{refusals:n(t(new URL("../../docs/contracts/refusals.bound",import.meta.url))),standing:n(t(new URL("../../docs/contracts/standing.bound",import.meta.url)))}}r(d,"installedReferenceContracts");export{d as installedReferenceContracts};
|