@lapxo/bound 0.2.0 → 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 (225) hide show
  1. package/README.md +40 -121
  2. package/TARGET.bound +51 -23
  3. package/dist/cli/standing-result.js +4 -3
  4. package/dist/cli/verb.js +36 -3
  5. package/dist/engine/encoding.js +2 -0
  6. package/dist/engine/index.d.ts +2 -0
  7. package/dist/engine/index.js +2 -0
  8. package/dist/engine/ports.d.ts +32 -0
  9. package/dist/engine/ports.js +103 -0
  10. package/dist/host/admitted-place.js +6 -0
  11. package/dist/host/contextual-place.js +2 -2
  12. package/dist/host/io.js +2 -2
  13. package/dist/host/reference-authenticated.d.ts +12 -0
  14. package/dist/host/reference-authenticated.js +47 -31
  15. package/dist/host/reference-contracts.js +6 -0
  16. package/dist/host/reference-export.d.ts +33 -0
  17. package/dist/host/reference-export.js +81 -0
  18. package/dist/host/reference-native-api.d.ts +3 -0
  19. package/dist/host/reference-native-api.js +3 -0
  20. package/dist/host/reference-native.d.ts +21 -1
  21. package/dist/host/reference-native.js +34 -10
  22. package/dist/host/reference-profile.js +2 -1
  23. package/dist/host/reference-standing.js +18 -0
  24. package/dist/host/refusals.d.ts +14 -0
  25. package/dist/host/refusals.js +43 -0
  26. package/dist/host/signing.js +7 -1
  27. package/dist/observe/files.js +11 -1
  28. package/dist/wasm/object.js +2 -2
  29. package/docs/contracts/authenticated-reference.publication.json +2 -2
  30. package/docs/contracts/host-ports.md +23 -0
  31. package/docs/contracts/host-ports.publication.json +25 -0
  32. package/docs/contracts/native-publication.inventory.json +7 -1
  33. package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
  34. package/docs/contracts/readme.publication.json +25 -0
  35. package/docs/contracts/reference-backend.md +24 -0
  36. package/docs/contracts/reference-backend.publication.json +25 -0
  37. package/docs/contracts/reference-export.md +31 -0
  38. package/docs/contracts/reference-export.publication.json +25 -0
  39. package/docs/contracts/reference-port.md +8 -1
  40. package/docs/contracts/reference-port.publication.json +1 -1
  41. package/docs/contracts/refusals.bound +94 -0
  42. package/docs/contracts/refusals.md +47 -0
  43. package/docs/contracts/refusals.publication.json +25 -0
  44. package/docs/contracts/standing.bound +9 -0
  45. package/docs/contracts/standing.md +24 -0
  46. package/docs/contracts/standing.publication.json +25 -0
  47. package/package.json +49 -4
  48. package/receipts.bound +9550 -7668
  49. package/samples/authenticated-reference/compatibility.json +306 -0
  50. package/samples/authenticated-reference/yes.json +2 -2
  51. package/samples/host-ports/no.json +16 -0
  52. package/samples/host-ports/yes.json +9 -0
  53. package/samples/readme/no.json +1 -0
  54. package/samples/readme/yes.json +1 -0
  55. package/samples/reference-backend/no.json +1 -0
  56. package/samples/reference-backend/world-fixture.json +8 -0
  57. package/samples/reference-backend/yes.json +1 -0
  58. package/samples/reference-export/no.json +5 -0
  59. package/samples/reference-export/yes.json +15 -0
  60. package/samples/reference-port/no/catalog/TARGET.bound +33 -0
  61. package/samples/reference-port/no/route-notes/TARGET.bound +33 -0
  62. package/samples/refusals/no.json +4 -0
  63. package/samples/refusals/yes.json +9 -0
  64. package/samples/standing/no.json +4 -0
  65. package/samples/standing/yes.json +21 -0
  66. package/vectors/native/authenticated-reference-formation.vector.json +55 -1
  67. package/vectors/native/authenticated-reference.vector.json +32 -396
  68. package/vectors/native/host-ports.vector.json +107 -0
  69. package/vectors/native/readme.vector.json +76 -0
  70. package/vectors/native/reference-backend.vector.json +84 -0
  71. package/vectors/native/reference-export.vector.json +78 -0
  72. package/vectors/native/refusals.vector.json +95 -0
  73. package/vectors/native/standing.vector.json +116 -0
  74. package/vectors/wasm-object.cli.json +16 -61
  75. package/dist/cli/accept.d.ts +0 -28
  76. package/dist/cli/act-result.d.ts +0 -12
  77. package/dist/cli/args.d.ts +0 -11
  78. package/dist/cli/custodian.d.ts +0 -1
  79. package/dist/cli/host.d.ts +0 -1
  80. package/dist/cli/names.d.ts +0 -25
  81. package/dist/cli/native-descendant.d.ts +0 -1
  82. package/dist/cli/native.d.ts +0 -1
  83. package/dist/cli/observe.d.ts +0 -1
  84. package/dist/cli/owner.d.ts +0 -37
  85. package/dist/cli/pass.d.ts +0 -6
  86. package/dist/cli/place.d.ts +0 -76
  87. package/dist/cli/release-result.d.ts +0 -5
  88. package/dist/cli/standing-result.d.ts +0 -5
  89. package/dist/cli/takes.d.ts +0 -30
  90. package/dist/fold/admission-history.d.ts +0 -6
  91. package/dist/fold/ceilings.d.ts +0 -24
  92. package/dist/fold/claims.d.ts +0 -29
  93. package/dist/fold/closed.d.ts +0 -15
  94. package/dist/fold/configures.d.ts +0 -4
  95. package/dist/fold/digests.d.ts +0 -29
  96. package/dist/fold/evidence.d.ts +0 -32
  97. package/dist/fold/fold-worker.d.ts +0 -1
  98. package/dist/fold/kept.d.ts +0 -29
  99. package/dist/fold/keys.d.ts +0 -17
  100. package/dist/fold/laid.d.ts +0 -17
  101. package/dist/fold/nos.d.ts +0 -8
  102. package/dist/fold/object.d.ts +0 -3
  103. package/dist/fold/observed.d.ts +0 -35
  104. package/dist/fold/opened.d.ts +0 -5
  105. package/dist/fold/own.d.ts +0 -24
  106. package/dist/fold/paid.d.ts +0 -11
  107. package/dist/fold/place-inputs.d.ts +0 -17
  108. package/dist/fold/places.d.ts +0 -24
  109. package/dist/fold/rank.d.ts +0 -3
  110. package/dist/fold/reach.d.ts +0 -18
  111. package/dist/fold/readers.d.ts +0 -47
  112. package/dist/fold/receipts.d.ts +0 -8
  113. package/dist/fold/region-declarations.d.ts +0 -2
  114. package/dist/fold/region.d.ts +0 -72
  115. package/dist/fold/release-policy.d.ts +0 -67
  116. package/dist/fold/resolved.d.ts +0 -18
  117. package/dist/fold/roles.d.ts +0 -17
  118. package/dist/fold/second.d.ts +0 -18
  119. package/dist/fold/shards.d.ts +0 -29
  120. package/dist/fold/signed.d.ts +0 -23
  121. package/dist/fold/signers.d.ts +0 -24
  122. package/dist/fold/stranger.d.ts +0 -3
  123. package/dist/fold/views.d.ts +0 -27
  124. package/dist/fold/wire.d.ts +0 -19
  125. package/dist/fold/zoom.d.ts +0 -16
  126. package/dist/host/act-cost.d.ts +0 -13
  127. package/dist/host/adapters/keys/ed25519.d.ts +0 -7
  128. package/dist/host/adapters/modules/javascript.d.ts +0 -3
  129. package/dist/host/adapters/transport/http.d.ts +0 -5
  130. package/dist/host/admitted-place.d.ts +0 -5
  131. package/dist/host/archive.d.ts +0 -11
  132. package/dist/host/capsule-process.d.ts +0 -21
  133. package/dist/host/capsule.d.ts +0 -19
  134. package/dist/host/cell-receipts.d.ts +0 -2
  135. package/dist/host/content-needed.d.ts +0 -5
  136. package/dist/host/content-store.d.ts +0 -8
  137. package/dist/host/contextual-place.d.ts +0 -59
  138. package/dist/host/digest.d.ts +0 -3
  139. package/dist/host/hash.d.ts +0 -9
  140. package/dist/host/io.d.ts +0 -11
  141. package/dist/host/lifetime.d.ts +0 -1
  142. package/dist/host/located.d.ts +0 -4
  143. package/dist/host/native/bounded-file.d.ts +0 -8
  144. package/dist/host/native/command.d.ts +0 -36
  145. package/dist/host/native/control-bridge-worker.d.ts +0 -1
  146. package/dist/host/native/control-bridge.d.ts +0 -30
  147. package/dist/host/native/control-selection.d.ts +0 -29
  148. package/dist/host/native/custody.d.ts +0 -5
  149. package/dist/host/native/deadline.d.ts +0 -12
  150. package/dist/host/native/descendants.d.ts +0 -1
  151. package/dist/host/native/generated-custodian.d.ts +0 -122
  152. package/dist/host/native/generated.d.ts +0 -217
  153. package/dist/host/native/image.d.ts +0 -36
  154. package/dist/host/native/jobs.d.ts +0 -75
  155. package/dist/host/native/loader.d.ts +0 -80
  156. package/dist/host/native/materialize.d.ts +0 -22
  157. package/dist/host/native/module-closure.d.ts +0 -27
  158. package/dist/host/native/module-syntax.d.ts +0 -8
  159. package/dist/host/native/native-binary.d.ts +0 -54
  160. package/dist/host/native/native-program.d.ts +0 -56
  161. package/dist/host/native/node-jobs.d.ts +0 -84
  162. package/dist/host/native/node-launch.d.ts +0 -72
  163. package/dist/host/native/preload.d.ts +0 -1
  164. package/dist/host/native/reading.d.ts +0 -91
  165. package/dist/host/native/receipt-environment.d.ts +0 -3
  166. package/dist/host/native/references.d.ts +0 -5
  167. package/dist/host/native/root.d.ts +0 -17
  168. package/dist/host/native/route.d.ts +0 -66
  169. package/dist/host/native/runtime.d.ts +0 -4
  170. package/dist/host/native-reader-context.d.ts +0 -3
  171. package/dist/host/objects.d.ts +0 -6
  172. package/dist/host/place-clock-selection.d.ts +0 -55
  173. package/dist/host/ports/ingress.d.ts +0 -24
  174. package/dist/host/ports/local-act.d.ts +0 -44
  175. package/dist/host/ports/place-act.d.ts +0 -46
  176. package/dist/host/ports/publication.d.ts +0 -10
  177. package/dist/host/ports/signing.d.ts +0 -31
  178. package/dist/host/ports/store.d.ts +0 -2
  179. package/dist/host/ports/transport.d.ts +0 -1
  180. package/dist/host/process-lifetime.d.ts +0 -11
  181. package/dist/host/provide.d.ts +0 -48
  182. package/dist/host/read-only.d.ts +0 -3
  183. package/dist/host/reading-closures.d.ts +0 -47
  184. package/dist/host/reading-context.d.ts +0 -29
  185. package/dist/host/reference.d.ts +0 -6
  186. package/dist/host/release.d.ts +0 -14
  187. package/dist/host/root-clock-source.d.ts +0 -85
  188. package/dist/host/selected-capsules.d.ts +0 -8
  189. package/dist/host/selected-topos.d.ts +0 -24
  190. package/dist/host/signing.d.ts +0 -3
  191. package/dist/host/source-content.d.ts +0 -12
  192. package/dist/host/timing.d.ts +0 -2
  193. package/dist/host/walk.d.ts +0 -6
  194. package/dist/judge/late.d.ts +0 -7
  195. package/dist/judge/take.d.ts +0 -59
  196. package/dist/judge/widening.d.ts +0 -1
  197. package/dist/land/act.d.ts +0 -30
  198. package/dist/land/alias.d.ts +0 -10
  199. package/dist/land/held.d.ts +0 -7
  200. package/dist/land/inert.d.ts +0 -26
  201. package/dist/land/leaving.d.ts +0 -14
  202. package/dist/land/ledger.d.ts +0 -33
  203. package/dist/land/owned.d.ts +0 -23
  204. package/dist/land/sign.d.ts +0 -15
  205. package/dist/land/standing-pin.d.ts +0 -3
  206. package/dist/land/vouched.d.ts +0 -21
  207. package/dist/land/withdraw.d.ts +0 -14
  208. package/dist/observe/files.d.ts +0 -14
  209. package/dist/observe/identity.d.ts +0 -21
  210. package/dist/observe/match.d.ts +0 -1
  211. package/dist/observe/regions.d.ts +0 -33
  212. package/dist/observe/run.d.ts +0 -31
  213. package/dist/observe/runner.d.ts +0 -20
  214. package/dist/render/capsule.d.ts +0 -24
  215. package/dist/render/field.d.ts +0 -3
  216. package/dist/render/names.d.ts +0 -3
  217. package/dist/render/open.d.ts +0 -27
  218. package/dist/render/page.d.ts +0 -3
  219. package/dist/render/receipts.d.ts +0 -3
  220. package/dist/render/shape.d.ts +0 -16
  221. package/dist/render/target.d.ts +0 -18
  222. package/dist/render/view.d.ts +0 -22
  223. package/dist/wasm/node-worker.d.ts +0 -1
  224. package/dist/wasm/object.d.ts +0 -8
  225. package/dist/wasm/worker.d.ts +0 -1
