@lapxo/bound 0.1.9 → 0.2.1

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 (319) hide show
  1. package/README.md +40 -121
  2. package/TARGET.bound +85 -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.js +7 -4
  9. package/dist/cli/standing-result.js +4 -3
  10. package/dist/cli/verb.js +79 -9
  11. package/dist/engine/encoding.js +2 -0
  12. package/dist/engine/index.d.ts +13 -0
  13. package/dist/engine/index.js +11 -0
  14. package/dist/engine/object.js +38 -0
  15. package/dist/engine/ports.d.ts +32 -0
  16. package/dist/engine/ports.js +103 -0
  17. package/dist/fold/closed.js +22 -5
  18. package/dist/fold/digests.js +3 -2
  19. package/dist/fold/object.js +2 -38
  20. package/dist/fold/resolved.js +1 -1
  21. package/dist/host/admitted-place.js +10 -1
  22. package/dist/host/cell-receipts.js +1 -1
  23. package/dist/host/contextual-place.js +2 -2
  24. package/dist/host/io.js +2 -2
  25. package/dist/host/objects.js +24 -4
  26. package/dist/host/reading-closures.js +11 -13
  27. package/dist/host/reference-authenticated.d.ts +40 -0
  28. package/dist/host/reference-authenticated.js +100 -0
  29. package/dist/host/reference-contracts.js +6 -0
  30. package/dist/host/reference-export.d.ts +33 -0
  31. package/dist/host/reference-export.js +81 -0
  32. package/dist/host/reference-native-api.d.ts +3 -0
  33. package/dist/host/reference-native-api.js +3 -0
  34. package/dist/host/reference-native.d.ts +30 -0
  35. package/dist/host/reference-native.js +74 -0
  36. package/dist/host/reference-profile.d.ts +23 -0
  37. package/dist/host/reference-profile.js +63 -0
  38. package/dist/host/reference-standing.js +18 -0
  39. package/dist/host/reference.js +24 -0
  40. package/dist/host/refusals.d.ts +14 -0
  41. package/dist/host/refusals.js +43 -0
  42. package/dist/host/selected-topos.js +9 -1
  43. package/dist/host/signing.js +7 -1
  44. package/dist/observe/files.js +11 -1
  45. package/dist/observe/regions.js +109 -1
  46. package/dist/wasm/adapter.d.ts +51 -0
  47. package/dist/wasm/adapter.js +180 -0
  48. package/dist/wasm/node-worker.js +13 -0
  49. package/dist/wasm/object.js +76 -0
  50. package/dist/wasm/worker.js +7 -0
  51. package/docs/contracts/authenticated-reference.publication.json +25 -0
  52. package/docs/contracts/engine-object-projection.publication.json +25 -0
  53. package/docs/contracts/engine.md +39 -0
  54. package/docs/contracts/host-ports.md +23 -0
  55. package/docs/contracts/host-ports.publication.json +25 -0
  56. package/docs/contracts/native-publication.inventory.json +14 -0
  57. package/docs/contracts/optional-wasm-adapter.publication.json +25 -0
  58. package/docs/contracts/publication-additions.inventory.json +8 -0
  59. package/docs/contracts/readme.publication.json +25 -0
  60. package/docs/contracts/reference-backend.md +24 -0
  61. package/docs/contracts/reference-backend.publication.json +25 -0
  62. package/docs/contracts/reference-export.md +31 -0
  63. package/docs/contracts/reference-export.publication.json +25 -0
  64. package/docs/contracts/reference-port.md +40 -0
  65. package/docs/contracts/reference-port.publication.json +25 -0
  66. package/docs/contracts/refusals.bound +94 -0
  67. package/docs/contracts/refusals.md +47 -0
  68. package/docs/contracts/refusals.publication.json +25 -0
  69. package/docs/contracts/standing.bound +9 -0
  70. package/docs/contracts/standing.md +24 -0
  71. package/docs/contracts/standing.publication.json +25 -0
  72. package/licenses/wasm/bound.txt +21 -0
  73. package/licenses/wasm/fast-text-encoding.txt +201 -0
  74. package/licenses/wasm/jitl-quickjs-ffi-types.txt +21 -0
  75. package/licenses/wasm/jitl-quickjs-wasmfile-release-sync.txt +47 -0
  76. package/licenses/wasm/obligations.txt +21 -0
  77. package/licenses/wasm/quickjs-emscripten-core.txt +21 -0
  78. package/licenses/wasm/topos.txt +21 -0
  79. package/package.json +141 -6
  80. package/receipts.bound +9554 -613
  81. package/samples/authenticated-reference/compatibility.json +306 -0
  82. package/samples/authenticated-reference/no.json +31 -0
  83. package/samples/authenticated-reference/yes/editorial-state/TARGET.bound +57 -0
  84. package/samples/authenticated-reference/yes/editorial-state/inputs/arrival.json +1 -0
  85. package/samples/authenticated-reference/yes/editorial-state/inputs/coverage.json +1 -0
  86. package/samples/authenticated-reference/yes/editorial-state/inputs/era.json +1 -0
  87. package/samples/authenticated-reference/yes/editorial-state/inputs/order.json +1 -0
  88. package/samples/authenticated-reference/yes/editorial-state/inputs/signature.json +1 -0
  89. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.arrival.json +1 -0
  90. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.coverage.json +1 -0
  91. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.era.json +1 -0
  92. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.order.json +1 -0
  93. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.signature.json +1 -0
  94. package/samples/authenticated-reference/yes/editorial-state/inputs/transport.yes.json +1 -0
  95. package/samples/authenticated-reference/yes/editorial-state/inputs/yes.json +1 -0
  96. package/samples/authenticated-reference/yes/weather-reading/TARGET.bound +57 -0
  97. package/samples/authenticated-reference/yes/weather-reading/inputs/arrival.json +1 -0
  98. package/samples/authenticated-reference/yes/weather-reading/inputs/coverage.json +1 -0
  99. package/samples/authenticated-reference/yes/weather-reading/inputs/era.json +1 -0
  100. package/samples/authenticated-reference/yes/weather-reading/inputs/order.json +1 -0
  101. package/samples/authenticated-reference/yes/weather-reading/inputs/signature.json +1 -0
  102. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.arrival.json +1 -0
  103. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.coverage.json +1 -0
  104. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.era.json +1 -0
  105. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.order.json +1 -0
  106. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.signature.json +1 -0
  107. package/samples/authenticated-reference/yes/weather-reading/inputs/transport.yes.json +1 -0
  108. package/samples/authenticated-reference/yes/weather-reading/inputs/yes.json +1 -0
  109. package/samples/authenticated-reference/yes.json +19 -0
  110. package/samples/engine-alphabet/TARGET.bound +29 -0
  111. package/samples/engine-alphabet/inputs/sample.json +323 -0
  112. package/samples/engine-alphabet/package.json +1 -0
  113. package/samples/engine-alphabet/reader.js +1457 -0
  114. package/samples/engine-alphabet/receipts.bound +12 -0
  115. package/samples/engine-interval/TARGET.bound +29 -0
  116. package/samples/engine-interval/inputs/sample.json +277 -0
  117. package/samples/engine-interval/package.json +1 -0
  118. package/samples/engine-interval/reader.js +1457 -0
  119. package/samples/engine-interval/receipts.bound +12 -0
  120. package/samples/host-ports/no.json +16 -0
  121. package/samples/host-ports/yes.json +9 -0
  122. package/samples/no/engine-order/TARGET.bound +29 -0
  123. package/samples/no/engine-order/inputs/sample.json +277 -0
  124. package/samples/no/engine-order/package.json +1 -0
  125. package/samples/no/engine-order/reader.js +1457 -0
  126. package/samples/readme/no.json +1 -0
  127. package/samples/readme/yes.json +1 -0
  128. package/samples/reference-backend/no.json +1 -0
  129. package/samples/reference-backend/world-fixture.json +8 -0
  130. package/samples/reference-backend/yes.json +1 -0
  131. package/samples/reference-export/no.json +5 -0
  132. package/samples/reference-export/yes.json +15 -0
  133. package/samples/reference-port/no/catalog/TARGET.bound +33 -0
  134. package/samples/reference-port/no/catalog/standing.json +1 -0
  135. package/samples/reference-port/no/catalog/transport.json +1 -0
  136. package/samples/reference-port/no/route-notes/TARGET.bound +33 -0
  137. package/samples/reference-port/no/route-notes/standing.json +1 -0
  138. package/samples/reference-port/no/route-notes/transport.json +1 -0
  139. package/samples/reference-port/yes/catalog/TARGET.bound +33 -0
  140. package/samples/reference-port/yes/catalog/standing.json +1 -0
  141. package/samples/reference-port/yes/catalog/transport.json +1 -0
  142. package/samples/reference-port/yes/route-notes/TARGET.bound +33 -0
  143. package/samples/reference-port/yes/route-notes/standing.json +1 -0
  144. package/samples/reference-port/yes/route-notes/transport.json +1 -0
  145. package/samples/refusals/no.json +4 -0
  146. package/samples/refusals/yes.json +9 -0
  147. package/samples/standing/no.json +4 -0
  148. package/samples/standing/yes.json +21 -0
  149. package/samples/wasm/editorial-notes/TARGET.bound +65 -0
  150. package/samples/wasm/editorial-notes/esm.bound +59 -0
  151. package/samples/wasm/editorial-notes/no-deadline.bound +65 -0
  152. package/samples/wasm/editorial-notes/no-memory.bound +65 -0
  153. package/samples/wasm/editorial-notes/no-missing-limit.bound +64 -0
  154. package/samples/wasm/editorial-notes/receipts.bound +1 -0
  155. package/samples/wasm/water-level/TARGET.bound +65 -0
  156. package/samples/wasm/water-level/esm.bound +59 -0
  157. package/samples/wasm/water-level/no-deadline.bound +65 -0
  158. package/samples/wasm/water-level/no-memory.bound +65 -0
  159. package/samples/wasm/water-level/no-missing-limit.bound +64 -0
  160. package/samples/wasm/water-level/receipts.bound +1 -0
  161. package/vectors/engine-object.cli.json +548 -0
  162. package/vectors/native/authenticated-reference-formation.vector.json +107 -0
  163. package/vectors/native/authenticated-reference.cli.json +157 -0
  164. package/vectors/native/authenticated-reference.vector.json +76 -0
  165. package/vectors/native/host-ports.vector.json +107 -0
  166. package/vectors/native/readme.vector.json +76 -0
  167. package/vectors/native/reference-backend.vector.json +84 -0
  168. package/vectors/native/reference-export.vector.json +78 -0
  169. package/vectors/native/refusals.vector.json +95 -0
  170. package/vectors/native/standing.vector.json +116 -0
  171. package/vectors/reference-port.cli.json +688 -0
  172. package/vectors/wasm-object.cli.json +407 -0
  173. package/dist/cli/accept.d.ts +0 -28
  174. package/dist/cli/act-result.d.ts +0 -12
  175. package/dist/cli/args.d.ts +0 -11
  176. package/dist/cli/custodian.d.ts +0 -1
  177. package/dist/cli/host.d.ts +0 -1
  178. package/dist/cli/names.d.ts +0 -25
  179. package/dist/cli/native-descendant.d.ts +0 -1
  180. package/dist/cli/native.d.ts +0 -1
  181. package/dist/cli/observe.d.ts +0 -1
  182. package/dist/cli/owner.d.ts +0 -37
  183. package/dist/cli/pass.d.ts +0 -6
  184. package/dist/cli/place.d.ts +0 -74
  185. package/dist/cli/release-result.d.ts +0 -5
  186. package/dist/cli/standing-result.d.ts +0 -5
  187. package/dist/cli/takes.d.ts +0 -30
  188. package/dist/fold/admission-history.d.ts +0 -6
  189. package/dist/fold/ceilings.d.ts +0 -24
  190. package/dist/fold/claims.d.ts +0 -29
  191. package/dist/fold/closed.d.ts +0 -15
  192. package/dist/fold/configures.d.ts +0 -4
  193. package/dist/fold/digests.d.ts +0 -29
  194. package/dist/fold/evidence.d.ts +0 -32
  195. package/dist/fold/fold-worker.d.ts +0 -1
  196. package/dist/fold/kept.d.ts +0 -29
  197. package/dist/fold/keys.d.ts +0 -17
  198. package/dist/fold/laid.d.ts +0 -17
  199. package/dist/fold/nos.d.ts +0 -8
  200. package/dist/fold/observed.d.ts +0 -35
  201. package/dist/fold/opened.d.ts +0 -5
  202. package/dist/fold/own.d.ts +0 -24
  203. package/dist/fold/paid.d.ts +0 -11
  204. package/dist/fold/place-inputs.d.ts +0 -17
  205. package/dist/fold/places.d.ts +0 -24
  206. package/dist/fold/rank.d.ts +0 -3
  207. package/dist/fold/reach.d.ts +0 -18
  208. package/dist/fold/readers.d.ts +0 -47
  209. package/dist/fold/receipts.d.ts +0 -8
  210. package/dist/fold/region-declarations.d.ts +0 -2
  211. package/dist/fold/region.d.ts +0 -72
  212. package/dist/fold/release-policy.d.ts +0 -67
  213. package/dist/fold/resolved.d.ts +0 -18
  214. package/dist/fold/roles.d.ts +0 -17
  215. package/dist/fold/second.d.ts +0 -18
  216. package/dist/fold/shards.d.ts +0 -29
  217. package/dist/fold/signed.d.ts +0 -23
  218. package/dist/fold/signers.d.ts +0 -24
  219. package/dist/fold/stranger.d.ts +0 -3
  220. package/dist/fold/views.d.ts +0 -27
  221. package/dist/fold/wire.d.ts +0 -19
  222. package/dist/fold/zoom.d.ts +0 -16
  223. package/dist/host/act-cost.d.ts +0 -13
  224. package/dist/host/adapters/keys/ed25519.d.ts +0 -7
  225. package/dist/host/adapters/modules/javascript.d.ts +0 -3
  226. package/dist/host/adapters/transport/http.d.ts +0 -5
  227. package/dist/host/admitted-place.d.ts +0 -5
  228. package/dist/host/archive.d.ts +0 -11
  229. package/dist/host/capsule-process.d.ts +0 -21
  230. package/dist/host/capsule.d.ts +0 -19
  231. package/dist/host/cell-receipts.d.ts +0 -2
  232. package/dist/host/content-needed.d.ts +0 -5
  233. package/dist/host/content-store.d.ts +0 -8
  234. package/dist/host/contextual-place.d.ts +0 -59
  235. package/dist/host/digest.d.ts +0 -3
  236. package/dist/host/hash.d.ts +0 -9
  237. package/dist/host/io.d.ts +0 -11
  238. package/dist/host/lifetime.d.ts +0 -1
  239. package/dist/host/located.d.ts +0 -4
  240. package/dist/host/native/bounded-file.d.ts +0 -8
  241. package/dist/host/native/command.d.ts +0 -36
  242. package/dist/host/native/control-bridge-worker.d.ts +0 -1
  243. package/dist/host/native/control-bridge.d.ts +0 -30
  244. package/dist/host/native/control-selection.d.ts +0 -29
  245. package/dist/host/native/custody.d.ts +0 -5
  246. package/dist/host/native/deadline.d.ts +0 -12
  247. package/dist/host/native/descendants.d.ts +0 -1
  248. package/dist/host/native/generated-custodian.d.ts +0 -122
  249. package/dist/host/native/generated.d.ts +0 -217
  250. package/dist/host/native/image.d.ts +0 -36
  251. package/dist/host/native/jobs.d.ts +0 -75
  252. package/dist/host/native/loader.d.ts +0 -80
  253. package/dist/host/native/materialize.d.ts +0 -22
  254. package/dist/host/native/module-closure.d.ts +0 -27
  255. package/dist/host/native/module-syntax.d.ts +0 -8
  256. package/dist/host/native/native-binary.d.ts +0 -54
  257. package/dist/host/native/native-program.d.ts +0 -56
  258. package/dist/host/native/node-jobs.d.ts +0 -84
  259. package/dist/host/native/node-launch.d.ts +0 -72
  260. package/dist/host/native/preload.d.ts +0 -1
  261. package/dist/host/native/reading.d.ts +0 -91
  262. package/dist/host/native/receipt-environment.d.ts +0 -3
  263. package/dist/host/native/references.d.ts +0 -5
  264. package/dist/host/native/root.d.ts +0 -17
  265. package/dist/host/native/route.d.ts +0 -66
  266. package/dist/host/native/runtime.d.ts +0 -4
  267. package/dist/host/native-reader-context.d.ts +0 -3
  268. package/dist/host/objects.d.ts +0 -5
  269. package/dist/host/place-clock-selection.d.ts +0 -55
  270. package/dist/host/ports/ingress.d.ts +0 -24
  271. package/dist/host/ports/local-act.d.ts +0 -44
  272. package/dist/host/ports/place-act.d.ts +0 -46
  273. package/dist/host/ports/publication.d.ts +0 -10
  274. package/dist/host/ports/signing.d.ts +0 -31
  275. package/dist/host/ports/store.d.ts +0 -2
  276. package/dist/host/ports/transport.d.ts +0 -1
  277. package/dist/host/process-lifetime.d.ts +0 -11
  278. package/dist/host/provide.d.ts +0 -48
  279. package/dist/host/read-only.d.ts +0 -3
  280. package/dist/host/reading-closures.d.ts +0 -45
  281. package/dist/host/reading-context.d.ts +0 -29
  282. package/dist/host/release.d.ts +0 -14
  283. package/dist/host/root-clock-source.d.ts +0 -85
  284. package/dist/host/selected-capsules.d.ts +0 -8
  285. package/dist/host/selected-topos.d.ts +0 -24
  286. package/dist/host/signing.d.ts +0 -3
  287. package/dist/host/source-content.d.ts +0 -12
  288. package/dist/host/timing.d.ts +0 -2
  289. package/dist/host/walk.d.ts +0 -6
  290. package/dist/judge/late.d.ts +0 -7
  291. package/dist/judge/take.d.ts +0 -59
  292. package/dist/judge/widening.d.ts +0 -1
  293. package/dist/land/act.d.ts +0 -30
  294. package/dist/land/alias.d.ts +0 -10
  295. package/dist/land/held.d.ts +0 -7
  296. package/dist/land/inert.d.ts +0 -26
  297. package/dist/land/leaving.d.ts +0 -14
  298. package/dist/land/ledger.d.ts +0 -33
  299. package/dist/land/owned.d.ts +0 -23
  300. package/dist/land/sign.d.ts +0 -15
  301. package/dist/land/standing-pin.d.ts +0 -3
  302. package/dist/land/vouched.d.ts +0 -21
  303. package/dist/land/withdraw.d.ts +0 -14
  304. package/dist/observe/files.d.ts +0 -14
  305. package/dist/observe/identity.d.ts +0 -21
  306. package/dist/observe/match.d.ts +0 -1
  307. package/dist/observe/regions.d.ts +0 -29
  308. package/dist/observe/run.d.ts +0 -31
  309. package/dist/observe/runner.d.ts +0 -20
  310. package/dist/render/capsule.d.ts +0 -24
  311. package/dist/render/field.d.ts +0 -3
  312. package/dist/render/names.d.ts +0 -3
  313. package/dist/render/open.d.ts +0 -27
  314. package/dist/render/page.d.ts +0 -3
  315. package/dist/render/receipts.d.ts +0 -3
  316. package/dist/render/shape.d.ts +0 -16
  317. package/dist/render/target.d.ts +0 -18
  318. package/dist/render/view.d.ts +0 -22
  319. /package/dist/{fold → engine}/object.d.ts +0 -0
