@lapxo/bound 0.1.9 → 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.
Files changed (133) hide show
  1. package/README.md +10 -10
  2. package/TARGET.bound +57 -14
  3. package/assets/wasm/guest.js +4 -0
  4. package/assets/wasm/manifest.json +27 -0
  5. package/assets/wasm/runtime.js +6 -0
  6. package/assets/wasm/runtime.wasm +0 -0
  7. package/dist/cli/pass.js +3 -3
  8. package/dist/cli/place.d.ts +2 -0
  9. package/dist/cli/place.js +7 -4
  10. package/dist/cli/verb.js +43 -6
  11. package/dist/engine/index.d.ts +11 -0
  12. package/dist/engine/index.js +9 -0
  13. package/dist/engine/object.d.ts +17 -0
  14. package/dist/engine/object.js +38 -0
  15. package/dist/fold/closed.d.ts +1 -1
  16. package/dist/fold/closed.js +22 -5
  17. package/dist/fold/digests.js +3 -2
  18. package/dist/fold/object.d.ts +3 -17
  19. package/dist/fold/object.js +2 -38
  20. package/dist/fold/resolved.js +1 -1
  21. package/dist/host/admitted-place.js +4 -1
  22. package/dist/host/cell-receipts.js +1 -1
  23. package/dist/host/objects.d.ts +2 -1
  24. package/dist/host/objects.js +24 -4
  25. package/dist/host/reading-closures.d.ts +2 -0
  26. package/dist/host/reading-closures.js +11 -13
  27. package/dist/host/reference-authenticated.d.ts +28 -0
  28. package/dist/host/reference-authenticated.js +84 -0
  29. package/dist/host/reference-native.d.ts +10 -0
  30. package/dist/host/reference-native.js +50 -0
  31. package/dist/host/reference-profile.d.ts +23 -0
  32. package/dist/host/reference-profile.js +62 -0
  33. package/dist/host/reference.d.ts +6 -0
  34. package/dist/host/reference.js +24 -0
  35. package/dist/host/selected-topos.d.ts +1 -1
  36. package/dist/host/selected-topos.js +9 -1
  37. package/dist/observe/regions.d.ts +4 -0
  38. package/dist/observe/regions.js +109 -1
  39. package/dist/wasm/adapter.d.ts +51 -0
  40. package/dist/wasm/adapter.js +180 -0
  41. package/dist/wasm/node-worker.d.ts +1 -0
  42. package/dist/wasm/node-worker.js +13 -0
  43. package/dist/wasm/object.d.ts +8 -0
  44. package/dist/wasm/object.js +76 -0
  45. package/dist/wasm/worker.d.ts +1 -0
  46. package/dist/wasm/worker.js +7 -0
  47. package/docs/contracts/authenticated-reference.publication.json +25 -0
  48. package/docs/contracts/engine-object-projection.publication.json +25 -0
  49. package/docs/contracts/engine.md +39 -0
  50. package/docs/contracts/native-publication.inventory.json +8 -0
  51. package/docs/contracts/optional-wasm-adapter.publication.json +25 -0
  52. package/docs/contracts/publication-additions.inventory.json +8 -0
  53. package/docs/contracts/reference-port.md +33 -0
  54. package/docs/contracts/reference-port.publication.json +25 -0
  55. package/licenses/wasm/bound.txt +21 -0
  56. package/licenses/wasm/fast-text-encoding.txt +201 -0
  57. package/licenses/wasm/jitl-quickjs-ffi-types.txt +21 -0
  58. package/licenses/wasm/jitl-quickjs-wasmfile-release-sync.txt +47 -0
  59. package/licenses/wasm/obligations.txt +21 -0
  60. package/licenses/wasm/quickjs-emscripten-core.txt +21 -0
  61. package/licenses/wasm/topos.txt +21 -0
  62. package/package.json +94 -4
  63. package/receipts.bound +7672 -613
  64. package/samples/authenticated-reference/no.json +31 -0
  65. package/samples/authenticated-reference/yes/editorial-state/TARGET.bound +57 -0
  66. package/samples/authenticated-reference/yes/editorial-state/inputs/arrival.json +1 -0
  67. package/samples/authenticated-reference/yes/editorial-state/inputs/coverage.json +1 -0
  68. package/samples/authenticated-reference/yes/editorial-state/inputs/era.json +1 -0
  69. package/samples/authenticated-reference/yes/editorial-state/inputs/order.json +1 -0
  70. package/samples/authenticated-reference/yes/editorial-state/inputs/signature.json +1 -0
  71. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.arrival.json +1 -0
  72. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.coverage.json +1 -0
  73. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.era.json +1 -0
  74. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.order.json +1 -0
  75. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.signature.json +1 -0
  76. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.yes.json +1 -0
  77. package/samples/authenticated-reference/yes/editorial-state/inputs/yes.json +1 -0
  78. package/samples/authenticated-reference/yes/weather-reading/TARGET.bound +57 -0
  79. package/samples/authenticated-reference/yes/weather-reading/inputs/arrival.json +1 -0
  80. package/samples/authenticated-reference/yes/weather-reading/inputs/coverage.json +1 -0
  81. package/samples/authenticated-reference/yes/weather-reading/inputs/era.json +1 -0
  82. package/samples/authenticated-reference/yes/weather-reading/inputs/order.json +1 -0
  83. package/samples/authenticated-reference/yes/weather-reading/inputs/signature.json +1 -0
  84. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.arrival.json +1 -0
  85. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.coverage.json +1 -0
  86. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.era.json +1 -0
  87. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.order.json +1 -0
  88. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.signature.json +1 -0
  89. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.yes.json +1 -0
  90. package/samples/authenticated-reference/yes/weather-reading/inputs/yes.json +1 -0
  91. package/samples/authenticated-reference/yes.json +19 -0
  92. package/samples/engine-alphabet/TARGET.bound +29 -0
  93. package/samples/engine-alphabet/inputs/sample.json +323 -0
  94. package/samples/engine-alphabet/package.json +1 -0
  95. package/samples/engine-alphabet/reader.js +1457 -0
  96. package/samples/engine-alphabet/receipts.bound +12 -0
  97. package/samples/engine-interval/TARGET.bound +29 -0
  98. package/samples/engine-interval/inputs/sample.json +277 -0
  99. package/samples/engine-interval/package.json +1 -0
  100. package/samples/engine-interval/reader.js +1457 -0
  101. package/samples/engine-interval/receipts.bound +12 -0
  102. package/samples/no/engine-order/TARGET.bound +29 -0
  103. package/samples/no/engine-order/inputs/sample.json +277 -0
  104. package/samples/no/engine-order/package.json +1 -0
  105. package/samples/no/engine-order/reader.js +1457 -0
  106. package/samples/reference-port/no/catalog/standing.json +1 -0
  107. package/samples/reference-port/no/catalog/transport.json +1 -0
  108. package/samples/reference-port/no/route-notes/standing.json +1 -0
  109. package/samples/reference-port/no/route-notes/transport.json +1 -0
  110. package/samples/reference-port/yes/catalog/TARGET.bound +33 -0
  111. package/samples/reference-port/yes/catalog/standing.json +1 -0
  112. package/samples/reference-port/yes/catalog/transport.json +1 -0
  113. package/samples/reference-port/yes/route-notes/TARGET.bound +33 -0
  114. package/samples/reference-port/yes/route-notes/standing.json +1 -0
  115. package/samples/reference-port/yes/route-notes/transport.json +1 -0
  116. package/samples/wasm/editorial-notes/TARGET.bound +65 -0
  117. package/samples/wasm/editorial-notes/esm.bound +59 -0
  118. package/samples/wasm/editorial-notes/no-deadline.bound +65 -0
  119. package/samples/wasm/editorial-notes/no-memory.bound +65 -0
  120. package/samples/wasm/editorial-notes/no-missing-limit.bound +64 -0
  121. package/samples/wasm/editorial-notes/receipts.bound +1 -0
  122. package/samples/wasm/water-level/TARGET.bound +65 -0
  123. package/samples/wasm/water-level/esm.bound +59 -0
  124. package/samples/wasm/water-level/no-deadline.bound +65 -0
  125. package/samples/wasm/water-level/no-memory.bound +65 -0
  126. package/samples/wasm/water-level/no-missing-limit.bound +64 -0
  127. package/samples/wasm/water-level/receipts.bound +1 -0
  128. package/vectors/engine-object.cli.json +548 -0
  129. package/vectors/native/authenticated-reference-formation.vector.json +53 -0
  130. package/vectors/native/authenticated-reference.cli.json +157 -0
  131. package/vectors/native/authenticated-reference.vector.json +440 -0
  132. package/vectors/reference-port.cli.json +688 -0
  133. package/vectors/wasm-object.cli.json +452 -0