@@ -9,3 +9,5 @@
9
9
  export { objectFold } from './object.ts';
10
10
  export type { ObjectReading } from './object.ts';
11
11
  export type { ObjectRecord, ObjectContext } from '@lapxo/topos/wire';
12
+ export { readContent, retainContent, invokeSigningPort } from './ports.ts';
13
+ export type { PortLimits, ContentReadPort, PersistencePort, DigestPort, SigningPort, SigningLot, SignatureRecord } from './ports.ts';
@@ -7,3 +7,5 @@
7
7
  * Object operations delegate to the existing Obligations implementation.
8
8
  */
9
9
  export { objectFold } from "./object.js";
10
+ // Optional host operations are explicit and injected; objectFold itself never invokes them.
11
+ export { readContent, retainContent, invokeSigningPort } from "./ports.js";
@@ -0,0 +1,32 @@
1
+ export interface PortLimits {
2
+ readonly timeoutMs: number;
3
+ readonly maxBytes: number;
4
+ }
5
+ export interface SigningLot {
6
+ readonly bytes: readonly string[];
7
+ readonly keyId: string;
8
+ readonly algorithm: string;
9
+ }
10
+ export interface SignatureRecord {
11
+ readonly keyId: string;
12
+ readonly signature: string;
13
+ }
14
+ export type SigningPort = (lot: SigningLot, signal: AbortSignal) => Promise<readonly SignatureRecord[]>;
15
+ export type ContentReadPort = (source: string, digest: string, signal: AbortSignal) => Promise<Uint8Array>;
16
+ /** Atomic byte retention supplied by the host. This is neither admission nor a current-answer cache. */
17
+ export type PersistencePort = (digest: string, bytes: Uint8Array, signal: AbortSignal) => Promise<void>;
18
+ export type DigestPort = (bytes: Uint8Array) => string | Promise<string>;
19
+ export declare function readContent(source: string, digest: string, ports: {
20
+ readonly read: ContentReadPort;
21
+ readonly digestBytes: DigestPort;
22
+ }, limits: PortLimits, signal?: AbortSignal): Promise<Uint8Array>;
23
+ /** Verify before invoking retention; no operation automatically writes the projected result. */
24
+ export declare function retainContent(digest: string, bytes: Uint8Array, ports: {
25
+ readonly retain: PersistencePort;
26
+ readonly digestBytes: DigestPort;
27
+ }, limits: PortLimits, signal?: AbortSignal): Promise<{
28
+ readonly digest: string;
29
+ readonly bytes: number;
30
+ }>;
31
+ /** Acquire protocol-shaped signatures only; the existing consent path verifies them and decides authority. */
32
+ export declare function invokeSigningPort(lot: SigningLot, sign: SigningPort, limits: PortLimits, signal?: AbortSignal): Promise<readonly SignatureRecord[]>;
@@ -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
+ }
@@ -55,3 +55,9 @@ export function authenticatedRootStanding(root) {
55
55
  }
