@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,3 +1,11 @@
1
+ import { childFlags } from "../host/capsule.js";
2
+ import { readingProfile, readingExecution, withReadingBoundary } from "../host/reading-context.js";
3
+ import { readingIdentityBytes } from '@lapxo/topos/contract';
4
+ import { portableReadingReceipt, verifiedReadingReceipts } from "../host/reading-closures.js";
5
+ import { bytesDigest, instrumentCoordinates } from "../fold/digests.js";
6
+ import { ownRoot, processOf } from "./runner.js";
7
+ import { contentPolicy } from "../host/content-store.js";
8
+ import { carriedFrom } from "../fold/resolved.js";
1
9
  import { selectedCapsules } from "../host/selected-capsules.js";
2
10
  import { selectionLines, projectionLines } from "../fold/reach.js";
3
11
  export { selectionLines } from "../fold/reach.js";
@@ -22,6 +30,7 @@ import { ownLock } from "./runner.js";
22
30
  import { nameOf } from "../render/page.js";
23
31
  import { placeLines, takenFor } from "../fold/place-inputs.js";
24
32
  import { isRegionDeclaration } from "../fold/region-declarations.js";
33
+ const receiptHasher = (store) => (text) => bytesDigest(store, new TextEncoder().encode(text));
25
34
  const fact = (scope, value, form = 'alphabet', measure = 'fact') => canonical({ scope, role: 'writes', form, measure, value, by: 'fold', at: 'place:fold' });