@@ -0,0 +1,103 @@
1
+ import { signerRequestBytes, signerResponseBytes, signerResponseOf } from '@lapxo/topos/wire';
2
+ import { BoundRefusal } from "../host/refusals.js";
3
+ import { encodeUTF8 as encoded } from "./encoding.js";
4
+ class PortFault extends BoundRefusal {
5
+ }
6
+ const fail = (code) => { throw new PortFault('host-port/' + code, 'REFUSE·host-port/' + code); };
7
+ function limitsOf(limits) { if (!limits || !Number.isSafeInteger(limits.timeoutMs) || limits.timeoutMs < 1 || limits.timeoutMs > 2147483647 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1)
8
+ fail('budget'); }
9
+ /** The callback must cooperate with cancellation; JavaScript callbacks are not a sandbox. */
10
+ async function bounded(limits, signal, run) {
11
+ limitsOf(limits);
12
+ if (signal?.aborted)
13
+ fail('cancelled');
14
+ return new Promise((resolve, reject) => {
15
+ const controller = new AbortController();
16
+ let done = false;
17
+ const finish = (code, value) => { if (done)
18
+ return; done = true; clearTimeout(timer); signal?.removeEventListener('abort', abort); if (code) {
19
+ controller.abort();
20
+ reject(new BoundRefusal('host-port/' + code, 'REFUSE·host-port/' + code));
21
+ }
22
+ else
23
+ resolve(value); };
24
+ const abort = () => finish('cancelled'), timer = setTimeout(() => finish('timeout'), limits.timeoutMs);
25
+ signal?.addEventListener('abort', abort, { once: true });
26
+ if (signal?.aborted) {
27
+ abort();
28
+ return;
29
+ }
30
+ Promise.resolve().then(() => { if (done)
31
+ fail('cancelled'); return run(controller.signal); }).then(value => finish(undefined, value), error => finish(error instanceof PortFault ? error.code.slice('host-port/'.length) : 'failed'));
32
+ });
33
+ }
34
+ function owned(bytes, maxBytes) { if (!(bytes instanceof Uint8Array))
35
+ fail('response'); if (bytes.byteLength > maxBytes)
36
+ fail('budget'); if (typeof SharedArrayBuffer !== 'undefined' && bytes.buffer instanceof SharedArrayBuffer)
37
+ fail('response'); return new Uint8Array(bytes); }
38
+ const identity = (digest) => { if (!/^sha256:[a-f0-9]{64}$/.test(digest))
39
+ fail('integrity'); };
40
+ export async function readContent(source, digest, ports, limits, signal) {
41
+ limitsOf(limits);
42
+ identity(digest);
43
+ return bounded(limits, signal, async (s) => { const bytes = owned(await ports.read(source, digest, s), limits.maxBytes); if (s.aborted)
44
+ fail('cancelled'); if (await ports.digestBytes(new Uint8Array(bytes)) !== digest)
45
+ fail('integrity'); if (s.aborted)
46
+ fail('cancelled'); return bytes; });
47
+ }
48
+ /** Verify before invoking retention; no operation automatically writes the projected result. */
49
+ export async function retainContent(digest, bytes, ports, limits, signal) {
50
+ limitsOf(limits);
51
+ identity(digest);
52
+ if (signal?.aborted)
53
+ fail('cancelled');
54
+ const copy = owned(bytes, limits.maxBytes);
55
+ return bounded(limits, signal, async (s) => { if (await ports.digestBytes(new Uint8Array(copy)) !== digest)
56
+ fail('integrity'); if (s.aborted)
57
+ fail('cancelled'); const result = await ports.retain(digest, copy, s); if (result !== undefined)
58
+ fail('response'); return Object.freeze({ digest, bytes: copy.byteLength }); });
59
+ }
60
+ /** Acquire protocol-shaped signatures only; the existing consent path verifies them and decides authority. */
61
+ export async function invokeSigningPort(lot, sign, limits, signal) {
62
+ limitsOf(limits);
63
+ if (!lot || !Array.isArray(lot.bytes))
64
+ fail('budget');
65
+ let size = 0;
66
+ for (const bytes of lot.bytes) {
67
+ if (typeof bytes !== 'string' || bytes.length > limits.maxBytes - size)
68
+ fail('budget');
69
+ size += encoded(bytes).length + 1;
70
+ if (size > limits.maxBytes)
71
+ fail('budget');
72
+ }
73
+ let request;
74
+ try {
75
+ request = signerRequestBytes(lot);
76
+ }
77
+ catch {
78
+ return fail('response');
79
+ }
80
+ if (encoded(request).length > limits.maxBytes)
81
+ fail('budget');
82
+ const input = Object.freeze({ ...lot, bytes: Object.freeze([...lot.bytes]) });
83
+ return bounded(limits, signal, async (s) => {
84
+ const records = await sign(input, s);
85
+ if (!Array.isArray(records) || records.length !== lot.bytes.length || records.some(r => !r || typeof r.signature !== 'string' || r.signature.length > limits.maxBytes))
86
+ fail('response');
87
+ let response;
88
+ try {
89
+ response = signerResponseBytes(records);
90
+ }
91
+ catch {
92
+ return fail('response');
93
+ }
94
+ if (encoded(response).length > limits.maxBytes)
95
+ fail('budget');
96
+ try {
97
+ return Object.freeze(signerResponseOf(response, lot).map(record => Object.freeze({ ...record })));
98
+ }
99
+ catch {
100
+ return fail('response');
101
+ }
102
+ });
103
+ }
@@ -10,9 +10,10 @@ import { bytesDigest as hashBytes, fullDigest as hashFull } from "../host/digest
10
10
  import { mountedCoordinatesIn, worldAt } from "./places.js";