56
56
  /** Legacy Root lines remain unchanged; contextual public output has its own carrier. */
57
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
+ }
@@ -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";
@@ -9,6 +9,10 @@ export interface ReferenceTrust {
9
9
  readonly formImplementation: string;
10
10
  readonly signers: readonly Signer[];
11
11
  readonly algorithms: readonly string[];
12
+ readonly contracts?: {
13
+ readonly refusals: string;
14
+ readonly standing: string;
15
+ };
12
16
  readonly objects: ObjectContext;
13
17
  readonly maxBytes: number;
14
18
  readonly maxRecords: number;
@@ -18,6 +22,10 @@ export interface ReferenceTrust {
18
22
  }
19
23
  /** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
20
24
  export declare function inspectAuthenticatedReference(fragment: string, trust: ReferenceTrust): Promise<{
25
+ contracts?: {
26
+ refusals: string;
27
+ standing: string;
28
+ } | undefined;
21
29
  reference: import("./reference-profile.ts").StandingReference;
22
30
  records: readonly import("@lapxo/topos/wire").ObjectRecord[];
23
31
  semanticIdentity: string;
@@ -26,3 +34,7 @@ export declare function inspectAuthenticatedReference(fragment: string, trust: R
26
34
  originalRecords: readonly string[];
27
35
  }>;
28
36
  export { encodeReference, decodeReference } from './reference-profile.ts';
37
+ export { BoundRefusal, readRefusalCatalog, describeRefusal } from './refusals.ts';
38
+ export type { RefusalEntry } from './refusals.ts';
39
+ export { readContent, retainContent, invokeSigningPort } from '../engine/ports.ts';
40
+ export type { PortLimits, ContentReadPort, PersistencePort, DigestPort, SigningPort, SigningLot, SignatureRecord } from '../engine/ports.ts';
@@ -1,42 +1,61 @@
1
+ import { BoundRefusal } from "./refusals.js";
1
2
  import { parse, canonical, authorityOf, authorityAtEpoch, signedBytes, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
2
- import { cellInputs } from '@lapxo/topos/cell-inputs';
3
- import { semanticStandingBytes } from '@lapxo/topos/standing';
4
3
  import { decodeReference } from "./reference-profile.js";
5
- function fail(why) { throw Error('REFUSE·authenticated-reference ' + why); }
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
6
  const utf8 = (s) => new TextEncoder().encode(s);
7
7
  /** Prospective authenticated-standing@1 host composition; no effects and no authority from transport. */
8
8
  export async function inspectAuthenticatedReference(fragment, trust) {
9
9
  if (new Set(trust.signers.map(s => s.id)).size !== trust.signers.length)
10
- fail('duplicate host signer');
10
+ fail('context', 'duplicate host signer');
11
11
  const reference = decodeReference(fragment);
12
12
  if (!Number.isSafeInteger(trust.maxBytes) || trust.maxBytes < 1 || !Number.isSafeInteger(trust.maxRecords) || trust.maxRecords < 1)
13
- fail('resource budget');
14
- const received = await trust.fetchBytes(reference.source, reference.blob);
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
+ }
15
28
  if (received.byteLength > trust.maxBytes)
16
- fail('bytes budget');
29
+ fail('budget', 'bytes budget');
17
30
  if (typeof SharedArrayBuffer !== 'undefined' && received.buffer instanceof SharedArrayBuffer)
18
- fail('shared transport bytes');
31
+ fail('transport', 'shared transport bytes');
19
32
  const bytes = new Uint8Array(received);
20
33
  if (await trust.digestBytes(bytes) !== reference.blob)
21
- fail('blob integrity');
22
- const text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
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
+ }
23
42
  let packet;