@@ -1,38 +1,2 @@
1
- import { cell, sign, join, require as requireFloor, observe, mark, parts, state, live, restOf } from '@lapxo/obligations/views/field';
2
- import { restCoordinates } from '@lapxo/topos/wire';
3
- export function objectFold(records, context) {
4
- let world = new Map();
5
- const definitions = new Map(records.filter(r => r.record === 'cell').map(r => [r.fields.scope, r]));
6
- const codecs = new Map([...definitions].map(([name, c]) => [name, context.resolve(c)]));
7
- const ids = new Map(records.map(r => [r.fields.id, r]));
8
- for (const record of [...records].sort((a, b) => Number(a.fields.epoch) - Number(b.fields.epoch))) {
9
- const f = record.fields, name = f.scope, at = Number(f.epoch);
10
- if (record.record === 'cell') {
11
- world = new Map([...world, [name, cell(name, at, [], restCoordinates(f.restsOn))]]);
12
- continue;
13
- }
14
- const codec = codecs.get(name), L = codec.form.lattice(codec.params), held = world.get(name);
15
- if (record.record === 'claim') {
16
- const target = f.sign === '-1' ? ids.get(f.takes).fields : f;
17
- const changed = observe(held, { id: f.id, at, origin: target.origin, span: codec.form.parse(codec.params, target.value), ...(f.takes ? { takes: f.takes } : {}) }, f.sign === '-1' ? -1 : 1);
18
- const seen = [...changed.seen];
19
- seen[seen.length - 1] = { ...seen.at(-1), id: f.id };
20
- world = new Map([...world, [name, { ...changed, seen }]]);
21
- }
22
- else if (f.sign === '-1') {
23
- const target = ids.get(f.takes).fields;
24
- world = new Map([...world, [name, { ...held, marks: [...(held.marks ?? []), mark({ id: f.id, at, takes: f.takes, pole: target.pole, reach: 'local', span: codec.form.parse(codec.params, target.value) })] }]]);
25
- }
26
- else if (f.widens !== undefined) {
27
- const changed = join(L, held, codec.form.parse(codec.params, f.value), f.widens, at);
28
- const marks = [...(changed.marks ?? [])];
29
- marks[marks.length - 1] = { ...marks.at(-1), id: f.id };
30
- world = new Map([...world, [name, { ...changed, marks }]]);
31
- }
32
- else {
33
- const span = codec.form.parse(codec.params, f.value);
34
- world = f.pole === 'floor' ? requireFloor(L, world, name, span, { id: f.id, at }) : sign(L, world, name, span, { id: f.id, at });
35
- }
36
- }
37
- return [...world].map(([name, c]) => { const codec = codecs.get(name), L = codec.form.lattice(codec.params); return { cell: name, state: state(L, c, world), parts: parts(L, c, world), rest: restOf(c, world), ownMarks: live(c.marks ?? []).map(m => m.id), ownClaims: live(c.seen).map(s => ({ id: s.id, origin: s.origin })), marks: live(c.marks ?? []), claims: live(c.seen) }; });
38
- }
1
+ /** Compatibility path; the engine owns the single object projection. */
2
+ export { objectFold } from "../engine/index.js";
@@ -60,7 +60,7 @@ export function receiptsOf(fold) {
60
60
  return canonical({ scope: `${sourced(fold.standing)}/${place}${shape}`, role: 'writes', form: 'alphabet', measure: 'digest', value: digest, by: writerFor(fold.standing, 'fold') ?? '', at: `place:${digest}` });
61
61
  });