26
35
  const described = (local = []) => {
27
36
  const lock = ownLock();
@@ -164,12 +173,30 @@ function capsuleReadings(fold) {
164
173
  return known.get(place) ?? known.set(place, readingsAt(fold, place)).get(place);
165
174
  }
166
175
  function readingsAt(fold, place) {
167
- const asking = askingOf(fold);
168
176
  const speaker = keyFor(fold.store, 'read');
169
177
  return capsulesOf(fold).flatMap((capsule) => {
170
178
  const writing = new Set(capsule.lines.filter((line) => fieldOf(line, 'scope').startsWith('region/') && fieldOf(line, 'measure') === 'writes').map((line) => fieldOf(line, 'scope').slice('region/'.length)));
171
179
  const receipts = Object.keys(capsule.declaration.writes).filter((name) => capsule.declaration.writes[name] === 'receipt' && !writing.has(name)).sort();
180
+ const target = modernReceiptInputs(fold, capsule.selection ?? capsule.digest);
181
+ const asking = askingOf({ ...fold, observed: target.current });
172
182
  const requests = receipts.flatMap((name) => ((request) => (request === undefined ? [] : [request]))(asking(capsule.declaration, { name, at: 3 }, 'read', '')));
183
+ const modern = readingProfile(fold.standing, fieldOf);
184
+ if (modern && requests.length) {
185
+ const hash = receiptHasher(fold.store);
186
+ const batch = hash(JSON.stringify([capsule.selection ?? capsule.digest, capsule.digest, receipts]));
187
+ const coordinates = instrumentCoordinates(fold.store, processOf('verb'), [], ownRoot(), ownLock(), true), instrument = hash([...coordinates.values()].sort().join('\n'));
188
+ const legacy = target.legacy;
189
+ return requests.flatMap(request => {
190
+ const execution = readingExecution({ instrument, implementation: capsule.digest, policy: hash(JSON.stringify([contentPolicy(), capsule.selection ?? null, capsule.lines])), inputs: [['world', capsule.selection ?? capsule.digest], ['batch', batch], ['request', hash(JSON.stringify(request))], ...legacy, ...[...coordinates].map(([name, id]) => ['host/' + relative(ownRoot(), name), id])] }, hash, { env: process.env, cwd: fold.root, flags: childFlags() });
191
+ const closure = new TextDecoder().decode(readingIdentityBytes(execution.context)), input = hash(closure), said = withReadingBoundary(input, execution, () => capsule.ask([request]))[0];
192
+ if (said?.kind !== 'fact')
193
+ throw Error(`REFUSE·capsule ${capsule.digest} · ${request.region} · ${said?.why ?? 'no answer'}`);
194
+ const by = speaker + ':' + batch.slice(7), at = 'place:' + input;
195
+ const lines = (said.claims ?? []).map((row) => canonical({ scope: row.scope, role: row.role, measure: row.measure, form: row.bound.kind === 'interval' ? 'interval' : 'alphabet', value: row.bound.kind === 'interval' ? `${row.bound.lo ?? 0}..${row.bound.hi ?? 0}` : (row.bound.values ?? []).join('|'), by, at }));
196
+ const output = lines.join('\n') + (lines.length ? '\n' : ''), scope = 'receipts/readings/' + hash(request.region ?? '').slice(7) + '/' + input.slice(7);
197
+ return [canonical({ scope: place, role: 'writes', form: 'alphabet', measure: 'observed', value: input.slice(7, 23), by, at }), ...lines, ...portableReadingReceipt(scope, by, closure, output, hash)];
198
+ });
199
+ }
173
200
  const answers = capsule.ask(requests);
174
201
  const refused = requests.filter((_, i) => answers[i]?.kind !== 'fact');
175
202
  if (refused.length)
@@ -196,3 +223,84 @@ export function capsuleReadingsOver(fold) {
196
223
  return [...capsuleReadings(fold), ...places.flatMap((place) => capsuleReadings({ ...fold, under: `${place}/`,
197
224
  standing: [...new Set([...said.filter((one) => one.place === place).map((one) => one.line), ...fold.standing])] }))];
198
225
  }
226
+ /** Current judgement excludes authenticated outcomes of the selected producer batches; retained evidence stays in its carrier. */
227
+ export const currentReceiptObservations = (fold) => modernReceiptInputs(fold).current;
228
+ /** Preserve prior producer outcomes in the owning place's public carrier, independently of current judgement. */
229
+ export const retiredReceiptHistory = (fold) => { const carried = carriedFrom(fold.store, (observeText(join(fold.root, fold.under ?? '', 'receipts.bound')) ?? '').split('\n').filter(line => line.startsWith('bound-lock/'))) ?? []; const all = [...new Set([...fold.observed, ...carried])], current = new Set(modernReceiptInputs({ ...fold, observed: all }).current); return all.filter(line => !current.has(line)); };
230
+ /** A modern capsule batch cannot consume its own retained outcomes. Full context units bind history; legacy conversion never supplies a current answer. */
231
+ function modernReceiptInputs(fold, target) {
232
+ if (!readingProfile(fold.standing, fieldOf))
233
+ return { current: fold.observed, legacy: [] };
234
+ const observed = [...new Set(fold.observed)];
235
+ const hash = receiptHasher(fold.store), allSelected = capsulesOf(fold), selected = allSelected.filter(c => { const writing = new Set(c.lines.filter(l => fieldOf(l, 'measure') === 'writes').map(l => fieldOf(l, 'scope').slice(7))); return Object.keys(c.declaration.writes).some(n => c.declaration.writes[n] === 'receipt' && !writing.has(n)); });
236
+ if (!selected.length)
237
+ return { current: fold.observed, legacy: [] };
238
+ const speaker = keyFor(fold.store, 'read'), removed = new Set(), refs = [];
239
+ const units = observed.filter(l => fieldOf(l, 'scope').startsWith('receipts/readings/') && fieldOf(l, 'measure') === 'bytes' && fieldOf(l, 'role') === 'writes');
240
+ if (verifiedReadingReceipts(fold.store, observed, hash, undefined, true) === undefined)
241
+ throw Error('REFUSE·capsule receipt carrier context invalid');
242
+ for (const capsule of selected) {
243
+ if (target !== undefined && (capsule.selection ?? capsule.digest) !== target)
244
+ continue;
245
+ const writing = new Set(capsule.lines.filter(l => fieldOf(l, 'measure') === 'writes').map(l => fieldOf(l, 'scope').slice(7))), receipts = Object.keys(capsule.declaration.writes).filter(n => capsule.declaration.writes[n] === 'receipt' && !writing.has(n)).sort();
246
+ if (!receipts.length)
247
+ continue;
248
+ const batch = hash(JSON.stringify([capsule.selection ?? capsule.digest, capsule.digest, receipts])), knownLegacy = new Set();
249
+ for (const unit of units) {
250
+ const about = fieldOf(unit, 'about');
251
+ if (!about)
252
+ continue;
253
+ let c;
254
+ try {
255
+ c = JSON.parse(about);
256
+ }
257
+ catch {
258
+ continue;
259
+ }
260
+ if (!Array.isArray(c) || c[0] !== 'reading-context@1' || c[2] !== capsule.digest || !Array.isArray(c[4]) || !c[4].some(([n, id]) => n === 'batch' && id === batch) || !c[4].some(([n, id]) => n === 'world' && id === (capsule.selection ?? capsule.digest)))
261
+ continue;
262
+ const expectedBy = speaker + ':' + batch.slice(7);
263
+ if (fieldOf(unit, 'by') !== expectedBy)
264
+ continue;
265
+ if (fold.standing.some(line => fieldOf(line, 'scope') === 'keys/' + expectedBy && fieldOf(line, 'value') !== 'withdraw'))
266
+ throw Error('REFUSE·modern capsule receipt producer ambiguous');
267
+ const namedScope = fieldOf(unit, 'scope'), inputIdentity = fieldOf(unit, 'value');
268
+ if (!receipts.some(name => namedScope === 'receipts/readings/' + hash(name).slice(7) + '/' + inputIdentity.slice(7)))
269
+ throw Error('REFUSE·modern capsule receipt scope differs from declared region');
270
+ for (const [n, id] of c[4])
271
+ if (typeof n === 'string' && n.startsWith('legacy/output/') && n === 'legacy/output/' + id) {
272
+ knownLegacy.add(id);
273
+ refs.push([n, id]);
274
+ }
275
+ const by = fieldOf(unit, 'by'), input = fieldOf(unit, 'value'), scope = fieldOf(unit, 'scope'), output = observed.find(l => fieldOf(l, 'scope') === scope && fieldOf(l, 'by') === by && fieldOf(l, 'measure') === 'count');
276
+ const payload = output ? fieldOf(output, 'at') : '';
277
+ for (const l of observed)
278
+ if (fieldOf(l, 'by') === by && (fieldOf(l, 'at') === 'place:' + input || fieldOf(l, 'at') === payload || fieldOf(l, 'scope') === scope || fieldOf(l, 'scope') === [scope, 'identity'].join('/')))
279
+ removed.add(l);
280
+ }
281
+ const alias = capsule.digest.slice(7, 19), by = speaker + ':' + alias, groups = new Map();
282
+ for (const l of observed)
283
+ if (fieldOf(l, 'by') === by) {
284
+ const at = fieldOf(l, 'at');
285
+ groups.set(at, [...(groups.get(at) ?? []), l]);
286
+ }
287
+ if (groups.size) {
288
+ if (selected.filter(c => c.digest === capsule.digest).length !== 1 || receipts.length !== 1 || new Set(allSelected.filter(c => c.digest.slice(7, 19) === alias).map(c => c.digest)).size !== 1 || fold.standing.some(l => fieldOf(l, 'scope') === 'keys/' + by && fieldOf(l, 'value') !== 'withdraw'))
289
+ throw Error('REFUSE·legacy capsule receipt producer ambiguous');
290
+ for (const [at, group] of groups) {
291
+ if (!/^place:sha256:[a-f0-9]{64}$/.test(at) || group.filter(l => fieldOf(l, 'measure') === 'observed' && fieldOf(l, 'scope') === (fold.under || './')).length !== 1)
292
+ throw Error('REFUSE·legacy capsule receipt occurrence ambiguous');
293
+ const id = hash(group.join('\n'));
294
+ if (!knownLegacy.has(id)) {
295
+ const candidate = observed.filter(l => !removed.has(l) && !group.includes(l)), request = askingOf({ ...fold, observed: candidate })(capsule.declaration, { name: receipts[0], at: 3 }, 'read', '');
296
+ if (!request || at !== 'place:' + hash(JSON.stringify(request)))
297
+ throw Error('REFUSE·legacy capsule receipt context unavailable · expected ' + at + ' · current ' + (request ? hash(JSON.stringify(request)) : 'absent'));
298
+ }
299
+ refs.push(['legacy/output/' + id, id]);
300
+ for (const l of group)
301
+ removed.add(l);
302
+ }
303
+ }
304
+ }
305
+ return { current: observed.filter(l => !removed.has(l)), legacy: [...new Map(refs.map(([n, id]) => [n + ' ' + id, [n, id]])).values()] };
306
+ }
@@ -0,0 +1,51 @@
1
+ export interface WasmLimits {
2
+ readonly memoryBytes: number;
3
+ readonly stackBytes: number;
4
+ readonly executionMs: number;
5
+ }
6
+ export interface WasmTrust {
7
+ readonly runtime: string;
8
+ readonly wasm: string;
9
+ readonly modules: Readonly<Record<string, string>>;
10
+ readonly entry: string;
11
+ readonly aliases: Readonly<Record<string, string>>;
12
+ readonly limits: WasmLimits;
13
+ readonly byteLengths: {
14
+ readonly runtime: number;
15
+ readonly wasm: number;
16
+ readonly modules: Readonly<Record<string, number>>;
17
+ };
18
+ }
19
+ export interface WasmRequest {
20
+ readonly trusted: WasmTrust;
21
+ readonly assets: {
22
+ readonly runtime: Uint8Array | ArrayBuffer;
23
+ readonly wasm: Uint8Array | ArrayBuffer;
24
+ };
25
+ readonly modules: Readonly<Record<string, string>>;
26
+ readonly entry: string;
27
+ readonly aliases?: Readonly<Record<string, string>>;
28
+ readonly limits: WasmLimits;
29
+ }
30
+ export interface WasmResult {
31
+ readonly output: string;
32
+ readonly initializedMs: number;
33
+ readonly executionMs: number;
34
+ }
35
+ export interface WorkerPort {
36
+ postMessage(message: WasmRequest): void;
37
+ terminate(): unknown;
38
+ addEventListener?(type: string, listener: (event: {
39
+ data?: unknown;
40
+ message?: string;
41
+ }) => void): void;
42
+ on?(type: string, listener: (value: unknown) => void): void;
43
+ }
44
+ export declare function digest(bytes: Uint8Array): Promise<string>;
45
+ export declare function preflight(request: WasmRequest): void;
46
+ export declare function runWasmRequest(request: WasmRequest): Promise<WasmResult>;
47
+ export declare function evaluateWasm(request: WasmRequest, { workerFactory, signal, lifetimeMs }: {
48
+ readonly workerFactory?: () => WorkerPort;
49
+ readonly signal?: AbortSignal;
50
+ readonly lifetimeMs: number;
51
+ }): Promise<WasmResult>;
@@ -0,0 +1,180 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ const owns = (object, key) => Object.prototype.hasOwnProperty.call(object, key);
10
+ const assetNames = ['runtime', 'wasm'], limitNames = ['memoryBytes', 'stackBytes', 'executionMs'];
11
+ // Host adapter only. Source integrity grants neither admission nor authority.
12
+ function refuse(cause) { throw Error('REFUSE·wasm ' + cause); }
13
+ export async function digest(bytes) { return 'sha256:' + Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes.slice().buffer))).map(x => x.toString(16).padStart(2, '0')).join(''); }
14
+ async function verified(bytes, expected) { if (typeof SharedArrayBuffer !== 'undefined' && (bytes instanceof SharedArrayBuffer || (ArrayBuffer.isView(bytes) && bytes.buffer instanceof SharedArrayBuffer)))
15
+ refuse('shared assets unsupported'); const copy = (bytes instanceof Uint8Array ? new Uint8Array(bytes) : new Uint8Array(bytes)).slice(); if (!/^sha256:[a-f0-9]{64}$/.test(expected) || await digest(copy) !== expected)
16
+ refuse('integrity'); return copy; }
17
+ const text = new TextEncoder();
18
+ // Exact host-declared sizes bound copies, hashing and worker handoff before work.
19
+ export function preflight(request) {
20
+ if (!request || Object.keys(request).some(k => !['trusted', 'assets', 'modules', 'aliases', 'entry', 'limits'].includes(k)))
21
+ refuse('request fields');
22
+ const { trusted, assets, modules, entry, aliases = {}, limits } = request, sizes = trusted?.byteLengths;
23
+ if (!sizes || !modules || !assets)
24
+ refuse('declared byte sizes');
25
+ if (Object.keys(assets).length !== 2 || Object.keys(assets).some(k => !['runtime', 'wasm'].includes(k)))
26
+ refuse('asset fields');
27
+ if (!limits || Object.keys(limits).length !== 3 || Object.keys(limits).some(k => !['memoryBytes', 'stackBytes', 'executionMs'].includes(k)))
28
+ refuse('limit fields');
29
+ for (const name of limitNames)
30
+ if (!Number.isSafeInteger(limits[name]) || limits[name] <= 0 || limits[name] !== trusted.limits?.[name])
31
+ refuse('limit ' + name);
32
+ if (typeof entry !== 'string' || entry !== trusted.entry || !aliases || Object.values(aliases).some(v => typeof v !== 'string') || JSON.stringify(Object.entries(aliases).sort()) !== JSON.stringify(Object.entries(trusted.aliases ?? {}).sort()))
33
+ refuse('entry or aliases differ from trusted context');
34
+ const names = Object.keys(modules), expected = Object.keys(trusted.modules ?? {});
35
+ if (names.length !== expected.length || names.some(n => !owns(trusted.modules, n)))
36
+ refuse('module count');
37
+ const size = (n, want) => { if (!Number.isSafeInteger(want) || want < 0)
38
+ refuse('declared size ' + n); return want; };
39
+ for (const name of assetNames) {
40
+ const v = assets[name];
41
+ if (typeof SharedArrayBuffer !== 'undefined' && (v instanceof SharedArrayBuffer || (ArrayBuffer.isView(v) && v.buffer instanceof SharedArrayBuffer)))
42
+ refuse('shared assets unsupported');
43
+ if (!(v instanceof Uint8Array || v instanceof ArrayBuffer) || v.byteLength !== size(name, sizes[name]))
44
+ refuse('asset bytes ' + name);
45
+ }
46
+ for (const name of names) {
47
+ const source = modules[name], want = size(name, sizes.modules?.[name]);
48
+ if (typeof source !== 'string' || source.length > want)
49
+ refuse('module bytes ' + name);
50
+ let bytes = 0;
51
+ for (let i = 0; i < source.length; i++) {
52
+ const c = source.charCodeAt(i);
53
+ bytes += c < 128 ? 1 : c < 2048 ? 2 : c >= 55296 && c <= 56319 && i + 1 < source.length && source.charCodeAt(i + 1) >= 56320 && source.charCodeAt(i + 1) <= 57343 ? (i++, 4) : 3;
54
+ if (bytes > want)
55
+ refuse('module bytes ' + name);
56
+ }
57
+ if (bytes !== want)
58
+ refuse('module bytes ' + name);
59
+ }
60
+ }
61
+ export async function runWasmRequest(request) {
62
+ preflight(request);
63
+ try {
64
+ request = structuredClone(request);
65
+ }
66
+ catch {
67
+ refuse('request is not cloneable');
68
+ }
69
+ const { trusted, assets, modules, aliases = {}, entry, limits } = request;
70
+ for (const name of limitNames)
71
+ if (!Number.isSafeInteger(limits?.[name]) || limits[name] <= 0 || limits[name] !== trusted?.limits?.[name])
72
+ refuse('limit ' + name);
73
+ if (entry !== trusted.entry || JSON.stringify(Object.entries(aliases).sort()) !== JSON.stringify(Object.entries(trusted.aliases ?? {}).sort()))
74
+ refuse('entry or aliases differ from trusted context');
75
+ const runtime = await verified(assets.runtime, trusted.runtime), wasm = await verified(assets.wasm, trusted.wasm), sources = {};
76
+ for (const [id, source] of Object.entries(modules)) {
77
+ if (!owns(trusted.modules, id))
78
+ refuse('undeclared module ' + id);
79
+ sources[id] = new TextDecoder('utf-8', { fatal: true }).decode(await verified(text.encode(source), trusted.modules[id]));
80
+ }
81
+ if (Object.keys(trusted.modules).some(id => !owns(sources, id)))
82
+ refuse('missing module');
83
+ if (!owns(sources, entry))
84
+ refuse('entry');
85
+ // The runtime is a closed esbuild ESM bundle: no imports and no second fetch.
86
+ const url = 'data:text/javascript;base64,' + bytesBase64(runtime), factory = await import(__rewriteRelativeImportExtension(url));
87
+ const started = performance.now(), quick = await factory.createQuickJS(wasm);
88
+ const rt = quick.newRuntime();
89
+ let context;
90
+ try {
91
+ context = rt.newContext();
92
+ const memory = rt.computeMemoryUsage();
93
+ let initializedMemory;
94
+ try {
95
+ initializedMemory = context.dump(memory);
96
+ }
97
+ finally {
98
+ memory.dispose();
99
+ }
100
+ const used = typeof initializedMemory === 'object' && initializedMemory !== null ? initializedMemory.memory_used_size : undefined;
101
+ if (typeof used !== 'number' || !Number.isSafeInteger(used) || used < 0)
102
+ refuse('initialized memory measurement unavailable');
103
+ if (used > limits.memoryBytes)
104
+ refuse('initialized memory ' + used + ' exceeds declared memory ' + limits.memoryBytes);
105
+ const initializedMs = performance.now() - started;
106
+ rt.setMemoryLimit(limits.memoryBytes);
107
+ rt.setMaxStackSize(limits.stackBytes);
108
+ const start = performance.now();
109
+ rt.setInterruptHandler(() => performance.now() - start > limits.executionMs);
110
+ rt.setModuleLoader(id => { if (!owns(sources, id))
111
+ refuse('undeclared import ' + id); return sources[id]; }, (parent, name) => { if (owns(aliases, name))
112
+ return aliases[name]; if (name.startsWith('.'))
113
+ return new URL(name, new URL(parent, 'file:///')).pathname; if (owns(sources, name))
114
+ return name; refuse('undeclared import ' + name); });
115
+ const result = context.evalCode(sources[entry], entry, { type: 'module' });
116
+ if (result.error) {
117
+ const e = context.dump(result.error);
118
+ result.error.dispose();
119
+ refuse('guest ' + JSON.stringify(e));
120
+ }
121
+ result.value.dispose();
122
+ const value = context.evalCode('globalThis.result');
123
+ if (value.error) {
124
+ const e = context.dump(value.error);
125
+ value.error.dispose();
126
+ refuse('result ' + JSON.stringify(e));
127
+ }
128
+ const output = context.dump(value.value);
129
+ value.value.dispose();
130
+ if (typeof output !== 'string')
131
+ refuse('result must be declared text');
132
+ return { output, initializedMs, executionMs: performance.now() - start };
133
+ }
134
+ finally {
135
+ context?.dispose();
136
+ rt.dispose();
137
+ }
138
+ }
139
+ function bytesBase64(bytes) { let binary = ''; for (const b of bytes)
140
+ binary += String.fromCharCode(b); return btoa(binary); }
141
+ // A separate worker makes external abort possible while synchronous WASM executes.
142
+ export async function evaluateWasm(request, { workerFactory = () => new Worker(new URL('./worker.js', import.meta.url), { type: 'module' }), signal, lifetimeMs }) {
143
+ if (!Number.isSafeInteger(lifetimeMs) || lifetimeMs <= 0)
144
+ refuse('worker lifetime');
145
+ if (signal?.aborted)
146
+ refuse('cancelled');
147
+ preflight(request);
148
+ return new Promise((resolve, reject) => { const worker = workerFactory(); let done = false; const finish = (error, value) => { if (done)
149
+ return; done = true; clearTimeout(timer); signal?.removeEventListener('abort', abort); worker.terminate(); error ? reject(error) : resolve(value); }; const abort = () => finish(Error('REFUSE·wasm cancelled')); const timer = setTimeout(() => finish(Error('REFUSE·wasm worker lifetime')), lifetimeMs); signal?.addEventListener('abort', abort, { once: true }); const message = (raw) => { const data = raw; if (!data || typeof data !== 'object' || typeof data.ok !== 'boolean') {
150
+ finish(Error('REFUSE·wasm malformed worker result'));
151
+ return;
152
+ } if (data.ok) {
153
+ if (!data.result || typeof data.result.output !== 'string') {
154
+ finish(Error('REFUSE·wasm malformed worker output'));
155
+ return;
156
+ }
157
+ finish(null, data.result);
158
+ }
159
+ else
160
+ finish(Error(typeof data.error === 'string' ? data.error : 'REFUSE·wasm worker failure')); }; if (typeof worker.addEventListener === 'function') {
161
+ worker.addEventListener('message', e => message(e.data));
162
+ worker.addEventListener('error', e => finish(Error('REFUSE·wasm worker ' + e.message)));
163
+ }
164
+ else {
165
+ if (!worker.on) {
166
+ finish(Error('REFUSE·wasm worker event port'));
167
+ return;
168
+ }
169
+ worker.on('message', message);
170
+ worker.on('error', e => finish(e instanceof Error ? e : Error(String(e))));
171
+ } if (signal?.aborted) {
172
+ abort();
173
+ return;
174
+ } try {
175
+ worker.postMessage(request);
176
+ }
177
+ catch {
178
+ finish(Error('REFUSE·wasm request is not cloneable'));
179
+ } });
180
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { parentPort } from 'node:worker_threads';
2
+ import { runWasmRequest } from "./adapter.js";
3
+ if (!parentPort)
4
+ throw Error('REFUSE·wasm worker port missing');
5
+ const port = parentPort;
6
+ port.once('message', async (request) => {
7
+ try {
8
+ port.postMessage({ ok: true, result: await runWasmRequest(request) });
9
+ }
10
+ catch (error) {
11
+ port.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) });
12
+ }
13
+ });
@@ -0,0 +1,8 @@
1
+ import type { ObjectRecord, ObjectContext } from '@lapxo/topos/wire';
2
+ import type { ObjectReading } from '../engine/index.ts';
3
+ type Fields = Readonly<Record<string, string>>;
4
+ /** Configuration is the caller's already authenticated, live standing; selection grants no authority. */
5
+ export declare function usesWasmObject(configuration: readonly Fields[]): boolean;
6
+ /** The installed instrument owns the asset/form closure; place input cannot choose executable bytes. */
7
+ export declare function foldObjectsWithWasm(root: string, records: readonly ObjectRecord[], context: ObjectContext, configuration: readonly Fields[], signal?: AbortSignal): Promise<readonly ObjectReading[]>;
8
+ export {};
@@ -0,0 +1,76 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { Worker } from 'node:worker_threads';
3
+ import { sha256 } from "../host/hash.js";
4
+ import { toposAt } from "../host/selected-topos.js";
5
+ import { storeOf } from "../land/ledger.js";
6
+ import { evaluateWasm } from "./adapter.js";
7
+ function refuse(cause) { throw Error('REFUSE·wasm object ' + cause); }
8
+ /** Configuration is the caller's already authenticated, live standing; selection grants no authority. */
9
+ export function usesWasmObject(configuration) {
10
+ const rows = configuration.filter(f => f.scope === 'engine/object' && f.value !== 'withdraw');
11
+ if (!rows.length)
12
+ return false;
13
+ if (rows.length !== 1 || rows[0].role !== 'writes' || rows[0].measure !== 'id' || rows[0].form !== 'alphabet' || rows[0].value !== 'wasm@1')
14
+ refuse('unique declared backend required');
15
+ return true;
16
+ }
17
+ function limit(configuration, name, measure) {
18
+ const rows = configuration.filter(f => f.scope === 'engine/wasm/' + name && f.value !== 'withdraw');
19
+ if (rows.length !== 1 || rows[0].form !== 'interval' || rows[0].measure !== measure || rows[0].role !== 'writes')
20
+ refuse('missing or ambiguous ' + name + ' limit');
21
+ const span = /^(0|[1-9][0-9]*)\.\.(0|[1-9][0-9]*)$/.exec(rows[0].value ?? '');
22
+ if (!span || !Number.isSafeInteger(Number(span[1])) || !Number.isSafeInteger(Number(span[2])) || Number(span[1]) > Number(span[2]) || Number(span[2]) < 1)
23
+ refuse('invalid ' + name + ' limit');
24
+ return Number(span[2]);
25
+ }
26
+ /** The installed instrument owns the asset/form closure; place input cannot choose executable bytes. */
27
+ export async function foldObjectsWithWasm(root, records, context, configuration, signal) {
28
+ if (!usesWasmObject(configuration))
29
+ refuse('backend not selected');
30
+ const limits = { memoryBytes: limit(configuration, 'memory', 'bytes'), stackBytes: limit(configuration, 'stack', 'bytes'), executionMs: limit(configuration, 'execution', 'milliseconds') }, lifetimeMs = limit(configuration, 'worker-lifetime', 'milliseconds'), responseBytes = limit(configuration, 'response', 'bytes');
31
+ const assets = new URL('../../assets/wasm/', import.meta.url), manifest = JSON.parse(readFileSync(new URL('manifest.json', assets), 'utf8'));
32
+ if (!manifest.forms)
33
+ refuse('installed form closure missing');
34
+ const codecs = {};
35
+ for (const record of records.filter(r => r.record === 'cell')) {
36
+ const pin = record.fields.topos, binding = manifest.forms[pin];
37
+ if (!binding || !['intervalForm', 'alphabetOfForm'].includes(binding.export) || binding.form !== record.fields.form)
38
+ refuse('unsupported form standing ' + pin);
39
+ const standing = toposAt(pin, storeOf(root)), selector = standing.parts.filter(f => f.scope === 'wire/topos/context-view');
40
+ if (selector.length !== 1)
41
+ refuse('context selection ' + pin);
42
+ const offers = standing.parts.filter(f => f.kind === 'check' && f.view === selector[0].value);
43
+ if (offers.length !== 1 || offers[0].value !== binding.contextArtifact || !(offers[0].restsOn ?? '').split('|').includes(binding.contextArtifact))
44
+ refuse('context implementation differs ' + pin);
45
+ const selected = context.resolve(record);
46
+ if (selected.digest !== pin || selected.form.id !== binding.form)
47
+ refuse('prepared form differs ' + record.fields.scope);
48
+ codecs[record.fields.scope] = { digest: pin, export: binding.export, params: selected.params };
49
+ }
50
+ const guestNames = Object.keys(manifest.modules);
51
+ if (guestNames.length !== 1)
52
+ refuse('installed guest module map');
53
+ const guestName = guestNames[0];
54
+ let program;
55
+ try {
56
+ program = `import{objectFold,intervalForm,alphabetOfForm}from${JSON.stringify(guestName)};const forms={intervalForm,alphabetOfForm},codecs=${JSON.stringify(codecs)},records=${JSON.stringify(records)};const context={resolve:cell=>{const c=codecs[cell.fields.scope];if(!c)throw Error('REFUSE unknown prepared cell');return{digest:c.digest,form:forms[c.export],params:c.params}}};globalThis.result=JSON.stringify(objectFold(records,context));`;
57
+ }
58
+ catch {
59
+ return refuse('context is not serializable');
60
+ }
61
+ const entry = guestName + '.entry', programDigest = sha256(program);
62
+ const guest = readFileSync(new URL('guest.js', assets), 'utf8'), modules = { [guestName]: guest, [entry]: program }, request = { trusted: { ...manifest, modules: { ...manifest.modules, [entry]: programDigest }, entry, aliases: {}, limits, byteLengths: { ...manifest.byteLengths, modules: { ...manifest.byteLengths.modules, [entry]: Buffer.byteLength(program) } } }, assets: { runtime: new Uint8Array(readFileSync(new URL('runtime.js', assets))), wasm: new Uint8Array(readFileSync(new URL('runtime.wasm', assets))) }, modules, entry, aliases: {}, limits };
63
+ const result = await evaluateWasm(request, { workerFactory: () => new Worker(new URL('./node-worker.js', import.meta.url)), lifetimeMs, ...(signal ? { signal } : {}) });
64
+ if (Buffer.byteLength(result.output) > responseBytes)
65
+ refuse('response bytes limit');
66
+ let readings;
67
+ try {
68
+ readings = JSON.parse(result.output);
69
+ }
70
+ catch {
71
+ return refuse('invalid projection text');
72
+ }
73
+ if (!Array.isArray(readings) || readings.some(r => !r || typeof r.cell !== 'string' || typeof r.state !== 'string'))
74
+ refuse('invalid projection shape');
75
+ return readings;
76
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { runWasmRequest } from "./adapter.js";
2
+ self.onmessage = async ({ data }) => { try {
3
+ self.postMessage({ ok: true, result: await runWasmRequest(data) });
4
+ }
5
+ catch (error) {
6
+ self.postMessage({ ok: false, error: String(error) });
7
+ } };
@@ -0,0 +1,25 @@
1
+ {
2
+ "scope": "contract/published/authenticated-reference",
3
+ "members": [
4
+ {
5
+ "role": "contract",
6
+ "path": "docs/contracts/reference-port.md",
7
+ "digest": "sha256:ae500aaec7e94dc9bc7c4d45e3294b212b0ad5ade7647e3ce24807ca7a0ea7cc"
8
+ },
9
+ {
10
+ "role": "yes",
11
+ "path": "samples/authenticated-reference/yes/weather-reading/inputs/yes.json",
12
+ "digest": "sha256:f285a014cc9e9453aebd4c4ed94354fba5cac55c05929f4ad7cdd1b3348a9717"
13
+ },
14
+ {
15
+ "role": "vector",
16
+ "path": "vectors/native/authenticated-reference.vector.json",
17
+ "digest": "sha256:297c4942a847c42eb9d2cf000ab38d8a5f38b64e2e4a469919f0dfc88c9d8bd1"
18
+ },
19
+ {
20
+ "role": "no",
21
+ "path": "samples/authenticated-reference/yes/weather-reading/inputs/signature.json",
22
+ "digest": "sha256:2049bbbf88077727911a0cfec3193ab6ce7bccb8d4e3bb22fc537c59f5addf58"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "scope": "contract/published/engine-object-projection",
3
+ "members": [
4
+ {
5
+ "role": "contract",
6
+ "path": "docs/contracts/engine.md",
7
+ "digest": "sha256:2d54ddc75386a0706a3bc500e36deedc85ca9e7dc965eff5c795f02b1a716078"
8
+ },
9
+ {
10
+ "role": "yes",
11
+ "path": "samples/engine-interval/inputs/sample.json",
12
+ "digest": "sha256:ef476bda8c15b4967a88dc27a188ab25cfac42bdf381fb82f4e57a482d2fd27d"
13
+ },
14
+ {
15
+ "role": "no",
16
+ "path": "samples/no/engine-order/inputs/sample.json",
17
+ "digest": "sha256:f2104a661541baf7f8e6d9224590210b272e24519dafce94ca7c588e5a78a20d"
18
+ },
19
+ {
20
+ "role": "vector",
21
+ "path": "vectors/engine-object.cli.json",
22
+ "digest": "sha256:191920f597e2d8bafc9aeeff13934645c64a790dcad94b22c562b30c5fe6baa0"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,39 @@
1
+ # Pure object projection
2
+
3
+ `@lapxo/bound/engine` exports `objectFold` and the existing `ObjectReading`, `ObjectRecord` and `ObjectContext` types. The CLI and receipt paths use that same implementation. Its Obligations 0.3.5 algebra is unchanged.
4
+
5
+ The input is authenticated and context-admitted effective history with selected deterministic forms and parameters. This function does not admit records, verify signatures, resolve content, run commands, select clocks or write storage. Applications must perform preparation before projection.
6
+
7
+ Within each epoch, a cell declaration precedes its uses in effective order. A same-epoch act before its cell refuses in preparation by name: `object context: cell not declared before <act-id>`. Lower-epoch cell declarations remain eligible even when their envelope arrives later. Effective sign/join order is preserved; transport arrival does not authorize arbitrary semantic reordering. The consumed Topos contract must be published before this export is admitted for release.
8
+
9
+ Outputs preserve existing attributed origins, local and travelling marks, exact withdrawals, floor and ceiling parts and REQUIRED/FREE/FORBIDDEN/CONFLICT decisions. Raw object JSON is not a new canonical identity. Byte identity requires the existing declared reading projection.
10
+
11
+ Malformed or unauthenticated records are outside this pure projector's input contract. Refusal, authority and interrupted-admission vectors exercise the host boundary; they do not claim cryptographic admission inside WASM. No host callback calculates an algebraic result in a WASM comparison.
12
+
13
+ ## Public conformance samples
14
+
15
+ `samples/engine-interval` and `samples/engine-alphabet` are formed places: TARGET.bound, receipts.bound, package.json, the declared reader.js and inputs/sample.json. The instrument exercises the same projector against prepared fixtures; its reader is not a command users invoke. `fold <sample-directory> --check` verifies each portable receipt. Cold native `fold --as receipts` recomputes the declared conformance reading without a private store.
16
+
17
+ `samples/no/engine-interval.json` and `samples/no/engine-alphabet.json` are negative data fixtures. Their captured native preparation refusal is `cell not declared before s-B`; no closed receipts are asserted for refusing input. The actual CLI vector is `vectors/engine-object.cli.json`. This conformance reader does not authenticate arbitrary signatures, coverage or clocks.
18
+
19
+
20
+ ## Optional WASM host adapter
21
+
22
+ The optional `@lapxo/bound/wasm` entry evaluates the existing pure projection in QuickJS WASM. It performs no admission, signature verification, state calculation of its own, transport, key discovery or effects. Host authentication must finish before records become projection inputs. A valid digest identifies bytes; it grants no authority or confinement guarantee.
23
+
24
+ The host supplies the trusted runtime/WASM/module digests, exact byte lengths, entry, aliases and limits. These expectations must come from its accepted configuration, independently of fetched place bytes. Closed request metadata and exact lengths are checked before copying, hashing or worker handoff. Shared assets refuse. Every supplied byte is verified and copied before execution; the closed runtime bundle has no imports and receives the already verified WASM binary. Guest imports resolve only within the verified module map. No second fetch occurs.
25
+
26
+ `runWasmRequest` returns declared text, without inventing a JSON canonicalization or number policy. The caller's declared entry chooses the serialization contract. Memory and stack limits govern the guest; they do not describe total host/WASM initialization memory. Guest memory, stack and execution budgets are explicit host inputs. Initialization and execution times are separate readings, not a speed guarantee.
27
+
28
+ `evaluateWasm` runs in a host worker. It accepts an AbortSignal and an explicit worker lifetime. Cancellation, timeout, malformed messages, failed serialization and worker errors terminate the worker and return no successful result. Guest state remains provisional until success; no host write port is supplied. A terminated computation produces no successful receipt. The direct `runWasmRequest` entry has deadline interruption but cannot asynchronously receive an external abort while synchronously executing; callers needing that contract use `evaluateWasm`.
29
+
30
+
31
+ ### Native optional WASM projection
32
+
33
+ The existing object view consumes an optional, authenticated live declaration `engine/object form=alphabet measure=id value=wasm@1`. With no selector it retains the ESM projection; a conflicting or unknown selector refuses. This profile selects only the explicit object view, not every other view of the place. The host admits and folds configuration with the existing authority and withdrawal rules before selecting execution.
34
+
35
+ The place declares five interval limits: `engine/wasm/memory`, `engine/wasm/stack` and `engine/wasm/response` in bytes; `engine/wasm/execution` and `engine/wasm/worker-lifetime` in milliseconds. Missing, ambiguous or invalid limits refuse; there is no inferred fallback. The installed instrument binds the guest module and supported form standings to their context implementation artifacts. A place cannot choose another verifier or executable through this selection; an unsupported form or different implementation refuses.
36
+
37
+ Context initialization is measured before place execution. If observed initialized guest memory exceeds the declared memory budget, the operation refuses without running place code. Host initialization memory is outside that guest execution limit; the worker lifetime bounds the entire attempt. During execution the unchanged declared memory, stack and execution limits apply. SIGINT reaches the worker cancellation port; cancellation or failure emits no successful object result.
38
+
39
+ For the opt-in `wasm@1` profile, the host verifies the selected context implementation before importing it. Every SHA-256 member declared by the context offer’s `restsOn` is checked by digest, and the existing `moduleClosure` validates the imported closure. Missing, altered, undeclared or unresolved imported members refuse before context code executes. This establishes content identity and import closure, not correctness, authority or sandbox confinement. Admission still precedes projection, and this check grants no permission to reuse a retained answer as current.
@@ -0,0 +1,8 @@
1
+ {
2
+ "profile": "quartet-process@1",
3
+ "scope": "contract/published/native",
4
+ "surface": [
5
+ "contract/published/optional-wasm-adapter",
6
+ "contract/published/authenticated-reference"
7
+ ]
8
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "scope": "contract/published/optional-wasm-adapter",
3
+ "members": [
4
+ {
5
+ "role": "contract",
6
+ "path": "docs/contracts/engine.md",
7
+ "digest": "sha256:2d54ddc75386a0706a3bc500e36deedc85ca9e7dc965eff5c795f02b1a716078"
8
+ },
9
+ {
10
+ "role": "yes",
11
+ "path": "samples/wasm/water-level/TARGET.bound",
12
+ "digest": "sha256:d2f3efc176a6fa7f695dcaab93ed6f35fac4e601d0b7e47f485ac15109d11a0c"
13
+ },
14
+ {
15
+ "role": "vector",
16
+ "path": "vectors/wasm-object.cli.json",
17
+ "digest": "sha256:5f761fabd0592d7f8e9b95a9fc80583424a8e83fb7cb3c4b4905b62eca43db9e"
18
+ },
19
+ {
20
+ "role": "no",
21
+ "path": "samples/wasm/water-level/no-memory.bound",
22
+ "digest": "sha256:4c30cb6c77a9079a5961f0ff50c53830ad2c7ca8526ef63aa7c3eb4fa2af4554"
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "profile": "quartet-process@1",
3
+ "scope": "contract/published/additions",
4
+ "surface": [
5
+ "contract/published/engine-object-projection",
6
+ "contract/published/reference-port"
7
+ ]
8
+ }