24
43
  try {
25
44
  packet = JSON.parse(text);
26
45
  }
27
46
  catch {
28
- fail('carrier framing');
47
+ fail('framing', 'carrier framing');
29
48
  }
30
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)
31
- fail('carrier framing');
50
+ fail('framing', 'carrier framing');
32
51
  const originals = packet[1], proofLine = packet[2];
33
52
  const parsed = [...originals, proofLine].map(line => { const p = parse(line); if (p.kind !== 'fact' || canonical(p.value.fields) !== line)
34
- fail('original canonical'); return p.value.fields; });
53
+ fail('wire', 'original canonical'); return p.value.fields; });
35
54
  const proof = parsed[parsed.length - 1];
36
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))
37
- fail('untrusted snapshot binding');
56
+ fail('context', 'untrusted snapshot binding');
38
57
  if (proof.value !== await trust.digestBytes(utf8(JSON.stringify(['authenticated-standing@1', originals]) + '\n')))
39
- fail('snapshot commitment');
58
+ fail('integrity', 'snapshot commitment');
40
59
  // Async crypto port is called on exact native signed bytes; authorityOf consumes only those results.
41
60
  const verifiedCrypto = new Set();
42
61
  const key = (f, pub) => JSON.stringify([signedBytes(f), pub, f.sig, trust.algorithms]);