11
11
  import { ownLockOf } from "./signed.js";
12
12
  import { viewsOf } from "./views.js";
13
+ import { keptPlace } from "./kept.js";
13
14
  import { storeAt, storeOf } from "../land/ledger.js";
14
15
  import { coordinatesUnder, observeFile, observeText } from "../observe/files.js";
15
- import { carriedFrom } from "./resolved.js";
16
+ import { bytesOnly, carriedFrom } from "./resolved.js";
16
17
  import { takenFor } from "../fold/place-inputs.js";
17
18
  /** A region is closed when the digest of its bytes is the one its receipt names. Folding a closed region is idle: idleCount is how many were already closed, actSeconds the largest measured act or unpartitioned host work, receiptsSeen how many receipts were read and how long that took. */
18
19
  let idle = 0;
@@ -178,7 +179,7 @@ export async function closeReceipts(root, store, semantic) {
178
179
  return open;
179
180
  }
180
181
  /** One comparison. A place meets its receipt; the root meets when every receipt it names meets. */
181
- export function meets(root, store, place, current = true) {
182
+ export function meets(root, store, place, current = true, active) {
182
183
  const name = place.replace(/\/$/, '');
183
184
  if (name === '') {
184
185
  const at = Date.now();
@@ -192,12 +193,28 @@ export function meets(root, store, place, current = true) {
192
193
  return bytes !== undefined && hashBytes(bytes, algorithm) === expected && meets(root, store, `${one}/`, current);
193
194
  });
194
195
  if (closed && carried) {
195
- const algorithm = algorithmIn(own.join('\n'));
196
+ const algorithm = algorithmIn(own.join('\n')), hash = (text) => hashFull(text, algorithm);
196
197
  const profile = readingProfile((observeText(join(root, LOCK)) ?? '').split('\n').filter(isWire), fieldOf);
197
- const readings = verifiedReadingReceipts(store, carried, text => hashFull(text, algorithm), current && profile ? currentReadingEnvironment(store, root, text => hashFull(text, algorithm), childFlags()) : undefined, profile);
198
+ // History keeps its canonical identity and bytes. It does not become a
199
+ // current execution merely because it shares a public carrier with one.
200
+ const readings = verifiedReadingReceipts(store, carried, hash, undefined, profile);
198
201
  if (readings === undefined)
199
202
  closed = false;
200
- else if (readings)
203
+ else if (current && profile && readings) {
204
+ const stamp = instrumentKept(store);
205
+ const snapshot = active ?? (stamp ? keptPlace(store, undefined, stamp)?.observed : undefined);
206
+ if (snapshot === undefined)
207
+ closed = false;
208
+ else {
209
+ const units = snapshot.filter(line => fieldOf(line, 'scope').startsWith('receipts/readings/') && fieldOf(line, 'role') === 'writes');
210
+ const supplied = new Set(carried);
211
+ const present = units.length > 0 && units.every(line => supplied.has(line) || (fieldOf(line, 'measure') === 'observed' && supplied.has(bytesOnly(line))));
212
+ const fresh = present ? verifiedReadingReceipts(store, snapshot, hash, currentReadingEnvironment(store, root, hash, childFlags()), profile) : undefined;
213
+ if (!fresh)
214
+ closed = false;
215
+ }
216
+ }
217
+ if (closed && readings)
201
218
  process.stderr.write(`RECEIPTS ${readings} read · 0 opened · reading closures\n`);
202
219
  }
203
220
  seen = { count: named.size + (own.length > 0 ? 1 : 0), ms: Date.now() - at };
@@ -160,8 +160,9 @@ const importsOf = (store, at, digest) => {
160
160
  * A fold is a function of its points and the instrument is one of them, so a fold kept by another bound is another
161
161
  * origin's fold — readable as history, never as this instrument's own state.
162
162
  */
163
- export function instrumentOf(store, entry, standing = [], root = dirname(resolve(entry)), releases = []) {
164
- return instrumentDigest(store, entry, standing, root, releases).replace(/^[^:]*:/, '').slice(0, 12);
163
+ export function instrumentOf(store, entry, standing = [], root, releases = []) {
164
+ const coordinate = root === undefined ? resolve(entry) : resolve(root, entry);
165
+ return instrumentDigest(store, coordinate, standing, root ?? dirname(coordinate), releases).replace(/^[^:]*:/, '').slice(0, 12);
165
166
  }
166
167
  export function instrumentDigest(store, entry, standing = [], root = dirname(resolve(entry)), releases = []) {
167
168
  const seen = instrumentCoordinates(store, entry, standing, root, releases);
@@ -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
  }
@@ -52,3 +55,9 @@ export function authenticatedRootStanding(root) {
52
55
  }
53
56
  /** Legacy Root lines remain unchanged; contextual public output has its own carrier. */
54
57
  export const authenticatedStanding = authenticatedRootStanding;
58
+ /** Public current configuration omits withdrawal markers; typed effective history
59
+ * remains intact for the existing typed-object projection. Internal readers keep
60
+ * authenticatedStanding, including its historical markers. */
61
+ export function publicStandingLines(root) {
62
+ return authenticatedRootStanding(root).filter(line => !isConfig(line) || fieldOf(line, 'value') !== 'withdraw');
63
+ }
@@ -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";
@@ -5,7 +5,7 @@ import { commitPlaceAct, placeActResult, committedPlaceActResults } from "./port
5
5
  import { placesIn } from "../fold/places.js";
6
6
  import { appliedHistoryIn } from "../fold/signed.js";
7
7
  import { storeOf } from "../land/ledger.js";
8
- import { authenticatedRootStanding } from "./admitted-place.js";
8
+ import { publicStandingLines } from "./admitted-place.js";
9
9
  import { foldClaims, fieldOf } from "../fold/claims.js";
10
10
  const fail = (s) => { throw Error('REFUSE·contextual place ' + s); };
11
11
  function read(path, limit) {
@@ -73,5 +73,5 @@ export function contextualStanding(root) {
73
73
  }
74
74
  }
75
75
  const legacyPlaces = placesIn(root).map(place => { const history = appliedHistoryIn(root, place), folded = foldClaims(history); return { namespace: 'legacy-global', place: [place, 'TARGET.bound'].join('/'), standing: folded.standing.filter(line => fieldOf(line, 'value') !== 'withdraw'), history }; });
76
- return { profile: PLACE_ACT_CONTEXT, root: authenticatedRootStanding(root), legacyPlaces, contexts: acts.map(a => ({ identity: a.identity, context: a.bundle.context, receipts: a.bundle.receipts, authority: selected.authority(a.bundle.context) })), places: [...groups.values()].map(group => { const folded = foldClaims(group.lines); return { namespace: group.namespace, place: group.place, standing: folded.standing.filter(line => fieldOf(line, 'value') !== 'withdraw'), retracted: folded.retracted, superseded: folded.superseded, forks: folded.forks, contexts: group.contexts }; }) };
76
+ return { profile: PLACE_ACT_CONTEXT, root: publicStandingLines(root), legacyPlaces, contexts: acts.map(a => ({ identity: a.identity, context: a.bundle.context, receipts: a.bundle.receipts, authority: selected.authority(a.bundle.context) })), places: [...groups.values()].map(group => { const folded = foldClaims(group.lines); return { namespace: group.namespace, place: group.place, standing: folded.standing.filter(line => fieldOf(line, 'value') !== 'withdraw'), retracted: folded.retracted, superseded: folded.superseded, forks: folded.forks, contexts: group.contexts }; }) };
77
77
  }
package/dist/host/io.js CHANGED
@@ -7,5 +7,5 @@ export { basename, dirname, join, relative, resolve } from 'node:path';
7
7
  export { fileURLToPath } from 'node:url';
8
8
  export { parentPort, workerData } from 'node:worker_threads';
9
9
  export { gzipSync } from 'node:zlib';
10
- /** UTF-8 encoding for host byte formats. */
11
- export const encodeUTF8 = (text) => new TextEncoder().encode(text);
10
+ /** UTF-8 encoding is shared with host-free protocol ports. */
11
+ export { encodeUTF8 } from "../engine/encoding.js";
@@ -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
  }
@@ -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,40 @@
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 contracts?: {
13
+ readonly refusals: string;
14
+ readonly standing: string;
15
+ };
16
+ readonly objects: ObjectContext;
17
+ readonly maxBytes: number;
18
+ readonly maxRecords: number;
19
+ readonly digestBytes: (bytes: Uint8Array) => string | Promise<string>;
20
+ readonly verifyBytes: (bytes: string, key: string, signature: string, algorithms: readonly string[]) => boolean | Promise<boolean>;
21
+ readonly fetchBytes: (source: string, blob: string) => Promise<Uint8Array>;
22
+ }
23
+ /** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
24
+ export declare function inspectAuthenticatedReference(fragment: string, trust: ReferenceTrust): Promise<{
25
+ contracts?: {
26
+ refusals: string;
27
+ standing: string;
28
+ } | undefined;
29
+ reference: import("./reference-profile.ts").StandingReference;
30
+ records: readonly import("@lapxo/topos/wire").ObjectRecord[];
31
+ semanticIdentity: string;
32
+ authorityContextIdentity: string;
33
+ semanticStanding: string;
34
+ originalRecords: readonly string[];
35
+ }>;
36
+ export { encodeReference, decodeReference } from './reference-profile.ts';
37
+ export { BoundRefusal, readRefusalCatalog, describeRefusal } from './refusals.ts';
38
+ export type { RefusalEntry } from './refusals.ts';
39
+ export { readContent, retainContent, invokeSigningPort } from '../engine/ports.ts';
40
+ export type { PortLimits, ContentReadPort, PersistencePort, DigestPort, SigningPort, SigningLot, SignatureRecord } from '../engine/ports.ts';
@@ -0,0 +1,100 @@
1
+ import { BoundRefusal } from "./refusals.js";
2
+ import { parse, canonical, authorityOf, authorityAtEpoch, signedBytes, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
3
+ import { decodeReference } from "./reference-profile.js";
4
+ import { referenceStanding, referenceContracts, referenceContextBytes } from "./reference-standing.js";
5
+ function fail(code, why) { throw new BoundRefusal('authenticated-reference/' + code, 'REFUSE·authenticated-reference ' + why); }
6
+ const utf8 = (s) => new TextEncoder().encode(s);
7
+ /** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
8
+ export async function inspectAuthenticatedReference(fragment, trust) {
9
+ if (new Set(trust.signers.map(s => s.id)).size !== trust.signers.length)
10
+ fail('context', 'duplicate host signer');
11
+ const reference = decodeReference(fragment);
12
+ if (!Number.isSafeInteger(trust.maxBytes) || trust.maxBytes < 1 || !Number.isSafeInteger(trust.maxRecords) || trust.maxRecords < 1)
13
+ fail('budget', 'resource budget');
14
+ if (trust.contracts !== undefined) {
15
+ if (!referenceContracts(trust.contracts))
16
+ fail('context', 'contract identities');
17
+ const snapshot = await trust.digestBytes(referenceContextBytes({ ...trust, semanticIdentity: 'sha256:' + reference.digest, contracts: trust.contracts }));
18
+ if (snapshot !== trust.snapshot)
19
+ fail('context', 'context snapshot');
20
+ }
21
+ let received;
22
+ try {
23
+ received = await trust.fetchBytes(reference.source, reference.blob);
24
+ }
25
+ catch {
26
+ return fail('transport', 'content port failed');
27
+ }
28
+ if (received.byteLength > trust.maxBytes)
29
+ fail('budget', 'bytes budget');
30
+ if (typeof SharedArrayBuffer !== 'undefined' && received.buffer instanceof SharedArrayBuffer)
31
+ fail('transport', 'shared transport bytes');
32
+ const bytes = new Uint8Array(received);
33
+ if (await trust.digestBytes(bytes) !== reference.blob)
34
+ fail('integrity', 'blob integrity');
35
+ let text;
36
+ try {
37
+ text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
38
+ }
39
+ catch {
40
+ return fail('framing', 'carrier UTF-8');
41
+ }
42
+ let packet;
43
+ try {
44
+ packet = JSON.parse(text);
45
+ }
46
+ catch {
47
+ fail('framing', 'carrier framing');
48
+ }
49
+ if (!Array.isArray(packet) || packet.length !== 3 || packet[0] !== 'authenticated-standing@1' || !Array.isArray(packet[1]) || packet[1].length < 1 || packet[1].length > trust.maxRecords || packet[1].some((x) => typeof x !== 'string') || typeof packet[2] !== 'string' || JSON.stringify(packet) + '\n' !== text)
50
+ fail('framing', 'carrier framing');
51
+ const originals = packet[1], proofLine = packet[2];
52
+ const parsed = [...originals, proofLine].map(line => { const p = parse(line); if (p.kind !== 'fact' || canonical(p.value.fields) !== line)
53
+ fail('wire', 'original canonical'); return p.value.fields; });
54
+ const proof = parsed[parsed.length - 1];
55
+ if (proof.scope !== trust.proofScope || proof.at !== 'witness:' + trust.snapshot || proof.role !== 'writes' || proof.form !== 'alphabet' || proof.measure !== 'digest' || proof.epoch !== String(trust.epoch))
56
+ fail('context', 'untrusted snapshot binding');
57
+ if (proof.value !== await trust.digestBytes(utf8(JSON.stringify(['authenticated-standing@1', originals]) + '\n')))
58
+ fail('integrity', 'snapshot commitment');
59
+ // Async crypto port is called on exact native signed bytes; authorityOf consumes only those results.
60
+ const verifiedCrypto = new Set();
61
+ const key = (f, pub) => JSON.stringify([signedBytes(f), pub, f.sig, trust.algorithms]);
62
+ for (const f of parsed)
63
+ for (const s of trust.signers)
64
+ if (s.id === f.by && await trust.verifyBytes(signedBytes(f), s.publicKey, f.sig, trust.algorithms))
65
+ verifiedCrypto.add(key(f, s.publicKey));
66
+ const verifies = (f, pub) => verifiedCrypto.has(key(f, pub));
67
+ const authentic = authorityOf(proof, trust.signers, verifies);
68
+ if (authentic.kind !== 'admitted')
69
+ fail('authority', 'proof ' + authentic.kind + ' ' + authentic.why);
70
+ const signer = trust.signers.find(s => s.id === proof.by);
71
+ if (!signer)
72
+ fail('authority', 'proof signer');
73
+ const current = authorityAtEpoch(proof, signer, trust.epoch, trust.proofScope, 'authority');
74
+ if (current.kind !== 'admitted')
75
+ fail('authority', 'current proof ' + current.kind + ' ' + current.why);
76
+ const verified = new Set();
77
+ for (let i = 0; i < originals.length; i++) {
78
+ const admitted = authorityOf(parsed[i], trust.signers, verifies);
79
+ if (admitted.kind !== 'admitted')
80
+ fail('authority', 'original ' + admitted.kind + ' ' + admitted.why);
81
+ verified.add(originals[i]);
82
+ }
83
+ const history = objectHistory(originals);
84
+ if (history.kind !== 'fact')
85
+ fail('wire', 'wire ' + history.why);
86
+ if (history.value.configuration.some(f => f.value === 'withdraw'))
87
+ fail('history', 'configuration history requires admitted standing fold');
88
+ const context = { ...trust.objects, admit: (r) => verified.has(canonical(r.fields)) && trust.objects.admit(r) };
89
+ const checked = validateObjectContext(history.value.objects, context);
90
+ if (checked.kind !== 'fact')
91
+ throw new BoundRefusal('authenticated-reference/object-context', 'REFUSE·' + checked.why);
92
+ const semantic = referenceStanding(originals, checked.value), semanticIdentity = await trust.digestBytes(utf8(semantic));
93
+ if (semanticIdentity !== 'sha256:' + reference.digest)
94
+ fail('identity', 'standing identity');
95
+ const authorityContextIdentity = await trust.digestBytes(utf8(JSON.stringify(trust.contracts ? ['reference-inspection-trust@2', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms, trust.contracts] : ['reference-inspection-trust@1', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms])));
96
+ return { reference, records: checked.value, semanticIdentity, authorityContextIdentity, semanticStanding: semantic, originalRecords: originals, ...(trust.contracts ? { contracts: { ...trust.contracts } } : {}) };
97
+ }
98
+ export { encodeReference, decodeReference } from "./reference-profile.js";
99
+ export { BoundRefusal, readRefusalCatalog, describeRefusal } from "./refusals.js";
100
+ export { readContent, retainContent, invokeSigningPort } from "../engine/ports.js";
@@ -0,0 +1,6 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { sha256 } from "./hash.js";
3
+ /** Exact shipped interpretation contracts; this helper grants no authority. */
4
+ export function installedReferenceContracts() {
5
+ return { refusals: sha256(readFileSync(new URL('../../docs/contracts/refusals.bound', import.meta.url))), standing: sha256(readFileSync(new URL('../../docs/contracts/standing.bound', import.meta.url))) };
6
+ }
@@ -0,0 +1,33 @@
1
+ import type { SigningPort } from '../engine/ports.ts';
2
+ export { BoundRefusal as ReferenceExportRefusal } from './refusals.ts';
3
+ /** Signs a public snapshot proof; never lands, refreshes evidence or changes the Root. */
4
+ export declare function exportAuthenticatedReference(root: string, source: string, options?: {
5
+ readonly key?: string;
6
+ readonly keyFile?: string;
7
+ readonly signer?: string;
8
+ readonly hostSigner?: {
9
+ readonly name: string;
10
+ readonly sign: SigningPort;
11
+ };
12
+ }): Promise<{
13
+ profile: string;
14
+ reference: string;
15
+ carrier: string;
16
+ context: {
17
+ snapshot: string;
18
+ profile: string;
19
+ semanticIdentity: string;
20
+ epoch: number;
21
+ proofScope: string;
22
+ policy: string;
23
+ formImplementation: string;
24
+ signers: readonly import("@lapxo/topos/wire").Signer[];
25
+ algorithms: readonly string[];
26
+ contracts: {
27
+ refusals: string;
28
+ standing: string;
29
+ };
30
+ maxBytes: number;
31
+ maxRecords: number;
32
+ };
33
+ }>;