62
62
  const place = under.replace(/\/$/, '');
63
- const lines = [...named, ...[...cones].sort(), ...[...ran].sort(), ...paid]
63
+ const lines = [...new Set([...named, ...[...cones].sort(), ...[...ran].sort(), ...paid, ...(fold.receiptHistory ?? [])])]
64
64
  .map((line) => (place && !fieldOf(line, 'sig') ? zoomed(line, place) : line));
65
65
  if (!lines.length) {
66
66
  const shipped = (observeText(join(fold.root, under, RECEIPTS)) ?? '').split('\n').filter(isWire);
@@ -35,7 +35,10 @@ export function authenticatedRootStanding(root) {
35
35
  const snapshot = history.filter(record => !isConfig(record) || Number(fieldOf(record, 'epoch')) <= epoch);
36
36
  // The signature vocabulary is configuration, not the as-yet
37
37
  // unauthenticated evidence whose values are about to be folded.
38
- const wire = snapshot.filter(record => fieldOf(record, 'scope').startsWith('wire/'));
38
+ const wire = snapshot.filter(record => {
39
+ const scope = fieldOf(record, 'scope');
40
+ return scope.startsWith('wire/') || scope.startsWith('audit/wire/');
41
+ });
39
42
  authority = authorityFor(snapshot, anchor, eraOf(foldClaims(wire).standing).admitted);
40
43
  snapshots.set(epoch, authority);
41
44
  }
@@ -6,7 +6,7 @@ import { canonical, objectHistory, parse, byBytes, wireAt, RECEIPTS } from '@lap
6
6
  import { cellInputs, cellReceiptContract, cellInputProfile } from '@lapxo/topos/cell-inputs';
7
7
  import { renderCells } from '@lapxo/topos/cells-view';
8
8
  import { admittedObjects } from "./objects.js";
9
- import { objectFold } from "../fold/object.js";
9
+ import { objectFold } from "../engine/index.js";
10
10
  import { fullDigest, bytesDigest } from "./digest.js";
11
11
  import { closeReceipts, noteIdle } from "../fold/closed.js";
12
12
  import { authorityFor, rootSigner } from "../fold/signers.js";
@@ -1,5 +1,6 @@
1
1
  /** Bound supplies host authority and I/O; topos owns grammar, selection and context validation. */
2
- export declare function admittedObjects(root: string, lines: readonly string[], emit?: (text: string) => void): Promise<{
2
+ export declare function admittedObjects(root: string, lines: readonly string[], emit?: (text: string) => void, requireContextClosure?: boolean): Promise<{
3
3
  records: readonly import("@lapxo/topos/wire").ObjectRecord[];
4
4
  context: import("@lapxo/topos/wire").ObjectContext;
5
+ configuration: Readonly<Record<string, string>>[];
5
6
  }>;
@@ -1,18 +1,38 @@
1
- import { canonical, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
1
+ import { canonical, parse, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
2
2
  import { objectProvider } from '@lapxo/topos/object-provider';
3
3
  import { authorityFor, rootSigner } from "../fold/signers.js";
4
4
  import { signaturesOf } from "../fold/digests.js";
5
5
  import { storeOf } from "../land/ledger.js";
6
6
  import { toposAt, contextModuleAt } from "./selected-topos.js";
7
+ import { foldClaims } from "../fold/claims.js";
8
+ import { usesWasmObject } from "../wasm/object.js";
7
9
  /** Bound supplies host authority and I/O; topos owns grammar, selection and context validation. */
8
- export async function admittedObjects(root, lines, emit = () => { }) {
10
+ export async function admittedObjects(root, lines, emit = () => { }, requireContextClosure = false) {
9
11
  const history = objectHistory(lines);
10
12
  if (history.kind !== 'fact')
11
13
  throw Error(`REFUSE·wire ${history.why}`);
12
14
  const authority = authorityFor(lines, rootSigner(root), signaturesOf(storeOf(root)).admitted);
13
- const context = await objectProvider(history.value.objects, history.value.configuration, { admit: fields => authority.of(canonical(fields)).kind === 'admitted', emit, topos: digest => toposAt(digest, storeOf(root)), module: digest => contextModuleAt(digest, storeOf(root)) });
15
+ const admittedConfiguration = history.value.configuration.filter(fields => authority.of(canonical(fields)).kind === 'admitted').map(fields => canonical(fields));
16
+ const configuration = foldClaims(admittedConfiguration).standing.flatMap(line => { const got = parse(line); return got.kind === 'fact' ? [got.value.fields] : []; });
17
+ const verifyClosure = requireContextClosure || usesWasmObject(configuration), declaredModules = new Map();
18
+ const context = await objectProvider(history.value.objects, history.value.configuration, { admit: fields => authority.of(canonical(fields)).kind === 'admitted', emit, topos: digest => {
19
+ const selected = toposAt(digest, storeOf(root));
20
+ if (verifyClosure)
21
+ for (const offer of selected.parts.filter(fields => fields.kind === 'check' && fields.value)) {
22
+ const dependencies = [...new Set((offer.restsOn ?? '').split('|').filter(Boolean))].sort(), prior = declaredModules.get(offer.value);
23
+ if (prior && JSON.stringify(prior) !== JSON.stringify(dependencies))
24
+ throw Error('REFUSE·context module has conflicting declared closures');
25
+ declaredModules.set(offer.value, dependencies);
26
+ }
27
+ return selected;
28
+ }, module: digest => {
29
+ const dependencies = declaredModules.get(digest);
30
+ if (verifyClosure && dependencies === undefined)
31
+ throw Error('REFUSE·context module has no declared closure');
32
+ return contextModuleAt(digest, storeOf(root), verifyClosure ? dependencies : undefined);
33
+ } });
14
34
  const checked = validateObjectContext(history.value.objects, context);
15
35
  if (checked.kind !== 'fact')
16
36
  throw Error(`REFUSE·context ${checked.why}`);
17
- return { records: checked.value, context };
37
+ return { records: checked.value, context, configuration };
18
38
  }
@@ -14,6 +14,8 @@ type File = {
14
14
  readonly text: string;
15
15
  };
16
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[];
17
19
  /** The selected world owns input projection and reading. The host only verifies closures and keeps native count/bytes receipts. */
18
20
  export declare function readingClosures(options: {
19
21
  root: string;
@@ -11,6 +11,11 @@ const receiptScope = (scope) => [scope, 'identity'].join('/');
11
11
  const fields = (line) => { const got = parse(line); if (got.kind !== 'fact')
12
12
  throw Error('REFUSE·input projection contains a non-wire line'); return got.value.fields; };
13
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
+ }
14
19
  /** The selected world owns input projection and reading. The host only verifies closures and keeps native count/bytes receipts. */
15
20
  export function readingClosures(options) {
16
21
  const { root, store, reader, speaker, digest: hash, capsule, region, projection, files } = options, emit = options.emit ?? (() => { });
@@ -135,7 +140,7 @@ export function readingClosures(options) {
135
140
  opened++;
136
141
  }
137
142
  const receipt = context ? readingReceiptIdentity(input, payload, bytes => hash(new TextDecoder().decode(bytes))) : payload;
138
- if (context)
143
+ if (context && plan.resolution !== 1)
139
144
  out.push(canonical({ scope: receiptScope(scope), role: 'writes', form: 'alphabet', measure: 'digest', value: receipt, by, at: 'place:' + input }));
140
145
  results.set(plan.scope, { digest: receipt, lines });
141
146
  out.push(canonical({ scope: plan.scope, role: 'writes', form: 'alphabet', measure: 'observed', value: input, by, at: 'place:' + input }),
@@ -147,18 +152,11 @@ export function readingClosures(options) {
147
152
  ...plan.inputs.map(coordinate => canonical({ scope: coordinate, role: 'writes', form: 'alphabet', measure: 'observed', value: hash(handed.get(coordinate)), by, at: 'witness:' + input })), ...lines);
148
153
  if (plan.resolution === 1) {
149
154
  read++;
150
- const native = shardLines(store, speaker, journal).filter(line => fieldOf(line, 'scope') === scope && ((fieldOf(line, 'measure') === 'bytes' && fieldOf(line, 'value') === input) || (fieldOf(line, 'measure') === 'count') || (fieldOf(line, 'measure') === 's' && fieldOf(line, 'at') === 'place:' + input))).map(line => {
151
- const f = fields(line), id = f.measure === 'bytes' ? f.value : f.measure === 'count' ? (f.at ?? '').replace(/^place:/, '') : undefined;
152
- if (id === undefined)
153
- return line;
154
- const bytes = load(id);
155
- if (bytes === undefined)
156
- throw Error('REFUSE·receipt missing verified carrier ' + id);
157
- // The full-resolution native row carries the actual verified UTF-8 bytes.
158
- // Its semantic count/digest is unchanged; a public copy needs no private CAS.
159
- return canonical({ ...f, about: bytes });
160
- });
161
- out.push(canonical({ scope, role: 'writes', form: 'alphabet', measure: 'observed', value: payload, by, at: 'place:' + payload }), ...native);
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));
162
160
  }
163
161
  });
164
162
  }
@@ -0,0 +1,28 @@
1
+ import { authorityOf, validateObjectContext } from '@lapxo/topos/wire';
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 objects: ObjectContext;
13
+ readonly maxBytes: number;
14
+ readonly maxRecords: number;
15
+ readonly digestBytes: (bytes: Uint8Array) => string | Promise<string>;
16
+ readonly verifyBytes: (bytes: string, key: string, signature: string, algorithms: readonly string[]) => boolean | Promise<boolean>;
17
+ readonly fetchBytes: (source: string, blob: string) => Promise<Uint8Array>;
18
+ }
19
+ /** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
20
+ export declare function inspectAuthenticatedReference(fragment: string, trust: ReferenceTrust): Promise<{
21
+ reference: import("./reference-profile.ts").StandingReference;
22
+ records: readonly import("@lapxo/topos/wire").ObjectRecord[];
23
+ semanticIdentity: string;
24
+ authorityContextIdentity: string;
25
+ semanticStanding: string;
26
+ originalRecords: readonly string[];
27
+ }>;
28
+ export { encodeReference, decodeReference } from './reference-profile.ts';
@@ -0,0 +1,84 @@
1
+ import { parse, canonical, authorityOf, authorityAtEpoch, signedBytes, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
2
+ import { cellInputs } from '@lapxo/topos/cell-inputs';
3
+ import { semanticStandingBytes } from '@lapxo/topos/standing';
4
+ import { decodeReference } from "./reference-profile.js";
5
+ function fail(why) { throw Error('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('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('resource budget');
14
+ const received = await trust.fetchBytes(reference.source, reference.blob);
15
+ if (received.byteLength > trust.maxBytes)
16
+ fail('bytes budget');
17
+ if (typeof SharedArrayBuffer !== 'undefined' && received.buffer instanceof SharedArrayBuffer)
18
+ fail('shared transport bytes');
19
+ const bytes = new Uint8Array(received);
20
+ if (await trust.digestBytes(bytes) !== reference.blob)
21
+ fail('blob integrity');
22
+ const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
23
+ let packet;
24
+ try {
25
+ packet = JSON.parse(text);
26
+ }
27
+ catch {
28
+ fail('carrier framing');
29
+ }
30
+ 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)
31
+ fail('carrier framing');
32
+ const originals = packet[1], proofLine = packet[2];
33
+ const parsed = [...originals, proofLine].map(line => { const p = parse(line); if (p.kind !== 'fact' || canonical(p.value.fields) !== line)
34
+ fail('original canonical'); return p.value.fields; });
35
+ const proof = parsed[parsed.length - 1];
36
+ if (proof.scope !== trust.proofScope || proof.at !== 'witness:' + trust.snapshot || proof.role !== 'writes' || proof.form !== 'alphabet' || proof.measure !== 'digest' || proof.epoch !== String(trust.epoch))
37
+ fail('untrusted snapshot binding');
38
+ if (proof.value !== await trust.digestBytes(utf8(JSON.stringify(['authenticated-standing@1', originals]) + '\n')))
39
+ fail('snapshot commitment');
40
+ // Async crypto port is called on exact native signed bytes; authorityOf consumes only those results.
41
+ const verifiedCrypto = new Set();
42
+ const key = (f, pub) => JSON.stringify([signedBytes(f), pub, f.sig, trust.algorithms]);
43
+ for (const f of parsed)
44
+ for (const s of trust.signers)
45
+ if (s.id === f.by && await trust.verifyBytes(signedBytes(f), s.publicKey, f.sig, trust.algorithms))
46
+ verifiedCrypto.add(key(f, s.publicKey));
47
+ const verifies = (f, pub) => verifiedCrypto.has(key(f, pub));
48
+ const authentic = authorityOf(proof, trust.signers, verifies);
49
+ if (authentic.kind !== 'admitted')
50
+ fail('proof ' + authentic.kind + ' ' + authentic.why);
51
+ const signer = trust.signers.find(s => s.id === proof.by);
52
+ if (!signer)
53
+ fail('proof signer');
54
+ const current = authorityAtEpoch(proof, signer, trust.epoch, trust.proofScope, 'authority');
55
+ if (current.kind !== 'admitted')
56
+ fail('current proof ' + current.kind + ' ' + current.why);
57
+ const verified = new Set();
58
+ for (let i = 0; i < originals.length; i++) {
59
+ const admitted = authorityOf(parsed[i], trust.signers, verifies);
60
+ if (admitted.kind !== 'admitted')
61
+ fail('original ' + admitted.kind + ' ' + admitted.why);
62
+ verified.add(originals[i]);
63
+ }
64
+ const history = objectHistory(originals);
65
+ if (history.kind !== 'fact')
66
+ fail('wire ' + history.why);
67
+ if (history.value.configuration.some(f => f.value === 'withdraw'))
68
+ fail('configuration history requires admitted standing fold');
69
+ const context = { ...trust.objects, admit: (r) => verified.has(canonical(r.fields)) && trust.objects.admit(r) };
70
+ const checked = validateObjectContext(history.value.objects, context);
71
+ if (checked.kind !== 'fact')
72
+ throw Error('REFUSE·' + checked.why);
73
+ const membership = new Set([...cellInputs(checked.value, 'semantic-live@2').values()].flat().map(l => semanticStandingBytes([l])));
74
+ const fields = (line) => { const p = parse(line); if (p.kind !== 'fact')
75
+ fail('original parse'); return p.value.fields; };
76
+ const effective = [...originals].sort((a, b) => Number(fields(a).epoch) - Number(fields(b).epoch));
77
+ const live = effective.filter(line => { const f = fields(line); return f.type === undefined || membership.has(semanticStandingBytes([line])); });
78
+ const semantic = semanticStandingBytes(live), semanticIdentity = await trust.digestBytes(utf8(semantic));
79
+ if (semanticIdentity !== 'sha256:' + reference.digest)
80
+ fail('standing identity');
81
+ const authorityContextIdentity = await trust.digestBytes(utf8(JSON.stringify(['reference-inspection-trust@1', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms])));
82
+ return { reference, records: checked.value, semanticIdentity, authorityContextIdentity, semanticStanding: semantic, originalRecords: originals };
83
+ }
84
+ export { encodeReference, decodeReference } from "./reference-profile.js";
@@ -0,0 +1,10 @@
1
+ export declare function authenticatedReferenceSelected(standing: readonly string[]): boolean;
2
+ export declare function openAuthenticatedReference(root: string, fragment: string): Promise<{
3
+ readings: readonly import("../engine/object.ts").ObjectReading[];
4
+ reference: import("./reference-profile.ts").StandingReference;
5
+ records: readonly import("@lapxo/topos/wire").ObjectRecord[];
6
+ semanticIdentity: string;
7
+ authorityContextIdentity: string;
8
+ semanticStanding: string;
9
+ originalRecords: readonly string[];
10
+ }>;
@@ -0,0 +1,50 @@
1
+ /** Native ports for authenticated #bound opening; trusted policy belongs to the selected local place. */
2
+ import { sha256 as hash, isSha256 } from "./hash.js";
3
+ import { canonical, parse, transportSelection } from '@lapxo/topos/wire';
4
+ import { semanticStandingBytes } from '@lapxo/topos/standing';
5
+ import { authorityFor, rootSigner } from "../fold/signers.js";
6
+ import { eraOf } from "../fold/digests.js";
7
+ import { lockStanding, lockLines } from "../fold/keys.js";
8
+ import { signedOwnerLines } from "../cli/owner.js";
9
+ import { admittedObjects } from "./objects.js";
10
+ import { storeOf } from "../land/ledger.js";
11
+ import { objectFold } from "../engine/index.js";
12
+ import { toposAt, contentAt } from "./selected-topos.js";
13
+ import { transportBytes } from "./ports/transport.js";
14
+ import { decodeReference } from "./reference-profile.js";
15
+ import { inspectAuthenticatedReference } from "./reference-authenticated.js";
16
+ import { verifyBytes } from "./adapters/keys/ed25519.js";
17
+ export function authenticatedReferenceSelected(standing) {
18
+ const selected = standing.flatMap(line => { const p = parse(line); return p.kind === 'fact' && p.value.fields.scope === 'reference/authentication' ? [p.value.fields] : []; });
19
+ if (!selected.length)
20
+ return false;
21
+ if (selected.length !== 1 || selected[0].value !== 'authenticated-standing@1')
22
+ throw Error('REFUSE·reference conflicting or unknown authentication profile');
23
+ return true;
24
+ }
25
+ export async function openAuthenticatedReference(root, fragment) {
26
+ const store = storeOf(root), standing = lockStanding(store), era = eraOf(standing), authority = authorityFor(lockLines(store), rootSigner(root), era.admitted);
27
+ if (!authenticatedReferenceSelected(standing))
28
+ throw Error('REFUSE·reference no admitted authenticated-standing@1 profile');
29
+ const one = (scope) => { const found = standing.flatMap(line => { const p = parse(line); return p.kind === 'fact' && p.value.fields.scope === scope ? [{ line, fields: p.value.fields }] : []; }); if (found.length !== 1 || found[0].fields.role !== 'writes' || found[0].fields.measure !== 'id' || authority.of(found[0].line).kind !== 'admitted')
30
+ throw Error('REFUSE·reference missing, conflicting or unauthenticated ' + scope); return found[0].fields.value; };
31
+ if (one('reference/authentication') !== 'authenticated-standing@1')
32
+ throw Error('REFUSE·reference authentication profile');
33
+ const snapshot = one('reference/snapshot'), epochText = one('reference/epoch'), proofScope = one('reference/proof-scope'), recordsText = one('reference/records');
34
+ if (!isSha256(snapshot) || !/^\d+$/.test(epochText) || !Number.isSafeInteger(Number(epochText)) || !/^\d+$/.test(recordsText) || !Number.isSafeInteger(Number(recordsText)) || Number(recordsText) < 1 || !proofScope)
35
+ throw Error('REFUSE·reference authority snapshot or resource contract');
36
+ const reference = decodeReference(fragment);
37
+ // Provider selection is made from HOST-declared cells/configuration, never carrier-picked code.
38
+ const held = await admittedObjects(root, signedOwnerLines(store), () => { }, true);
39
+ if (!held.records.some(r => r.record === 'cell'))
40
+ throw Error('REFUSE·reference host has no declared object interpretation');
41
+ const context = held.context, policy = held.configuration.map(fields => canonical(fields)), selection = transportSelection(held.configuration, new URL(reference.source).protocol.slice(0, -1));
42
+ if (!selection)
43
+ throw Error('REFUSE·reference no admitted transport policy');
44
+ const implementations = [...new Set(held.records.filter(r => r.record === 'cell').map(r => r.fields.topos))].sort().map(pin => { const selected = toposAt(pin, store), selectors = selected.parts.filter(f => f.scope === 'wire/topos/context-view'); if (selectors.length !== 1)
45
+ throw Error('REFUSE·reference host context selector'); const offers = selected.parts.filter(f => f.kind === 'check' && f.view === selectors[0].value); if (offers.length !== 1)
46
+ throw Error('REFUSE·reference host context offer'); const offer = offers[0], closure = [...new Set((offer.restsOn ?? '').split('|').filter(Boolean))].sort(); for (const digest of closure)
47
+ contentAt(digest, store); return [pin, offer.value, closure]; });
48
+ const result = await inspectAuthenticatedReference(fragment, { snapshot, epoch: Number(epochText), proofScope, policy: hash(semanticStandingBytes(policy)), formImplementation: hash(JSON.stringify(implementations)), signers: authority.admitted, algorithms: era.admitted, objects: context, maxBytes: selection.responseBytes, maxRecords: Number(recordsText), digestBytes: hash, verifyBytes, fetchBytes: (source, blob) => transportBytes(source, blob, policy) });
49
+ return { ...result, readings: objectFold(result.records, context) };
50
+ }
@@ -0,0 +1,23 @@
1
+ /** reference-profile@1: platform URL parsing, no authority or transport effects. */
2
+ export interface StandingReference {
3
+ readonly class: 'standing';
4
+ readonly algorithm: 'sha256';
5
+ readonly digest: string;
6
+ readonly blob: string;
7
+ readonly source: string;
8
+ }
9
+ interface InspectionPort {
10
+ readonly maxBytes: number;
11
+ readonly readSemanticStanding: (bytes: string) => readonly string[];
12
+ readonly semanticStandingBytes: (lines: readonly string[]) => string;
13
+ readonly digestBytes: (bytes: Uint8Array) => string;
14
+ }
15
+ export declare function encodeReference(reference: StandingReference): string;
16
+ export declare function decodeReference(fragment: string): StandingReference;
17
+ export declare function inspectReferenceBytes(reference: StandingReference, bytes: Uint8Array, { maxBytes, readSemanticStanding, semanticStandingBytes, digestBytes }: InspectionPort): {
18
+ reference: StandingReference;
19
+ lines: readonly string[];
20
+ standing: string;
21
+ authentication: string;
22
+ };
23
+ export {};
@@ -0,0 +1,62 @@
1
+ const hex = /^[0-9a-f]{64}$/;
2
+ const refuse = (cause) => { throw Error(`REFUSE·reference ${cause}`); };
3
+ const keys = ['class', 'algorithm', 'digest', 'blob', 'source'];
4
+ function check(reference) {
5
+ if (Object.keys(reference).length !== keys.length || keys.some(key => !Object.prototype.hasOwnProperty.call(reference, key)))
6
+ refuse('fields');
7
+ if (reference.class !== 'standing')
8
+ refuse('unknown class');
9
+ if (reference.algorithm !== 'sha256')
10
+ refuse('unknown algorithm');
11
+ if (!hex.test(reference.digest) || !/^sha256:[0-9a-f]{64}$/.test(reference.blob))
12
+ refuse('digest');
13
+ let source;
14
+ try {
15
+ source = new URL(reference.source);
16
+ }
17
+ catch {
18
+ return refuse('source URL');
19
+ }
20
+ if (source.username || source.password || source.hash)
21
+ refuse('source credentials or fragment');
22
+ if (source.href !== reference.source)
23
+ refuse('noncanonical source URL');
24
+ return reference;
25
+ }
26
+ export function encodeReference(reference) {
27
+ check(reference);
28
+ return '#bound:1?' + keys.map(key => key + '=' + encodeURIComponent(reference[key])).join('&');
29
+ }
30
+ export function decodeReference(fragment) {
31
+ if (!fragment.startsWith('#bound:1?'))
32
+ refuse('profile');
33
+ const parameters = new URLSearchParams(fragment.slice('#bound:1?'.length));
34
+ if ([...parameters].length !== keys.length || new Set(parameters.keys()).size !== keys.length)
35
+ refuse('duplicate or missing field');
36
+ const reference = check(Object.fromEntries(parameters));
37
+ if (encodeReference(reference) !== fragment)
38
+ refuse('noncanonical encoding');
39
+ return Object.freeze(reference);
40
+ }
41
+ export function inspectReferenceBytes(reference, bytes, { maxBytes, readSemanticStanding, semanticStandingBytes, digestBytes }) {
42
+ if (bytes.byteLength > maxBytes)
43
+ refuse('bytes budget');
44
+ if (digestBytes(bytes) !== reference.blob.slice(7))
45
+ refuse('blob integrity');
46
+ let document;
47
+ try {
48
+ document = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
49
+ }
50
+ catch {
51
+ refuse('document');
52
+ }
53
+ if (document === null || typeof document !== 'object' || document.profile !== 'semantic-standing@1' || typeof document.standing !== 'string' || Object.keys(document).length !== 2)
54
+ refuse('document profile');
55
+ if (new TextDecoder('utf-8', { fatal: true }).decode(bytes) !== JSON.stringify({ profile: document.profile, standing: document.standing }))
56
+ refuse('noncanonical document wrapper');
57
+ const lines = readSemanticStanding(document.standing);
58
+ const semantic = semanticStandingBytes(lines);
59
+ if (digestBytes(new TextEncoder().encode(semantic)) !== reference.digest)
60
+ refuse('standing identity');
61
+ return { reference, lines, standing: semantic, authentication: 'not demonstrated; inspect only' };
62
+ }
@@ -0,0 +1,6 @@
1
+ export declare function inspectReference(fragment: string, policy: readonly string[]): Promise<{
2
+ reference: import("./reference-profile.ts").StandingReference;
3
+ lines: readonly string[];
4
+ standing: string;
5
+ authentication: string;
6
+ }>;
@@ -0,0 +1,24 @@
1
+ /** Host composition. Location supplies bytes, never identity or authority. No store writes. */
2
+ import { createHash } from 'node:crypto';
3
+ import { readSemanticStanding, semanticStandingBytes } from '@lapxo/topos/core';
4
+ import { parse, transportSelection } from '@lapxo/topos/wire';
5
+ import { transportBytes } from "./ports/transport.js";
6
+ import { decodeReference, inspectReferenceBytes } from "./reference-profile.js";
7
+ export async function inspectReference(fragment, policy) {
8
+ const reference = decodeReference(fragment), scheme = new URL(reference.source).protocol.slice(0, -1);
9
+ const fields = policy.flatMap(line => { const got = parse(line); return got.kind === 'fact' ? [got.value.fields] : []; });
10
+ const selected = transportSelection(fields, scheme);
11
+ if (selected === undefined)
12
+ throw Error(`REFUSE·transport ${scheme} no admitted content-request/1 policy`);
13
+ let bytes;
14
+ try {
15
+ bytes = await transportBytes(reference.source, reference.blob, policy);
16
+ }
17
+ catch (error) {
18
+ const cause = error instanceof Error ? error.message : 'transport failed';
19
+ if (cause.startsWith('REFUSE·'))
20
+ throw error;
21
+ throw Error(`REFUSE·transport ${scheme} ${cause}`);
22
+ }
23
+ return inspectReferenceBytes(reference, bytes, { maxBytes: selected.responseBytes, readSemanticStanding, semanticStandingBytes, digestBytes: value => createHash('sha256').update(value).digest('hex') });
24
+ }
@@ -12,7 +12,7 @@ export declare function admittedLine(line: string, local: readonly Readonly<Reco
12
12
  export declare function toposAt(digest: string, store?: string): StandingTopos;
13
13
  export declare function contentAt(digest: string, store?: string): Uint8Array;
14
14
  /** The declared module runs from the store that supplied its verified bytes. No copy or live-checkout fallback. */
15
- export declare function contextModuleAt(digest: string, store?: string): string;
15
+ export declare function contextModuleAt(digest: string, store?: string, dependencies?: readonly string[]): string;
16
16
  /** Selection verifies standing identity. Requested capabilities verify their artifact bytes separately. */
17
17
  export declare function selectedTopoi(pins: readonly string[], resolve?: ToposResolver): readonly SelectedTopos[];
18
18
  /** Read a declared wire list without copying a world's claims or executing its offers.
@@ -2,6 +2,8 @@ import { verifiedContent } from "./content-store.js";
2
2
  import { readStanding } from '@lapxo/topos/standing';
3
3
  import { parse, wireAt, fromLine } from '@lapxo/topos/wire';
4
4
  import { fieldOf } from "../fold/claims.js";
5
+ import { moduleClosure } from "./native/module-closure.js";
6
+ import { sha256, isSha256 } from "./hash.js";
5
7
  /** Local grammar is sufficient for locally declared records. Resolve selected
6
8
  * contracts only when a reader is missing; a local refusal remains a refusal. */
7
9
  export function admittedLine(line, local, epoch, pins, resolve = toposAt) {
@@ -26,7 +28,13 @@ export function contentAt(digest, store) {
26
28
  return verifiedContent(digest, store).bytes;
27
29
  }
28
30
  /** The declared module runs from the store that supplied its verified bytes. No copy or live-checkout fallback. */
29
- export function contextModuleAt(digest, store) {
31
+ export function contextModuleAt(digest, store, dependencies) {
32
+ if (dependencies !== undefined) {
33
+ if (!isSha256(digest) || !dependencies.includes(digest) || dependencies.some(pin => !isSha256(pin)))
34
+ throw Error('REFUSE·context module closure requires declared SHA-256 members');
35
+ const pins = [...new Set(dependencies)].sort(), members = new Map(pins.map(pin => [pin.slice(pin.indexOf(':') + 1), { digest: pin, source: new TextDecoder('utf-8', { fatal: true }).decode(contentAt(pin, store)) }]));
36
+ moduleClosure({ identity: sha256(JSON.stringify(pins)), entry: digest.slice(digest.indexOf(':') + 1), members }, () => undefined);
37
+ }
30
38
  return verifiedContent(digest, store).path;
31
39
  }
32
40
  /** Selection verifies standing identity. Requested capabilities verify their artifact bytes separately. */
@@ -27,3 +27,7 @@ type Ask = (declaration: Declaration, one: Asking, verb: 'render' | 'read', shap
27
27
  */
28
28
  export declare const askingOf: (fold: Placed) => Ask;
29
29
  export declare function capsuleReadingsOver(fold: Placed): readonly string[];
30
+ /** Current judgement excludes authenticated outcomes of the selected producer batches; retained evidence stays in its carrier. */
31
+ export declare const currentReceiptObservations: (fold: Parameters<typeof capsulesOf>[0]) => readonly string[];
32
+ /** Preserve prior producer outcomes in the owning place's public carrier, independently of current judgement. */
33
+ export declare const retiredReceiptHistory: (fold: Parameters<typeof capsulesOf>[0]) => readonly string[];