@@ -47,38 +66,35 @@ export async function inspectAuthenticatedReference(fragment, trust) {
47
66
  const verifies = (f, pub) => verifiedCrypto.has(key(f, pub));
48
67
  const authentic = authorityOf(proof, trust.signers, verifies);
49
68
  if (authentic.kind !== 'admitted')
50
- fail('proof ' + authentic.kind + ' ' + authentic.why);
69
+ fail('authority', 'proof ' + authentic.kind + ' ' + authentic.why);
51
70
  const signer = trust.signers.find(s => s.id === proof.by);
52
71
  if (!signer)
53
- fail('proof signer');
72
+ fail('authority', 'proof signer');
54
73
  const current = authorityAtEpoch(proof, signer, trust.epoch, trust.proofScope, 'authority');
55
74
  if (current.kind !== 'admitted')
56
- fail('current proof ' + current.kind + ' ' + current.why);
75
+ fail('authority', 'current proof ' + current.kind + ' ' + current.why);
57
76
  const verified = new Set();
58
77
  for (let i = 0; i < originals.length; i++) {
59
78
  const admitted = authorityOf(parsed[i], trust.signers, verifies);
60
79
  if (admitted.kind !== 'admitted')
61
- fail('original ' + admitted.kind + ' ' + admitted.why);
80
+ fail('authority', 'original ' + admitted.kind + ' ' + admitted.why);
62
81
  verified.add(originals[i]);
63
82
  }
64
83
  const history = objectHistory(originals);
65
84
  if (history.kind !== 'fact')
66
- fail('wire ' + history.why);
85
+ fail('wire', 'wire ' + history.why);
67
86
  if (history.value.configuration.some(f => f.value === 'withdraw'))
68
- fail('configuration history requires admitted standing fold');
87
+ fail('history', 'configuration history requires admitted standing fold');
69
88
  const context = { ...trust.objects, admit: (r) => verified.has(canonical(r.fields)) && trust.objects.admit(r) };
70
89
  const checked = validateObjectContext(history.value.objects, context);
71
90
  if (checked.kind !== 'fact')
72
- throw Error('REFUSE·' + checked.why);
73
- const membership = new Set([...cellInputs(checked.value, 'semantic-live@2').values()].flat().map(l => semanticStandingBytes([l])));
74
- const fields = (line) => { const p = parse(line); if (p.kind !== 'fact')
75
- fail('original parse'); return p.value.fields; };
76
- const effective = [...originals].sort((a, b) => Number(fields(a).epoch) - Number(fields(b).epoch));
77
- const live = effective.filter(line => { const f = fields(line); return f.type === undefined || membership.has(semanticStandingBytes([line])); });
78
- const semantic = semanticStandingBytes(live), semanticIdentity = await trust.digestBytes(utf8(semantic));
91
+ throw new BoundRefusal('authenticated-reference/object-context', 'REFUSE·' + checked.why);
92
+ const semantic = referenceStanding(originals, checked.value), semanticIdentity = await trust.digestBytes(utf8(semantic));
79
93
  if (semanticIdentity !== 'sha256:' + reference.digest)
80
- fail('standing identity');
81
- const authorityContextIdentity = await trust.digestBytes(utf8(JSON.stringify(['reference-inspection-trust@1', trust.snapshot, trust.epoch, trust.proofScope, trust.policy, trust.formImplementation, trust.signers, trust.algorithms])));
82
- return { reference, records: checked.value, semanticIdentity, authorityContextIdentity, semanticStanding: semantic, originalRecords: originals };
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 } } : {}) };
83
97
  }
84
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
+ }>;
@@ -0,0 +1,81 @@
1
+ import { installedReferenceContracts } from "./reference-contracts.js";
2
+ import { BoundRefusal, describeRefusal, readRefusalCatalog } from "./refusals.js";
3
+ import { readFileSync } from 'node:fs';
4
+ import { authorityAtEpoch, canonical, parse } from '@lapxo/topos/wire';
5
+ import { sha256 as hash } from "./hash.js";
6
+ import { signerFor } from "./signing.js";
7
+ import { signConsentLot } from "../land/sign.js";
8
+ import { underRootAuthority } from "../land/act.js";
9
+ import { storeOf } from "../land/ledger.js";
10
+ import { authorityFor, rootSigner } from "../fold/signers.js";
11
+ import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
12
+ import { authenticatedRootStanding } from "./admitted-place.js";
13
+ import { eraOf } from "../fold/digests.js";
14
+ import { epochOf } from "../cli/owner.js";
15
+ import { fieldOf } from "../fold/claims.js";
16
+ import { prepareNativeReference } from "./reference-native.js";
17
+ import { referenceContextBytes, referenceContracts, referenceStanding } from "./reference-standing.js";
18
+ import { encodeReference } from "./reference-profile.js";
19
+ const catalog = () => readFileSync(new URL('../../docs/contracts/refusals.bound', import.meta.url), 'utf8');
20
+ export { BoundRefusal as ReferenceExportRefusal } from "./refusals.js";
21
+ function fail(code) {
22
+ const refusal = new BoundRefusal('reference-export/' + code, 'REFUSE·reference-export/' + code);
23
+ const entry = describeRefusal(refusal, readRefusalCatalog(catalog()));
24
+ throw new BoundRefusal(entry.code, 'REFUSE·' + entry.code + ' ' + entry.meaning);
25
+ }
26
+ /** Signs a public snapshot proof; never lands, refreshes evidence or changes the Root. */
27
+ export async function exportAuthenticatedReference(root, source, options = {}) {
28
+ try {
29
+ encodeReference({ class: 'standing', algorithm: 'sha256', digest: '0'.repeat(64), blob: 'sha256:' + '0'.repeat(64), source });
30
+ }
31
+ catch {
32
+ fail('source');
33
+ }
34
+ return underRootAuthority(storeOf(root), 'reference export', async () => {
35
+ const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER), standing = authenticatedRootStanding(root), epoch = epochOf(history), era = eraOf(standing);
36
+ const authority = authorityFor(history, admissionAnchor(root), era.admitted);
37
+ const one = (scope, measure = 'id', form = 'alphabet') => { const rows = standing.filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'value') !== 'withdraw'); if (rows.length !== 1 || fieldOf(rows[0], 'role') !== 'writes' || fieldOf(rows[0], 'measure') !== measure || fieldOf(rows[0], 'form') !== form || authority.of(rows[0]).kind !== 'admitted')
38
+ fail('context'); return fieldOf(rows[0], 'value'); };
39
+ if (one('reference/authentication') !== 'authenticated-standing@1')
40
+ fail('context');
41
+ const proofScope = one('reference/proof-scope'), contracts = { refusals: one('reference/contracts/refusals'), standing: one('reference/contracts/standing') };
42
+ const installed = installedReferenceContracts();
43
+ if (!referenceContracts(contracts) || contracts.refusals !== installed.refusals || contracts.standing !== installed.standing)
44
+ fail('context');
45
+ const count = one('reference/records'), maxRecords = Number(count), range = /^(0|[1-9][0-9]*)\.\.(0|[1-9][0-9]*)$/.exec(one('reference/export-bytes', 'bytes', 'interval'));
46
+ if (!/^(0|[1-9][0-9]*)$/.test(count) || !Number.isSafeInteger(maxRecords) || maxRecords < 1 || !range || Number(range[1]) > Number(range[2]) || !Number.isSafeInteger(Number(range[2])) || Number(range[2]) < 1)
47
+ fail('budget');
48
+ const maxBytes = Number(range[2]);
49
+ const originals = standing.filter(line => fieldOf(line, 'value') !== 'withdraw' || fieldOf(line, 'type') !== '');
50
+ if (!originals.length || originals.length > maxRecords || Buffer.byteLength(originals.join('\n')) > maxBytes)
51
+ fail('budget');
52
+ if (originals.some(line => authority.of(line).kind !== 'admitted'))
53
+ fail('history');
54
+ const key = options.key ?? rootSigner(root).id, signer = authority.admitted.find(s => s.id === key);
55
+ const stamp = canonical({ scope: proofScope, role: 'writes', form: 'alphabet', measure: 'digest', value: hash(JSON.stringify(['authenticated-standing@1', originals]) + '\n'), by: 'target', at: 'witness:' + 'sha256:' + '0'.repeat(64) });
56
+ const probe = parse(stamp);
57
+ if (!signer || probe.kind !== 'fact' || authorityAtEpoch({ ...probe.value.fields, by: key, epoch: String(epoch) }, signer, epoch, proofScope, 'authority').kind !== 'admitted')
58
+ fail('authority');
59
+ const held = await prepareNativeReference(root), semantic = referenceStanding(originals, held.records), semanticIdentity = hash(semantic);
60
+ const context = { profile: 'reference-export-context@1', semanticIdentity, epoch, proofScope, policy: held.policy, formImplementation: held.formImplementation, signers: authority.admitted, algorithms: era.admitted, contracts, maxBytes, maxRecords };
61
+ const snapshot = hash(referenceContextBytes(context));
62
+ if (Buffer.byteLength(JSON.stringify({ originals, context, snapshot }) + '\n') > maxBytes)
63
+ fail('budget');
64
+ const drafted = canonical({ ...probe.value.fields, at: 'witness:' + snapshot });
65
+ let signed;
66
+ try {
67
+ signed = await signConsentLot([drafted], { epoch, by: key, algorithm: era.era }, signer.publicKey, era.admitted, signerFor(standing, key, options.signer, options.keyFile, options.hostSigner));
68
+ }
69
+ catch {
70
+ fail('signer');
71
+ }
72
+ if (signed.kind !== 'fact')
73
+ fail('signer');
74
+ const carrier = JSON.stringify(['authenticated-standing@1', originals, signed.lines[0]]) + '\n';
75
+ const reference = encodeReference({ class: 'standing', algorithm: 'sha256', digest: semanticIdentity.slice(7), blob: hash(carrier), source });
76
+ const result = { profile: 'reference-export@1', reference, carrier, context: { ...context, snapshot } };
77
+ if (Buffer.byteLength(JSON.stringify(result) + '\n') > maxBytes)
78
+ fail('budget');
79
+ return result;
80
+ });
81
+ }
@@ -0,0 +1,3 @@
1
+ /** Native host boundary. The platform-free reference inspector remains /reference. */
2
+ export { openAuthenticatedReference } from './reference-native.ts';
3
+ export { exportAuthenticatedReference } from './reference-export.ts';
@@ -0,0 +1,3 @@
1
+ /** Native host boundary. The platform-free reference inspector remains /reference. */
2
+ export { openAuthenticatedReference } from "./reference-native.js";
3
+ export { exportAuthenticatedReference } from "./reference-export.js";
@@ -1,6 +1,26 @@
1
+ import { type ContentReadPort } from '../engine/ports.ts';
1
2
  export declare function authenticatedReferenceSelected(standing: readonly string[]): boolean;
2
- export declare function openAuthenticatedReference(root: string, fragment: string): Promise<{
3
+ /** One context preparation shared by native export and native inspection. */
4
+ export declare function prepareNativeReference(root: string): Promise<{
5
+ policy: string;
6
+ formImplementation: string;
7
+ records: readonly import("@lapxo/topos/wire").ObjectRecord[];
8
+ context: import("@lapxo/topos/wire").ObjectContext;
9
+ configuration: Readonly<Record<string, string>>[];
10
+ }>;
11
+ export declare function openAuthenticatedReference(root: string, fragment: string, options?: {
12
+ readonly signal?: AbortSignal;
13
+ readonly content?: {
14
+ readonly name: string;
15
+ readonly read: ContentReadPort;
16
+ };
17
+ }): Promise<{
18
+ backend: string;
3
19
  readings: readonly import("../engine/object.ts").ObjectReading[];
20
+ contracts?: {
21
+ refusals: string;
22
+ standing: string;
23
+ } | undefined;
4
24
  reference: import("./reference-profile.ts").StandingReference;
5
25
  records: readonly import("@lapxo/topos/wire").ObjectRecord[];
6
26
  semanticIdentity: string;
@@ -1,3 +1,7 @@
1
+ import { readContent } from "../engine/ports.js";
2
+ import { installedReferenceContracts } from "./reference-contracts.js";
3
+ import { BoundRefusal } from "./refusals.js";
4
+ import { foldObjectsWithWasm } from "../wasm/object.js";
1
5
  /** Native ports for authenticated #bound opening; trusted policy belongs to the selected local place. */
2
6
  import { sha256 as hash, isSha256 } from "./hash.js";
3
7
  import { canonical, parse, transportSelection } from '@lapxo/topos/wire';
@@ -22,29 +26,49 @@ export function authenticatedReferenceSelected(standing) {
22
26
  throw Error('REFUSE·reference conflicting or unknown authentication profile');
23
27
  return true;
24
28
  }
25
- export async function openAuthenticatedReference(root, fragment) {
29
+ /** One context preparation shared by native export and native inspection. */
30
+ export async function prepareNativeReference(root) {
31
+ const store = storeOf(root), held = await admittedObjects(root, signedOwnerLines(store), () => { }, true);
32
+ const policy = held.configuration.map(fields => canonical(fields));
33
+ const implementations = [...new Set(held.records.filter(r => r.record === 'cell').map(r => r.fields.topos))].sort().map(pin => { const selected = toposAt(pin, store), selectors = selected.parts.filter(f => f.scope === 'wire/topos/context-view'); if (selectors.length !== 1)
34
+ throw Error('REFUSE·reference host context selector'); const offers = selected.parts.filter(f => f.kind === 'check' && f.view === selectors[0].value); if (offers.length !== 1)
35
+ throw Error('REFUSE·reference host context offer'); const offer = offers[0], closure = [...new Set((offer.restsOn ?? '').split('|').filter(Boolean))].sort(); for (const digest of closure)
36
+ contentAt(digest, store); return [pin, offer.value, closure]; });
37
+ return { ...held, policy: hash(semanticStandingBytes(policy)), formImplementation: hash(JSON.stringify(implementations)) };
38
+ }
39
+ export async function openAuthenticatedReference(root, fragment, options = {}) {
26
40
  const store = storeOf(root), standing = lockStanding(store), era = eraOf(standing), authority = authorityFor(lockLines(store), rootSigner(root), era.admitted);
27
41
  if (!authenticatedReferenceSelected(standing))
28
42
  throw Error('REFUSE·reference no admitted authenticated-standing@1 profile');
29
- const one = (scope) => { const found = standing.flatMap(line => { const p = parse(line); return p.kind === 'fact' && p.value.fields.scope === scope ? [{ line, fields: p.value.fields }] : []; }); if (found.length !== 1 || found[0].fields.role !== 'writes' || found[0].fields.measure !== 'id' || authority.of(found[0].line).kind !== 'admitted')
30
- throw Error('REFUSE·reference missing, conflicting or unauthenticated ' + scope); return found[0].fields.value; };
43
+ const one = (scope) => { const found = standing.flatMap(line => { const p = parse(line); return p.kind === 'fact' && p.value.fields.scope === scope ? [{ line, fields: p.value.fields }] : []; }); if (found.length !== 1 || found[0].fields.role !== 'writes' || found[0].fields.measure !== 'id' || found[0].fields.form !== 'alphabet' || authority.of(found[0].line).kind !== 'admitted')
44
+ throw new BoundRefusal(scope === 'engine/object' ? 'reference/backend' : 'authenticated-reference/context', 'REFUSE·reference missing, conflicting or unauthenticated ' + scope); return found[0].fields.value; };
31
45
  if (one('reference/authentication') !== 'authenticated-standing@1')
32
46
  throw Error('REFUSE·reference authentication profile');
33
47
  const snapshot = one('reference/snapshot'), epochText = one('reference/epoch'), proofScope = one('reference/proof-scope'), recordsText = one('reference/records');
34
48
  if (!isSha256(snapshot) || !/^\d+$/.test(epochText) || !Number.isSafeInteger(Number(epochText)) || !/^\d+$/.test(recordsText) || !Number.isSafeInteger(Number(recordsText)) || Number(recordsText) < 1 || !proofScope)
35
49
  throw Error('REFUSE·reference authority snapshot or resource contract');
50
+ const backend = one('engine/object');
51
+ if (!['esm@1', 'wasm@1'].includes(backend))
52
+ throw new BoundRefusal('reference/backend', 'REFUSE·reference unsupported declared backend');
53
+ if (options.signal?.aborted)
54
+ throw new BoundRefusal('reference/cancelled', 'REFUSE·reference cancelled');
36
55
  const reference = decodeReference(fragment);
37
56
  // Provider selection is made from HOST-declared cells/configuration, never carrier-picked code.
38
- const held = await admittedObjects(root, signedOwnerLines(store), () => { }, true);
57
+ const held = await prepareNativeReference(root);
39
58
  if (!held.records.some(r => r.record === 'cell'))
40
59
  throw Error('REFUSE·reference host has no declared object interpretation');
41
60
  const context = held.context, policy = held.configuration.map(fields => canonical(fields)), selection = transportSelection(held.configuration, new URL(reference.source).protocol.slice(0, -1));
42
61
  if (!selection)
43
62
  throw Error('REFUSE·reference no admitted transport policy');
44
- const implementations = [...new Set(held.records.filter(r => r.record === 'cell').map(r => r.fields.topos))].sort().map(pin => { const selected = toposAt(pin, store), selectors = selected.parts.filter(f => f.scope === 'wire/topos/context-view'); if (selectors.length !== 1)
45
- throw Error('REFUSE·reference host context selector'); const offers = selected.parts.filter(f => f.kind === 'check' && f.view === selectors[0].value); if (offers.length !== 1)
46
- throw Error('REFUSE·reference host context offer'); const offer = offers[0], closure = [...new Set((offer.restsOn ?? '').split('|').filter(Boolean))].sort(); for (const digest of closure)
47
- contentAt(digest, store); return [pin, offer.value, closure]; });
48
- const result = await inspectAuthenticatedReference(fragment, { snapshot, epoch: Number(epochText), proofScope, policy: hash(semanticStandingBytes(policy)), formImplementation: hash(JSON.stringify(implementations)), signers: authority.admitted, algorithms: era.admitted, objects: context, maxBytes: selection.responseBytes, maxRecords: Number(recordsText), digestBytes: hash, verifyBytes, fetchBytes: (source, blob) => transportBytes(source, blob, policy) });
49
- return { ...result, readings: objectFold(result.records, context) };
63
+ if (options.content && options.content.name !== selection.name)
64
+ throw new BoundRefusal('authenticated-reference/transport', 'REFUSE·reference injected content binding conflicts with declaration');
65
+ const contracts = { refusals: one('reference/contracts/refusals'), standing: one('reference/contracts/standing') }, expectedPolicy = one('reference/policy'), expectedImplementation = one('reference/form-implementation');
66
+ const installed = installedReferenceContracts();
67
+ if (contracts.refusals !== installed.refusals || contracts.standing !== installed.standing || !isSha256(expectedPolicy) || expectedImplementation !== held.formImplementation)
68
+ throw new BoundRefusal('authenticated-reference/context', 'REFUSE·reference expected interpretation differs');
69
+ const result = await inspectAuthenticatedReference(fragment, { snapshot, epoch: Number(epochText), proofScope, contracts, policy: expectedPolicy, formImplementation: expectedImplementation, signers: authority.admitted, algorithms: era.admitted, objects: context, maxBytes: selection.responseBytes, maxRecords: Number(recordsText), digestBytes: hash, verifyBytes, fetchBytes: (source, blob) => options.content ? readContent(source, blob, { read: options.content.read, digestBytes: hash }, { timeoutMs: selection.timeoutMs, maxBytes: selection.responseBytes }, options.signal) : transportBytes(source, blob, policy) });
70
+ if (options.signal?.aborted)
71
+ throw new BoundRefusal('reference/cancelled', 'REFUSE·reference cancelled');
72
+ const readings = backend === 'wasm@1' ? await foldObjectsWithWasm(root, result.records, context, held.configuration, options.signal) : objectFold(result.records, context);
73
+ return { ...result, backend, readings };
50
74
  }
@@ -1,5 +1,6 @@
1
+ import { BoundRefusal } from "./refusals.js";
1
2
  const hex = /^[0-9a-f]{64}$/;
2
- const refuse = (cause) => { throw Error(`REFUSE·reference ${cause}`); };
3
+ const refuse = (cause) => { throw new BoundRefusal('reference/format', `REFUSE·reference ${cause}`); };
3
4
  const keys = ['class', 'algorithm', 'digest', 'blob', 'source'];
4
5
  function check(reference) {
5
6
  if (Object.keys(reference).length !== keys.length || keys.some(key => !Object.prototype.hasOwnProperty.call(reference, key)))
@@ -0,0 +1,18 @@
1
+ import { parse } from '@lapxo/topos/wire';
2
+ import { cellInputs } from '@lapxo/topos/cell-inputs';
3
+ import { semanticStandingBytes } from '@lapxo/topos/standing';
4
+ /** Shared projection after admission; transport order never replaces effective order. */
5
+ export function referenceStanding(originals, records) {
6
+ const membership = new Set([...cellInputs(records, 'semantic-live@2').values()].flat().map(line => semanticStandingBytes([line])));
7
+ const fields = (line) => { const p = parse(line); if (p.kind !== 'fact')
8
+ throw Error('REFUSE·authenticated-reference original parse'); return p.value.fields; };
9
+ const effective = [...originals].sort((a, b) => Number(fields(a).epoch) - Number(fields(b).epoch));
10
+ return semanticStandingBytes(effective.filter(line => fields(line).type === undefined || membership.has(semanticStandingBytes([line]))));
11
+ }
12
+ export function referenceContracts(value) {
13
+ return value !== null && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 2 && /^sha256:[a-f0-9]{64}$/.test(value.refusals) && /^sha256:[a-f0-9]{64}$/.test(value.standing);
14
+ }
15
+ /** This versioned context frame binds interpretation contracts without changing wire fields. */
16
+ export function referenceContextBytes(context) {
17
+ return new TextEncoder().encode(JSON.stringify(['reference-export-context@1', context.semanticIdentity, context.epoch, context.proofScope, context.policy, context.formImplementation, context.signers, context.algorithms, context.contracts.refusals, context.contracts.standing, context.maxBytes, context.maxRecords]) + '\n');
18
+ }