@lapxo/bound 0.2.3 → 0.2.5

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 (242) hide show
  1. package/CITATION.cff +19 -0
  2. package/TARGET.bound +95 -39
  3. package/dist/cli/accept.js +72 -558
  4. package/dist/cli/act-result.js +3 -103
  5. package/dist/cli/args.js +3 -44
  6. package/dist/cli/custodian.js +2 -29
  7. package/dist/cli/host.js +4 -32
  8. package/dist/cli/names.js +1 -12
  9. package/dist/cli/native-descendant.js +1 -19
  10. package/dist/cli/native.js +3 -60
  11. package/dist/cli/observe.js +1 -13
  12. package/dist/cli/owner.js +3 -139
  13. package/dist/cli/pass.js +21 -321
  14. package/dist/cli/place.js +6 -281
  15. package/dist/cli/release-result.js +3 -80
  16. package/dist/cli/retained-judgment.js +1 -25
  17. package/dist/cli/standing-result.js +4 -21
  18. package/dist/cli/takes.js +10 -111
  19. package/dist/cli/verb.d.ts +1 -2
  20. package/dist/cli/verb.js +83 -683
  21. package/dist/engine/computation.js +2 -0
  22. package/dist/engine/conformance.js +4 -0
  23. package/dist/engine/encoding.js +1 -2
  24. package/dist/engine/index.d.ts +1 -13
  25. package/dist/engine/index.js +2 -11
  26. package/dist/engine/object.js +1 -38
  27. package/dist/engine/ports.js +2 -103
  28. package/dist/fold/admission-history.js +1 -17
  29. package/dist/fold/bootstrap-anchor.js +1 -28
  30. package/dist/fold/ceilings.js +1 -142
  31. package/dist/fold/claims.js +2 -131
  32. package/dist/fold/closed.js +24 -234
  33. package/dist/fold/configures.js +1 -25
  34. package/dist/fold/digests.js +10 -226
  35. package/dist/fold/evidence.js +1 -22
  36. package/dist/fold/fold-worker.js +1 -13
  37. package/dist/fold/kept.js +12 -173
  38. package/dist/fold/keys.js +3 -91
  39. package/dist/fold/laid.js +3 -90
  40. package/dist/fold/nos.js +1 -48
  41. package/dist/fold/object.js +1 -2
  42. package/dist/fold/observed.js +27 -534
  43. package/dist/fold/opened.js +1 -33
  44. package/dist/fold/own.js +3 -217
  45. package/dist/fold/paid.js +1 -92
  46. package/dist/fold/place-inputs.js +1 -70
  47. package/dist/fold/places.js +3 -100
  48. package/dist/fold/rank.js +1 -9
  49. package/dist/fold/reach.js +1 -83
  50. package/dist/fold/readers.js +6 -142
  51. package/dist/fold/receipts.js +1 -47
  52. package/dist/fold/region-declarations.js +1 -13
  53. package/dist/fold/region.js +8 -286
  54. package/dist/fold/release-policy.js +1 -101
  55. package/dist/fold/resolved.js +6 -145
  56. package/dist/fold/roles.js +2 -59
  57. package/dist/fold/second.js +4 -71
  58. package/dist/fold/shards.js +12 -100
  59. package/dist/fold/signed.js +3 -81
  60. package/dist/fold/signers.js +1 -54
  61. package/dist/fold/stranger.js +6 -24
  62. package/dist/fold/views.js +1 -66
  63. package/dist/fold/wire.js +1 -46
  64. package/dist/fold/zoom.js +3 -81
  65. package/dist/host/act-cost.js +1 -25
  66. package/dist/host/adapters/keys/ed25519.js +1 -30
  67. package/dist/host/adapters/modules/javascript.js +1 -50
  68. package/dist/host/adapters/transport/http.js +1 -28
  69. package/dist/host/admitted-place.js +1 -63
  70. package/dist/host/archive.js +1 -152
  71. package/dist/host/call-context.js +1 -0
  72. package/dist/host/capsule-process.js +2 -61
  73. package/dist/host/capsule.js +9 -168
  74. package/dist/host/cell-receipts.js +12 -128
  75. package/dist/host/commit-result.js +1 -0
  76. package/dist/host/content-needed.js +1 -8
  77. package/dist/host/content-store.js +1 -31
  78. package/dist/host/contextual-place.js +2 -77
  79. package/dist/host/digest.js +2 -15
  80. package/dist/host/hash.js +1 -9
  81. package/dist/host/host-controls.js +1 -0
  82. package/dist/host/in-process-act.js +3 -0
  83. package/dist/host/in-process-context.js +3 -0
  84. package/dist/host/in-process-root.js +6 -0
  85. package/dist/host/in-process.js +4 -0
  86. package/dist/host/index.d.ts +1 -0
  87. package/dist/host/index.js +1 -0
  88. package/dist/host/io.js +1 -11
  89. package/dist/host/lifetime.js +1 -79
  90. package/dist/host/located.js +6 -105
  91. package/dist/host/native/bounded-file.js +1 -32
  92. package/dist/host/native/command.js +1 -91
  93. package/dist/host/native/control-bridge-worker.js +2 -88
  94. package/dist/host/native/control-bridge.js +1 -110
  95. package/dist/host/native/control-selection.js +1 -32
  96. package/dist/host/native/custody.js +1 -91
  97. package/dist/host/native/deadline.js +1 -28
  98. package/dist/host/native/descendants.js +1 -528
  99. package/dist/host/native/generated-custodian.js +5 -267
  100. package/dist/host/native/generated.js +1 -379
  101. package/dist/host/native/image.js +1 -45
  102. package/dist/host/native/jobs.js +2 -148
  103. package/dist/host/native/loader.js +1 -269
  104. package/dist/host/native/materialize.js +1 -175
  105. package/dist/host/native/module-closure.js +1 -130
  106. package/dist/host/native/module-delivery.d.ts +1 -62
  107. package/dist/host/native/module-delivery.js +1 -151
  108. package/dist/host/native/module-syntax.js +1 -69
  109. package/dist/host/native/native-binary.js +1 -115
  110. package/dist/host/native/native-program.js +1 -72
  111. package/dist/host/native/node-jobs.js +3 -187
  112. package/dist/host/native/node-launch.js +1 -159
  113. package/dist/host/native/preload.js +1 -45
  114. package/dist/host/native/reading.js +1 -249
  115. package/dist/host/native/receipt-environment.js +1 -41
  116. package/dist/host/native/references.js +1 -30
  117. package/dist/host/native/root.js +1 -37
  118. package/dist/host/native/route.js +1 -216
  119. package/dist/host/native/runtime.js +1 -9
  120. package/dist/host/native-reader-context.js +1 -8
  121. package/dist/host/native-receiver.js +1 -0
  122. package/dist/host/objects.js +1 -38
  123. package/dist/host/order.js +1 -2
  124. package/dist/host/place-act-transaction.js +4 -0
  125. package/dist/host/place-clock-selection.js +5 -124
  126. package/dist/host/ports/ingress.js +2 -57
  127. package/dist/host/ports/local-act.js +5 -172
  128. package/dist/host/ports/place-act.js +1 -169
  129. package/dist/host/ports/publication.js +1 -56
  130. package/dist/host/ports/signing.js +1 -74
  131. package/dist/host/ports/store.js +1 -18
  132. package/dist/host/ports/transport.js +1 -48
  133. package/dist/host/process-lifetime.js +1 -17
  134. package/dist/host/provide.js +1 -183
  135. package/dist/host/read-only.js +1 -9
  136. package/dist/host/reading-closures.js +7 -227
  137. package/dist/host/reading-context.js +1 -53
  138. package/dist/host/reading-identity-types.js +0 -0
  139. package/dist/host/reading-identity.js +3 -0
  140. package/dist/host/reading-preparation.js +6 -0
  141. package/dist/host/reference-authenticated.d.ts +1 -40
  142. package/dist/host/reference-authenticated.js +3 -100
  143. package/dist/host/reference-contracts.js +1 -6
  144. package/dist/host/reference-export.js +6 -81
  145. package/dist/host/reference-native-api.d.ts +1 -3
  146. package/dist/host/reference-native-api.js +1 -3
  147. package/dist/host/reference-native.js +1 -74
  148. package/dist/host/reference-profile.js +1 -63
  149. package/dist/host/reference-standing.js +2 -18
  150. package/dist/host/reference.js +1 -24
  151. package/dist/host/refusals.js +2 -43
  152. package/dist/host/release.js +4 -256
  153. package/dist/host/root-clock-source.js +1 -305
  154. package/dist/host/selected-capsules.js +1 -50
  155. package/dist/host/selected-topos.js +1 -80
  156. package/dist/host/signature-era.js +1 -0
  157. package/dist/host/signing.js +2 -48
  158. package/dist/host/source-content.js +2 -110
  159. package/dist/host/standing-carrier.js +1 -0
  160. package/dist/host/standing-projection.js +1 -0
  161. package/dist/host/timing.js +1 -7
  162. package/dist/host/walk.js +20 -193
  163. package/dist/judge/late.js +1 -33
  164. package/dist/judge/take.js +2 -75
  165. package/dist/judge/widening.js +1 -25
  166. package/dist/land/act.js +8 -302
  167. package/dist/land/alias.js +1 -41
  168. package/dist/land/held.js +1 -23
  169. package/dist/land/inert.js +1 -33
  170. package/dist/land/leaving.js +1 -55
  171. package/dist/land/ledger.js +7 -188
  172. package/dist/land/owned.js +4 -66
  173. package/dist/land/sign.js +1 -53
  174. package/dist/land/standing-pin.js +1 -49
  175. package/dist/land/vouched.js +1 -112
  176. package/dist/land/withdraw.js +1 -24
  177. package/dist/observe/files.js +1 -88
  178. package/dist/observe/identity.js +1 -61
  179. package/dist/observe/match.js +1 -8
  180. package/dist/observe/regions.js +7 -306
  181. package/dist/observe/run.js +2 -40
  182. package/dist/observe/runner.js +4 -93
  183. package/dist/public-types.d.ts +1114 -0
  184. package/dist/render/capsule.js +1 -30
  185. package/dist/render/field.js +1 -40
  186. package/dist/render/names.js +1 -4
  187. package/dist/render/open.js +1 -61
  188. package/dist/render/page.js +1 -7
  189. package/dist/render/receipts.js +1 -47
  190. package/dist/render/shape.js +1 -74
  191. package/dist/render/target.js +4 -52
  192. package/dist/render/view.js +7 -197
  193. package/dist/vendor/50086958553b9376d600583f7dff354196bfeb41170fc6d1482ca7bea7c493e2.LICENSE +21 -0
  194. package/dist/vendor/b3e938d28f88a1851af303a40c749e12ebd5a686daac4f0b343b5cdd2e040419.LICENSE +21 -0
  195. package/dist/vendor/bundle.json +1 -0
  196. package/dist/vendor/library11.js +1 -0
  197. package/dist/vendor/object-provider.js +42 -0
  198. package/dist/vendor/runtime.js +38 -0
  199. package/dist/vendor/sources.json +1 -0
  200. package/dist/wasm/adapter.d.ts +1 -51
  201. package/dist/wasm/adapter.js +1 -180
  202. package/dist/wasm/node-worker.js +1 -13
  203. package/dist/wasm/object.js +1 -76
  204. package/dist/wasm/worker.js +1 -7
  205. package/docs/contracts/closure-delivery.md +1 -1
  206. package/docs/contracts/closure-delivery.publication.json +1 -1
  207. package/docs/contracts/conformance.json +142 -0
  208. package/docs/contracts/determinism.bound +9 -0
  209. package/docs/contracts/engine-object-projection.publication.json +1 -1
  210. package/docs/contracts/engine.md +21 -1
  211. package/docs/contracts/native-host.md +21 -0
  212. package/docs/contracts/native-host.publication.json +25 -0
  213. package/docs/contracts/native-publication.inventory.json +3 -1
  214. package/docs/contracts/optional-wasm-adapter.publication.json +1 -1
  215. package/docs/contracts/publication.inventory.json +29 -0
  216. package/docs/contracts/reading-identity.md +37 -0
  217. package/docs/contracts/reading-identity.publication.json +25 -0
  218. package/docs/contracts/refusals.bound +51 -0
  219. package/docs/contracts/refusals.md +18 -3
  220. package/docs/contracts/refusals.publication.json +2 -2
  221. package/docs/contracts/views.md +8 -0
  222. package/docs/guide.md +10 -0
  223. package/docs/reference.md +40 -0
  224. package/docs/why.md +109 -0
  225. package/package.json +37 -18
  226. package/receipts.bound +11675 -9725
  227. package/samples/native-host/no.json +10 -0
  228. package/samples/native-host/yes.json +20 -0
  229. package/samples/reading-identity/no.json +20 -0
  230. package/samples/reading-identity/yes.json +46 -0
  231. package/vectors/native/native-host.vector.json +62 -0
  232. package/vectors/native/reading-identity.vector.json +101 -0
  233. package/vectors/native/refusals.vector.json +104 -0
  234. package/vendor/syntax/LICENSE.md +7 -0
  235. package/vendor/syntax/parser.js +1 -0
  236. package/dist/engine/object.d.ts +0 -17
  237. package/dist/engine/ports.d.ts +0 -32
  238. package/dist/host/native/module-closure.d.ts +0 -35
  239. package/dist/host/reference-export.d.ts +0 -33
  240. package/dist/host/reference-native.d.ts +0 -30
  241. package/dist/host/reference-profile.d.ts +0 -23
  242. package/dist/host/refusals.d.ts +0 -14
@@ -1,256 +1,4 @@
1
- import { ContentNeeded } from "./content-needed.js";
2
- import { createHash } from 'node:crypto';
3
- import { existsSync, lstatSync, mkdirSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
4
- import { dirname, join, sep } from 'node:path';
5
- import { canonical } from '@lapxo/topos/wire';
6
- import { fieldOf, isWire, isConfig } from "../fold/claims.js";
7
- import { archiveCoordinate, archiveDestination, members } from "./archive.js";
8
- import { blobAt, holdStore, landBlob, replaceWhole, storeAt } from "../land/ledger.js";
9
- import { coordinatesUnder, observeFile, observeText } from "../observe/files.js";
10
- export const laidAt = (store, digest) => storeAt(store, 'cas', 'laid', digest.replace(/^[^:]*:/, ''));
11
- /** A host artifact binding, not a measured ceiling or an object act. */
12
- export const isReleaseBinding = (line) => isConfig(line)
13
- && fieldOf(line, 'scope').startsWith('dep/') && fieldOf(line, 'role') === 'reads'
14
- && fieldOf(line, 'value') !== 'withdraw'
15
- && (fieldOf(line, 'measure') === 'digest' || /^[a-zA-Z][a-zA-Z0-9-]*:[a-f0-9]+$/.test(fieldOf(line, 'value')));
16
- export const releasesOf = (lock) => lock.filter(isReleaseBinding);
17
- const laidMark = '.laid';
18
- const digestOf = (bytes) => `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
19
- const hashed = (digest, bytes) => createHash(digest.includes(':') ? digest.slice(0, digest.indexOf(':')) : 'sha256').update(bytes).digest('hex') === digest.replace(/^[^:]*:/, '');
20
- const snapshotOf = (text) => text.split('\n').filter(isWire).filter((line) => fieldOf(line, 'measure') === 'digest' && fieldOf(line, 'scope').startsWith('file/'))
21
- .map((line) => [fieldOf(line, 'scope').slice('file/'.length), fieldOf(line, 'value')]);
22
- /** Mount an explicitly declared archive coordinate; never infer a package root. */
23
- function mountedFiles(files, root) {
24
- if (!root)
25
- return files;
26
- const prefix = archiveCoordinate(root) + '/';
27
- if (!files.length || files.some(([name]) => !name.startsWith(prefix)))
28
- throw new Error(`REFUSE·release archive root ${root} does not contain every member`);
29
- return files.map(([name, digest]) => [archiveCoordinate(name.slice(prefix.length)), digest]);
30
- }
31
- function snapshotLines(files) {
32
- return files.map(([coord, digest]) => canonical({
33
- scope: `file/${coord}`, role: 'writes', form: 'alphabet', measure: 'digest', value: digest, at: `place:${digest}`, by: 'target',
34
- })).join('\n') + (files.length ? '\n' : '');
35
- }
36
- function layDir(from, into) {
37
- for (const coord of coordinatesUnder(from, from)) {
38
- if (coord === laidMark)
39
- continue;
40
- const src = join(from, coord);
41
- const dest = archiveDestination(into, coord);
42
- mkdirSync(dirname(dest), { recursive: true });
43
- if (existsSync(dest))
44
- continue;
45
- const bytes = observeFile(src);
46
- if (bytes === undefined)
47
- throw new Error(`REFUSE·release unreadable historical member ${coord}`);
48
- writeFileSync(dest, bytes, { flag: 'wx' });
49
- }
50
- }
51
- function swapPlace(place, next, digest, ready) {
52
- const old = `${next}.old`;
53
- if (lstatSync(old, { throwIfNoEntry: false }) !== undefined)
54
- throw new Error(`REFUSE·release recovery required for ${old}`);
55
- writeFileSync(join(next, laidMark), digest, { flag: 'wx' });
56
- if (ready !== undefined) {
57
- // Digest-addressed trees publish once. A verified concurrent winner is reused,
58
- // never moved aside while another process may be importing its members.
59
- if (ready()) {
60
- rmSync(next, { recursive: true, force: true });
61
- return;
62
- }
63
- try {
64
- renameSync(next, place);
65
- return;
66
- }
67
- catch (error) {
68
- const code = error.code;
69
- if (code !== 'EEXIST' && code !== 'ENOTEMPTY')
70
- throw error;
71
- if (ready()) {
72
- rmSync(next, { recursive: true, force: true });
73
- return;
74
- }
75
- // An existing invalid cache retains the repair/rollback path below.
76
- }
77
- }
78
- let moved = false;
79
- try {
80
- if (lstatSync(place, { throwIfNoEntry: false }) !== undefined) {
81
- renameSync(place, old);
82
- moved = true;
83
- }
84
- renameSync(next, place);
85
- }
86
- catch (error) {
87
- if (moved && lstatSync(place, { throwIfNoEntry: false }) === undefined)
88
- renameSync(old, place);
89
- throw error;
90
- }
91
- if (moved)
92
- rmSync(old, { recursive: true, force: true });
93
- }
94
- function nextPlace(place) {
95
- const next = `${place}.${process.pid}`;
96
- mkdirSync(dirname(place), { recursive: true });
97
- mkdirSync(next); // An abandoned staging directory is never silently reused.
98
- return next;
99
- }
100
- function layFiles(store, own, files, into) {
101
- const held = files.map(([coordinate, digest]) => {
102
- const coord = archiveCoordinate(coordinate);
103
- const dest = archiveDestination(into, coord);
104
- const src = [blobAt(own, digest), blobAt(store, digest)].find(existsSync);
105
- if (src === undefined)
106
- throw new Error(`REFUSE·release missing blob ${digest} for ${coord}`);
107
- const bytes = observeFile(src);
108
- if (bytes === undefined || !hashed(digest, bytes))
109
- throw new Error(`REFUSE·release blob ${digest} for ${coord} does not hold`);
110
- return { dest, bytes };
111
- });
112
- for (const { dest, bytes } of held) {
113
- mkdirSync(dirname(dest), { recursive: true });
114
- // Materialized files may be edited by their consumer; never share writable inodes with the CAS.
115
- writeFileSync(dest, bytes, { flag: 'wx' });
116
- }
117
- }
118
- function layoutMatches(place, files) {
119
- return files.every(([coord, digest]) => {
120
- const path = join(place, archiveCoordinate(coord));
121
- const st = lstatSync(path, { throwIfNoEntry: false });
122
- const bytes = st?.isFile() ? observeFile(path) : undefined;
123
- return bytes !== undefined && hashed(digest, bytes);
124
- });
125
- }
126
- function sameSnapshot(actual, expected) {
127
- const named = new Map(actual);
128
- return named.size === actual.length && actual.length === expected.length && expected.every(([name, digest]) => named.get(name) === digest);
129
- }
130
- function keepSnapshot(store, digest, bytes) {
131
- const files = members(bytes).map((one) => {
132
- const file = digestOf(one.bytes);
133
- landBlob(store, file, one.bytes);
134
- return [one.name, file];
135
- });
136
- const text = snapshotLines(files);
137
- replaceWhole(laidAt(store, digest), text);
138
- holdStore(store, 'laid', digest, Buffer.byteLength(text));
139
- return files;
140
- }
141
- /**
142
- * What the host does before anything runs: each release the instrument's own lock names by digest is a snapshot of
143
- * lines and digests, kept by digest, and the place the line needs is a materialization of verified CAS bytes. Materialized files do not share writable inodes with the store. A blob is laid only when its bytes hash to the digest that names it. What stands laid is
144
- * said by resolvedOf: a line for each release whose blob hashes to its name and whose place, or the cas when its line
145
- * needs none, holds it.
146
- */
147
- export function layReleases(store, own, lock, contentStore = own) {
148
- let laid = 0;
149
- for (const line of releasesOf(lock)) {
150
- // A release catalog entry without a mount is materialized only when requested.
151
- // Its absence must not block an unrelated place's fold.
152
- const needs = fieldOf(line, 'needs');
153
- if (!needs)
154
- continue;
155
- const digest = fieldOf(line, 'value');
156
- const at = laidAt(store, digest);
157
- const dir = lstatSync(at, { throwIfNoEntry: false })?.isDirectory() ?? false;
158
- const bytes = observeFile(blobAt(contentStore, digest));
159
- const named = digest.replace(/^[^:]*:/, '');
160
- if (bytes !== undefined && !hashed(digest, bytes)) {
161
- throw new Error(`REFUSE·release ${fieldOf(line, 'scope')} · blob ${named.slice(0, 12)} hashes to ${digestOf(bytes).slice(7, 19)} · not laid`);
162
- }
163
- if (bytes !== undefined && !dir && observeText(at) === undefined)
164
- keepSnapshot(store, digest, bytes);
165
- const current = observeText(at);
166
- if (bytes !== undefined && current !== undefined && !dir && !sameSnapshot(snapshotOf(current), members(bytes).map((one) => [one.name, digestOf(one.bytes)])))
167
- throw new Error(`REFUSE·release snapshot for ${digest} omits or changes archive members; explicit rebuild required`);
168
- if (bytes === undefined && !dir && current === undefined)
169
- throw new ContentNeeded(digest, `REFUSE·release ${fieldOf(line, 'scope')} has no available archive or snapshot`);
170
- const root = fieldOf(line, 'shape');
171
- const place = join(own, archiveCoordinate(needs));
172
- const link = lstatSync(place, { throwIfNoEntry: false });
173
- if (link?.isSymbolicLink() && !realpathSync(place).includes(`${sep}cas${sep}laid${sep}`))
174
- continue;
175
- if (dir) {
176
- const files = bytes === undefined ? undefined : mountedFiles(members(bytes).map((one) => [one.name, digestOf(one.bytes)]), root);
177
- if (!lstatSync(place, { throwIfNoEntry: false })?.isSymbolicLink() && observeText(join(place, laidMark)) === digest && files !== undefined && layoutMatches(place, files))
178
- continue;
179
- const next = nextPlace(place);
180
- try {
181
- layDir(root ? join(at, archiveCoordinate(root)) : at, next);
182
- swapPlace(place, next, digest);
183
- }
184
- catch (error) {
185
- rmSync(next, { recursive: true, force: true });
186
- throw error;
187
- }
188
- laid += 1;
189
- continue;
190
- }
191
- const snap = observeText(at);
192
- if (snap === undefined)
193
- throw new Error(`REFUSE·release ${fieldOf(line, 'scope')} has no available snapshot`);
194
- const mounted = mountedFiles(snapshotOf(snap), root);
195
- if (!lstatSync(place, { throwIfNoEntry: false })?.isSymbolicLink() && observeText(join(place, laidMark)) === digest && layoutMatches(place, mounted))
196
- continue;
197
- const next = nextPlace(place);
198
- try {
199
- layFiles(store, contentStore, mounted, next);
200
- swapPlace(place, next, digest);
201
- }
202
- catch (error) {
203
- rmSync(next, { recursive: true, force: true });
204
- throw error;
205
- }
206
- laid += 1;
207
- }
208
- return laid;
209
- }
210
- export const resolvedOf = (store, own, lock, contentStore = own) => releasesOf(lock).flatMap((line) => {
211
- const digest = fieldOf(line, 'value');
212
- const bytes = observeFile(blobAt(contentStore, digest));
213
- const ok = bytes !== undefined && hashed(digest, bytes);
214
- const needs = fieldOf(line, 'needs');
215
- const place = needs ? join(own, archiveCoordinate(needs)) : '';
216
- const at = laidAt(store, digest);
217
- const laid = needs
218
- ? observeText(join(place, laidMark)) === digest || (existsSync(place) && existsSync(at) && realpathSync(place) === realpathSync(at))
219
- : existsSync(at);
220
- const expected = ok ? members(bytes).map((one) => [one.name, digestOf(one.bytes)]) : undefined;
221
- const historicalDirectory = lstatSync(at, { throwIfNoEntry: false })?.isDirectory() === true;
222
- const snapshot = observeText(at);
223
- const complete = expected !== undefined && (needs
224
- ? layoutMatches(place, mountedFiles(expected, fieldOf(line, 'shape')))
225
- : historicalDirectory ? layoutMatches(at, expected) : snapshot !== undefined && sameSnapshot(snapshotOf(snapshot), expected));
226
- return ok && laid && complete ? [canonical({ scope: `resolved/${fieldOf(line, 'scope').slice('dep/'.length)}`, role: 'writes', form: 'alphabet', measure: 'digest', value: digest, at: `place:${digest}`, by: 'target' })] : [];
227
- });
228
- /** A host loads verified release bytes from a runtime tree, never interprets a snapshot file as a directory. */
229
- export function runtimeTreeAt(store, own, digest, contentStore = own, root = '') {
230
- const named = /^([a-zA-Z0-9-]+):([a-fA-F0-9]+)$/.exec(digest);
231
- if (named === null)
232
- throw new Error(`REFUSE·release invalid digest ${digest}`);
233
- const bytes = observeFile(blobAt(contentStore, digest));
234
- if (bytes === undefined)
235
- return undefined;
236
- if (!hashed(digest, bytes))
237
- throw new Error(`REFUSE·release ${digest} does not name its available bytes`);
238
- const files = members(bytes).map((one) => [one.name, digestOf(one.bytes)]);
239
- mountedFiles(files, root); // Validate the same declared projection used for installation.
240
- const place = storeAt(store, 'cas', 'runtime', named[1], named[2]);
241
- const selected = root ? join(place, archiveCoordinate(root)) : place;
242
- if (observeText(join(place, laidMark)) === digest && layoutMatches(place, files))
243
- return selected;
244
- for (const member of members(bytes))
245
- landBlob(store, digestOf(member.bytes), member.bytes);
246
- const next = nextPlace(place);
247
- try {
248
- layFiles(store, own, files, next);
249
- swapPlace(place, next, digest, () => observeText(join(place, laidMark)) === digest && layoutMatches(place, files));
250
- }
251
- catch (error) {
252
- rmSync(next, { recursive: true, force: true });
253
- throw error;
254
- }
255
- return selected;
256
- }
1
+ var te=Object.defineProperty;var l=(e,r)=>te(e,"name",{value:r,configurable:!0});import{ContentNeeded as ne}from"./content-needed.js";import{createHash as _}from"node:crypto";import{existsSync as I,lstatSync as y,mkdirSync as D,realpathSync as z,renameSync as L,rmSync as F,writeFileSync as B}from"node:fs";import{dirname as C,join as m,sep as P}from"node:path";import{library11_canonical as G}from"../vendor/runtime.js";import{fieldOf as u,isWire as oe,isConfig as se}from"../fold/claims.js";import{archiveCoordinate as w,archiveDestination as J,members as g}from"./archive.js";import{blobAt as k,holdStore as ie,landBlob as K,replaceWhole as ce,storeAt as Q}from"../land/ledger.js";import{coordinatesUnder as ae,observeFile as R,observeText as b}from"../observe/files.js";const Z=l((e,r)=>Q(e,"cas","laid",r.replace(/^[^:]*:/,"")),"laidAt"),fe=l(e=>se(e)&&u(e,"scope").startsWith("dep/")&&u(e,"role")==="reads"&&u(e,"value")!=="withdraw"&&(u(e,"measure")==="digest"||/^[a-zA-Z][a-zA-Z0-9-]*:[a-f0-9]+$/.test(u(e,"value"))),"isReleaseBinding"),V=l(e=>e.filter(fe),"releasesOf"),$=".laid",S=l(e=>`sha256:${_("sha256").update(e).digest("hex")}`,"digestOf"),A=l((e,r)=>_(e.includes(":")?e.slice(0,e.indexOf(":")):"sha256").update(r).digest("hex")===e.replace(/^[^:]*:/,""),"hashed"),j=l(e=>e.split(`
2
+ `).filter(oe).filter(r=>u(r,"measure")==="digest"&&u(r,"scope").startsWith("file/")).map(r=>[u(r,"scope").slice(5),u(r,"value")]),"snapshotOf");function M(e,r){if(!r)return e;const t=w(r)+"/";if(!e.length||e.some(([c])=>!c.startsWith(t)))throw new Error(`REFUSE\xB7release archive root ${r} does not contain every member`);return e.map(([c,n])=>[w(c.slice(t.length)),n])}l(M,"mountedFiles");function le(e){return e.map(([r,t])=>G({scope:`file/${r}`,role:"writes",form:"alphabet",measure:"digest",value:t,at:`place:${t}`,by:"target"})).join(`
3
+ `)+(e.length?`
4
+ `:"")}l(le,"snapshotLines");function ue(e,r){for(const t of ae(e,e)){if(t===$)continue;const c=m(e,t),n=J(r,t);if(D(C(n),{recursive:!0}),I(n))continue;const o=R(c);if(o===void 0)throw new Error(`REFUSE\xB7release unreadable historical member ${t}`);B(n,o,{flag:"wx"})}}l(ue,"layDir");function q(e,r,t,c){const n=`${r}.old`;if(y(n,{throwIfNoEntry:!1})!==void 0)throw new Error(`REFUSE\xB7release recovery required for ${n}`);if(B(m(r,$),t,{flag:"wx"}),c!==void 0){if(c()){F(r,{recursive:!0,force:!0});return}try{L(r,e);return}catch(s){const i=s.code;if(i!=="EEXIST"&&i!=="ENOTEMPTY")throw s;if(c()){F(r,{recursive:!0,force:!0});return}}}let o=!1;try{y(e,{throwIfNoEntry:!1})!==void 0&&(L(e,n),o=!0),L(r,e)}catch(s){throw o&&y(e,{throwIfNoEntry:!1})===void 0&&L(n,e),s}o&&F(n,{recursive:!0,force:!0})}l(q,"swapPlace");function H(e){const r=`${e}.${process.pid}`;return D(C(e),{recursive:!0}),D(r),r}l(H,"nextPlace");function ee(e,r,t,c){const n=t.map(([o,s])=>{const i=w(o),f=J(c,i),d=[k(r,s),k(e,s)].find(I);if(d===void 0)throw new Error(`REFUSE\xB7release missing blob ${s} for ${i}`);const a=R(d);if(a===void 0||!A(s,a))throw new Error(`REFUSE\xB7release blob ${s} for ${i} does not hold`);return{dest:f,bytes:a}});for(const{dest:o,bytes:s}of n)D(C(o),{recursive:!0}),B(o,s,{flag:"wx"})}l(ee,"layFiles");function U(e,r){return r.every(([t,c])=>{const n=m(e,w(t)),s=y(n,{throwIfNoEntry:!1})?.isFile()?R(n):void 0;return s!==void 0&&A(c,s)})}l(U,"layoutMatches");function re(e,r){const t=new Map(e);return t.size===e.length&&e.length===r.length&&r.every(([c,n])=>t.get(c)===n)}l(re,"sameSnapshot");function de(e,r,t){const c=g(t).map(o=>{const s=S(o.bytes);return K(e,s,o.bytes),[o.name,s]}),n=le(c);return ce(Z(e,r),n),ie(e,"laid",r,Buffer.byteLength(n)),c}l(de,"keepSnapshot");function xe(e,r,t,c=r){let n=0;for(const o of V(t)){const s=u(o,"needs");if(!s)continue;const i=u(o,"value"),f=Z(e,i),d=y(f,{throwIfNoEntry:!1})?.isDirectory()??!1,a=R(k(c,i)),p=i.replace(/^[^:]*:/,"");if(a!==void 0&&!A(i,a))throw new Error(`REFUSE\xB7release ${u(o,"scope")} \xB7 blob ${p.slice(0,12)} hashes to ${S(a).slice(7,19)} \xB7 not laid`);a!==void 0&&!d&&b(f)===void 0&&de(e,i,a);const E=b(f);if(a!==void 0&&E!==void 0&&!d&&!re(j(E),g(a).map(v=>[v.name,S(v.bytes)])))throw new Error(`REFUSE\xB7release snapshot for ${i} omits or changes archive members; explicit rebuild required`);if(a===void 0&&!d&&E===void 0)throw new ne(i,`REFUSE\xB7release ${u(o,"scope")} has no available archive or snapshot`);const x=u(o,"shape"),h=m(r,w(s));if(y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&!z(h).includes(`${P}cas${P}laid${P}`))continue;if(d){const v=a===void 0?void 0:M(g(a).map(O=>[O.name,S(O.bytes)]),x);if(!y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&b(m(h,$))===i&&v!==void 0&&U(h,v))continue;const W=H(h);try{ue(x?m(f,w(x)):f,W),q(h,W,i)}catch(O){throw F(W,{recursive:!0,force:!0}),O}n+=1;continue}const N=b(f);if(N===void 0)throw new Error(`REFUSE\xB7release ${u(o,"scope")} has no available snapshot`);const Y=M(j(N),x);if(!y(h,{throwIfNoEntry:!1})?.isSymbolicLink()&&b(m(h,$))===i&&U(h,Y))continue;const T=H(h);try{ee(e,c,Y,T),q(h,T,i)}catch(v){throw F(T,{recursive:!0,force:!0}),v}n+=1}return n}l(xe,"layReleases");const Fe=l((e,r,t,c=r)=>V(t).flatMap(n=>{const o=u(n,"value"),s=R(k(c,o)),i=s!==void 0&&A(o,s),f=u(n,"needs"),d=f?m(r,w(f)):"",a=Z(e,o),p=f?b(m(d,$))===o||I(d)&&I(a)&&z(d)===z(a):I(a),E=i?g(s).map(N=>[N.name,S(N.bytes)]):void 0,x=y(a,{throwIfNoEntry:!1})?.isDirectory()===!0,h=b(a),X=E!==void 0&&(f?U(d,M(E,u(n,"shape"))):x?U(a,E):h!==void 0&&re(j(h),E));return i&&p&&X?[G({scope:`resolved/${u(n,"scope").slice(4)}`,role:"writes",form:"alphabet",measure:"digest",value:o,at:`place:${o}`,by:"target"})]:[]}),"resolvedOf");function ge(e,r,t,c=r,n=""){const o=/^([a-zA-Z0-9-]+):([a-fA-F0-9]+)$/.exec(t);if(o===null)throw new Error(`REFUSE\xB7release invalid digest ${t}`);const s=R(k(c,t));if(s===void 0)return;if(!A(t,s))throw new Error(`REFUSE\xB7release ${t} does not name its available bytes`);const i=g(s).map(p=>[p.name,S(p.bytes)]);M(i,n);const f=Q(e,"cas","runtime",o[1],o[2]),d=n?m(f,w(n)):f;if(b(m(f,$))===t&&U(f,i))return d;for(const p of g(s))K(e,S(p.bytes),p.bytes);const a=H(f);try{ee(e,r,i,a),q(f,a,t,()=>b(m(f,$))===t&&U(f,i))}catch(p){throw F(a,{recursive:!0,force:!0}),p}return d}l(ge,"runtimeTreeAt");export{fe as isReleaseBinding,Z as laidAt,xe as layReleases,V as releasesOf,Fe as resolvedOf,ge as runtimeTreeAt};
@@ -1,305 +1 @@
1
- import { existsSync, opendirSync, statSync } from 'node:fs';
2
- import { join, basename } from 'node:path';
3
- /** Actual stored Root cursor. Inputs are fixed host selections from admitted
4
- * lines, never packet epochs, receipt maxima, or caller-supplied verification. */
5
- import { committedLocalActs, actLines } from "./ports/local-act.js";
6
- import { verifiedLegacyActResult } from "../cli/act-result.js";
7
- import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
8
- import { authorityFor } from "../fold/signers.js";
9
- import { eraOf } from "../fold/digests.js";
10
- import { fieldOf, foldClaims, isConfig, selfName } from "../fold/claims.js";
11
- import { authenticatedRootStanding } from "./admitted-place.js";
12
- import { verifiesOwnerLine } from "../land/sign.js";
13
- import { placesIn } from "../fold/places.js";
14
- import { appliedHistoryIn } from "../fold/signed.js";
15
- import { storeOf } from "../land/ledger.js";
16
- import { hostClockSelection } from "./place-clock-selection.js";
17
- import { readPlaceActContext, placeActReceiptsOf } from '@lapxo/topos/core';
18
- import { placeActResult, placeActHeads } from "./ports/place-act.js";
19
- import { rootAuthorityProtection } from "../land/act.js";
20
- import { readOnly } from "./read-only.js";
21
- const fail = (s) => { throw Error('REFUSE·Root cursor ' + s); };
22
- function sourceSelection(selected) {
23
- const b = selected?.resources;
24
- if (!b || Object.keys(b).sort().join(',') !== 'acts,bytes,lines,places' || Object.values(b).some(n => !Number.isSafeInteger(n) || n < 1) || typeof selected.selector !== 'string' || !selected.selector || typeof selected.profileSelector !== 'string' || !selected.profileSelector || !selected.bootstrapCheckpoint || Object.keys(selected.bootstrapCheckpoint).sort().join(',') !== 'measure,scope' || typeof selected.bootstrapCheckpoint.scope !== 'string' || !selected.bootstrapCheckpoint.scope || typeof selected.bootstrapCheckpoint.measure !== 'string' || !selected.bootstrapCheckpoint.measure)
25
- fail('missing admitted source selection');
26
- if (!selected.inherited?.clock || Object.keys(selected.inherited.clock).sort().join(',') !== 'bytes,placements,policies,proofs,records,signers' || !selected.inherited.storage || Object.keys(selected.inherited.storage).sort().join(',') !== 'acts,bytes' || [...Object.values(selected.inherited.clock), ...Object.values(selected.inherited.storage)].some(n => !Number.isSafeInteger(n) || n < 1))
27
- fail('missing inherited source budgets');
28
- return b;
29
- }
30
- function boundedInventory(root, selected) {
31
- const b = sourceSelection(selected), store = storeOf(root);
32
- let bytes = 0, files = 0, entries = 0;
33
- const limit = b.acts * (b.places + 2) + b.lines;
34
- if (!Number.isSafeInteger(limit))
35
- fail('source file bound');
36
- const visit = (path) => { if (!existsSync(path))
37
- return; const s = statSync(path); if (s.isDirectory()) {
38
- const directory = opendirSync(path);
39
- try {
40
- for (let entry = directory.readSync(); entry; entry = directory.readSync()) {
41
- if (++entries > limit)
42
- fail('source entry budget');
43
- if (basename(path) === 'ledger' && entry.name === 'place-acts')
44
- continue;
45
- visit(join(path, entry.name));
46
- }
47
- }
48
- finally {
49
- directory.closeSync();
50
- }
51
- }
52
- else {
53
- if (!s.isFile() || ++files > limit || (bytes += s.size) > b.bytes)
54
- fail('source file/byte budget');
55
- } };
56
- visit(join(store, 'ledger'));
57
- visit(join(store, selfName() + '.keys'));
58
- }
59
- function current(root, selected) {
60
- const b = sourceSelection(selected);
61
- boundedInventory(root, selected);
62
- const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER);
63
- if (history.length > b.lines)
64
- fail('history line budget');
65
- let bytes = 0;
66
- for (const line of history)
67
- if ((bytes += Buffer.byteLength(line)) > b.bytes)
68
- fail('history byte budget');
69
- const acts = committedLocalActs(storeOf(root));
70
- if (acts.length > b.acts)
71
- fail('act inventory budget');
72
- const rooted = acts.flatMap(a => { const roots = a.bundle.placements?.filter(p => p.place === '.') ?? []; if (!roots.some(p => actLines(p.records).some(isConfig)))
73
- return []; const verified = verifiedLegacyActResult(root, a.identity); return [{ ...verified, identity: a.identity, roots }]; });
74
- // Comparison is over verified committed Root CONFIGURATION acts only. This
75
- // is the existing local-act cursor, not a new maximum over arbitrary records.
76
- rooted.sort((a, b) => a.epoch - b.epoch);
77
- if (rooted.some((a, i) => i > 0 && a.epoch === rooted[i - 1].epoch && a.identity !== rooted[i - 1].identity))
78
- fail('ambiguous committed Root cursor');
79
- const latest = rooted.at(-1), anchor = admissionAnchor(root);
80
- let checkpoint;
81
- if (latest) {
82
- const rows = latest.roots.flatMap(p => actLines(p.records)).filter(isConfig);
83
- if (!rows.length)
84
- fail('verified Root act has no configuration checkpoint');
85
- checkpoint = rows[0];
86
- }
87
- else {
88
- const rows = history.filter(l => fieldOf(l, 'scope') === selected.bootstrapCheckpoint.scope && fieldOf(l, 'measure') === selected.bootstrapCheckpoint.measure);
89
- if (rows.length !== 1)
90
- fail('bootstrap checkpoint missing/ambiguous');
91
- checkpoint = rows[0];
92
- }
93
- const epoch = Number(fieldOf(checkpoint, 'epoch'));
94
- if (!Number.isSafeInteger(epoch) || epoch < 0)
95
- fail('unknown committed Root epoch');
96
- // This synchronous observation already read the full current inventory. Select
97
- // its admission prefix without opening that same inventory a second time;
98
- // the next current() invocation still reads and authenticates fresh bytes.
99
- const prefix = history.filter(line => !isConfig(line) || Number(fieldOf(line, 'epoch')) <= epoch), algorithms = eraOf(foldClaims(prefix.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).standing).admitted;
100
- const auth = authorityFor(prefix, anchor, algorithms);
101
- if (auth.of(checkpoint).kind !== 'admitted')
102
- fail('checkpoint is not authenticated');
103
- const places = placesIn(root);
104
- if (places.length > b.places)
105
- fail('legacy namespace inventory budget');
106
- const legacy = [...new Set([...history, ...places.flatMap(p => appliedHistoryIn(root, p))])];
107
- if (legacy.length + prefix.length > b.lines || legacy.reduce((n, l) => n + Buffer.byteLength(l), 0) + prefix.reduce((n, l) => n + Buffer.byteLength(l), 0) > b.bytes)
108
- fail('legacy origin inventory budget');
109
- return { history: prefix, anchor, algorithms, authorityCheckpoint: checkpoint, legacyOriginals: legacy };
110
- }
111
- export function rootClockSource(root, selected) {
112
- const state = current(root, selected), visiting = new Set(), cache = new Map();
113
- const historical = (encoded) => {
114
- const cached = cache.get(encoded);
115
- if (cached)
116
- return cached;
117
- if (visiting.has(encoded) || visiting.size >= selected.resources.acts)
118
- fail('historical predecessor cycle/budget');
119
- visiting.add(encoded);
120
- try {
121
- const context = readPlaceActContext(encoded, selected.inherited.clock), epoch = context.root.epoch;
122
- const saved = admissionHistoryAt(root, Number(epoch));
123
- const rows = saved.filter(l => isConfig(l) && fieldOf(l, 'epoch') === epoch);
124
- if (!rows.length)
125
- fail('historical checkpoint absent');
126
- // First obtain the explicitly declared historical genesis, without scanning
127
- // current receiver chains. Each non-genesis predecessor is then authenticated
128
- // through its stored signed receipt before supplying these HOST-only heads.
129
- const basic = hostClockSelection({ ...input, history: saved, authorityCheckpoint: rows[0], legacyOriginals: saved, authenticatedReceiverHeads: [], readCurrent: () => current(root, selected) });
130
- const heads = context.places.map(p => {
131
- const initial = basic.trust.initialHeads.find(h => h.place === p.place);
132
- if (initial?.identity === p.previous)
133
- return initial;
134
- const prior = placeActResult(input.store, p.previous, { historical }, basic.selection.storageBudget);
135
- if (!prior)
136
- fail('historical predecessor absent');
137
- const decoded = readPlaceActContext(prior.context, selected.inherited.clock), place = decoded.places.find(one => one.place === p.place);
138
- if (!place)
139
- fail('historical predecessor namespace absent');
140
- return { place: p.place, identity: p.previous, epoch: place.epoch };
141
- });
142
- const result = hostClockSelection({ ...input, history: saved, authorityCheckpoint: rows[0], legacyOriginals: saved, authenticatedReceiverHeads: heads, readCurrent: () => current(root, selected) }).trust.contract;
143
- cache.set(encoded, result);
144
- return result;
145
- }
146
- finally {
147
- visiting.delete(encoded);
148
- }
149
- };
150
- const input = { store: storeOf(root), selector: selected.selector, profileSelector: selected.profileSelector, ...state, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, readCurrent: () => current(root, selected), historical };
151
- const resolved = hostClockSelection(input);
152
- // Host-only bytes from this exact authenticated constructor observation. This
153
- // is not a retained authority cache: every constructor calls current() again.
154
- const observedAnchor = Object.freeze({ ...state.anchor, coverage: Object.freeze([...state.anchor.coverage]), depth: Object.freeze({ ...state.anchor.depth }), admittedBy: Object.freeze([...state.anchor.admittedBy]) });
155
- const observation = Object.freeze({ history: Object.freeze([...state.history]), anchor: observedAnchor, algorithms: Object.freeze([...state.algorithms]) });
156
- return { ...resolved, observation, resources: Object.freeze({ ...selected.resources }), authority: (encoded) => { const context = readPlaceActContext(encoded, selected.inherited.clock), saved = admissionHistoryAt(root, Number(context.root.epoch)); const rows = saved.filter(l => isConfig(l) && fieldOf(l, 'epoch') === context.root.epoch); if (!rows.length)
157
- fail('public historical checkpoint absent'); return { anchor: state.anchor, history: saved, checkpoint: rows[0] }; } };
158
- }
159
- /** Source mapping is an explicit Root declaration; absence never means a zero
160
- * clock or an inferred cursor. Existing legacy callers do not request it. */
161
- export function declaredRootClockSource(root) {
162
- const live = authenticatedRootStanding(root), rows = live.filter(l => fieldOf(l, 'scope') === 'wire/place-act-context-source' && fieldOf(l, 'measure') === 'text' && fieldOf(l, 'value') !== 'withdraw');
163
- if (rows.length !== 1)
164
- fail('requires one admitted source mapping');
165
- const line = rows[0], anchor = admissionAnchor(root), algorithms = eraOf(live).admitted;
166
- if (fieldOf(line, 'by') !== anchor.id || fieldOf(line, 'role') !== 'writes' || fieldOf(line, 'form') !== 'alphabet' || fieldOf(line, 'value') !== 'lock' || !verifiesOwnerLine(line, anchor.publicKey, algorithms))
167
- fail('mapping is not Root signed');
168
- const about = fieldOf(line, 'about');
169
- let value;
170
- try {
171
- value = JSON.parse(about);
172
- }
173
- catch {
174
- return fail('mapping JSON invalid');
175
- }
176
- if (!value || Object.keys(value).sort().join(',') !== 'bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector' || value.profile !== 'place-act-context@1' || JSON.stringify(value) !== about)
177
- fail('unknown/noncanonical mapping');
178
- return rootClockSource(root, value);
179
- }
180
- export function hasRootClockSource(root) { return authenticatedRootStanding(root).some(l => fieldOf(l, 'scope') === 'wire/place-act-context-source' && fieldOf(l, 'measure') === 'text' && fieldOf(l, 'value') !== 'withdraw'); }
181
- /** Historical inspection ONLY. The expected acting Root anchor and resource
182
- * selection come from the consumer's selected place, never from this packet.
183
- * This function returns data and cannot construct a current command permit. */
184
- export function inspectPortablePlaceContexts(encoded, expectedAnchor, selected) {
185
- sourceSelection(selected);
186
- if (typeof encoded !== 'string' || Buffer.byteLength(encoded) > selected.resources.bytes)
187
- fail('portable byte budget');
188
- let rows;
189
- try {
190
- rows = JSON.parse(encoded);
191
- }
192
- catch {
193
- return fail('portable JSON');
194
- }
195
- if (!Array.isArray(rows) || rows.length > selected.resources.acts)
196
- fail('portable context inventory budget');
197
- const byId = new Map();
198
- for (const row of rows) {
199
- if (!row || typeof row.identity !== 'string' || typeof row.context !== 'string' || !row.authority || JSON.stringify(row.authority.anchor) !== JSON.stringify(expectedAnchor) || !Array.isArray(row.authority.history) || !Array.isArray(row.receipts) || byId.has(row.identity))
200
- fail('portable anchor/shape/identity');
201
- byId.set(row.identity, row);
202
- }
203
- const done = new Map(), visiting = new Set();
204
- const inspect = (id) => {
205
- const saved = done.get(id);
206
- if (saved)
207
- return saved;
208
- const row = byId.get(id);
209
- if (!row || visiting.has(id))
210
- fail('portable predecessor missing/cyclic');
211
- visiting.add(id);
212
- try {
213
- const context = readPlaceActContext(row.context, selected.inherited.clock), input = { store: '', selector: selected.selector, profileSelector: selected.profileSelector, history: row.authority.history, anchor: expectedAnchor, algorithms: eraOf(foldClaims(row.authority.history.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).standing).admitted, legacyOriginals: row.authority.history, authorityCheckpoint: row.authority.checkpoint, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, authenticatedReceiverHeads: [], readCurrent: () => fail('portable inspection has no current Root authority'), historical: () => fail('portable inspection uses declared predecessor graph') };
214
- const basic = hostClockSelection(input), heads = context.places.map(p => { const initial = basic.trust.initialHeads.find(h => h.place === p.place); if (initial?.identity === p.previous)
215
- return initial; const parent = inspect(p.previous), place = parent.context.places.find(one => one.place === p.place); if (!place)
216
- fail('portable predecessor namespace'); return { place: p.place, identity: p.previous, epoch: place.epoch }; });
217
- const contract = hostClockSelection({ ...input, authenticatedReceiverHeads: heads }).trust.contract, result = placeActReceiptsOf(row.context, row.receipts, contract);
218
- if (result.identity !== id)
219
- fail('portable identity differs');
220
- const value = { contract, context };
221
- done.set(id, value);
222
- return value;
223
- }
224
- finally {
225
- visiting.delete(id);
226
- }
227
- };
228
- for (const id of byId.keys())
229
- inspect(id);
230
- return [...done].map(([identity, value]) => ({ identity, context: value.context }));
231
- }
232
- /** Prospective configuration is checked as authenticated Root history, never
233
- * against a prose birth condition. Initial heads are explicit typed selections;
234
- * stored committed chains cannot disappear when a selection is activated or
235
- * replaced. Preview reads only; the real admission repeats under its Root lock. */
236
- export function assertProspectiveRootClockSelection(root, signed) {
237
- const sourceScope = 'wire/place-act-context-source';
238
- const before = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER);
239
- const selectors = [sourceScope, 'wire/place-act-context-selection', 'wire/place-act-context'];
240
- // Ordinary lots need no clock projection, including bootstrap before an
241
- // admission anchor exists. Raw stored delivery is not authenticated claims.
242
- if (!before.some(l => fieldOf(l, 'scope') === sourceScope) && !signed.some(l => selectors.includes(fieldOf(l, 'scope'))))
243
- return;
244
- const oldLive = authenticatedRootStanding(root).filter(l => fieldOf(l, 'value') !== 'withdraw');
245
- const oldMapping = oldLive.find(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'measure') === 'text');
246
- if (oldMapping) {
247
- const anchor = admissionAnchor(root), algorithms = eraOf(oldLive.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).admitted;
248
- if (fieldOf(oldMapping, 'by') !== anchor.id || !verifiesOwnerLine(oldMapping, anchor.publicKey, algorithms))
249
- fail('existing source mapping is not Root signed');
250
- let old;
251
- try {
252
- old = JSON.parse(fieldOf(oldMapping, 'about'));
253
- }
254
- catch {
255
- return fail('existing source mapping JSON invalid');
256
- }
257
- selectors.push(old.selector, old.profileSelector);
258
- }
259
- if (!signed.some(l => selectors.includes(fieldOf(l, 'scope'))))
260
- return;
261
- if (signed.every(line => before.includes(line)))
262
- return;
263
- const store = storeOf(root);
264
- if (!readOnly())
265
- rootAuthorityProtection(store);
266
- const history = [...new Set([...before, ...signed])], live = foldClaims([...oldLive, ...signed].filter(isConfig)).standing.filter(l => fieldOf(l, 'value') !== 'withdraw'), rows = live.filter(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'measure') === 'text');
267
- if (!rows.length) {
268
- if (signed.some(l => fieldOf(l, 'scope') === sourceScope && fieldOf(l, 'value') === 'withdraw'))
269
- return;
270
- return fail('activation requires source and selection together');
271
- }
272
- if (rows.length !== 1)
273
- fail('prospective source mapping ambiguous');
274
- const line = rows[0], anchor = admissionAnchor(root), algorithms = eraOf(live.filter(l => fieldOf(l, 'scope').startsWith('wire/'))).admitted;
275
- if (fieldOf(line, 'by') !== anchor.id || fieldOf(line, 'role') !== 'writes' || fieldOf(line, 'form') !== 'alphabet' || fieldOf(line, 'value') !== 'lock' || !verifiesOwnerLine(line, anchor.publicKey, algorithms))
276
- fail('prospective mapping is not Root signed');
277
- const about = fieldOf(line, 'about');
278
- let selected;
279
- try {
280
- selected = JSON.parse(about);
281
- }
282
- catch {
283
- return fail('prospective source mapping JSON invalid');
284
- }
285
- if (!selected || Object.keys(selected).sort().join(',') !== 'bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector' || selected.profile !== 'place-act-context@1' || JSON.stringify(selected) !== about)
286
- fail('unsupported prospective source mapping');
287
- sourceSelection(selected);
288
- const positive = (scope, measure) => signed.filter(l => fieldOf(l, 'scope') === scope && fieldOf(l, 'measure') === measure && fieldOf(l, 'value') !== 'withdraw');
289
- if (!oldMapping && (positive(sourceScope, 'text').length !== 1 || positive(selected.selector, 'text').length !== 1))
290
- fail('activation requires source and selection together');
291
- const checkpoint = signed.filter(isConfig).at(-1) ?? fail('prospective authority checkpoint absent');
292
- if (!oldMapping && positive(selected.bootstrapCheckpoint.scope, selected.bootstrapCheckpoint.measure).length !== 1)
293
- fail('initial authority checkpoint must land with selection');
294
- const old = oldMapping ? declaredRootClockSource(root) : undefined;
295
- const actualLegacy = [...new Set([...before, ...placesIn(root).flatMap(place => appliedHistoryIn(root, place))])];
296
- const observed = { history, anchor, algorithms, authorityCheckpoint: checkpoint, legacyOriginals: actualLegacy };
297
- const resolved = hostClockSelection({ store, selector: selected.selector, profileSelector: selected.profileSelector, ...observed, resources: { bytes: selected.resources.bytes, lines: selected.resources.lines, places: selected.resources.places }, inherited: selected.inherited, readCurrent: () => observed, historical: encoded => { if (!old)
298
- return fail('committed receiver history precedes initial selection'); return old.trust.historical(encoded); } });
299
- const heads = placeActHeads(store, resolved.trust.initialHeads, resolved.trust, resolved.selection.storageBudget), expected = old ? old.trust.current().heads : resolved.trust.initialHeads;
300
- for (const head of expected) {
301
- const actual = heads.find(one => one.place === head.place);
302
- if (!actual || actual.identity !== head.identity || actual.epoch !== head.epoch)
303
- fail('prospective selection resets committed receiver ' + head.place);
304
- }
305
- }
1
+ var D=Object.defineProperty;var y=(e,t)=>D(e,"name",{value:t,configurable:!0});import{existsSync as U,opendirSync as Q,statSync as V}from"node:fs";import{join as _,basename as Y}from"node:path";import{committedLocalActs as Z,actLines as K}from"./ports/local-act.js";import{verifiedLegacyActResult as $}from"../cli/act-result.js";import{admissionAnchor as I,admissionHistoryAt as L}from"../fold/admission-history.js";import{authorityFor as ee}from"../fold/signers.js";import{signatureEraOf as H}from"./signature-era.js";import{fieldOf as i,foldClaims as F,isConfig as A,selfName as te}from"../fold/claims.js";import{authenticatedRootStanding as W}from"./admitted-place.js";import{verifiesOwnerLine as q}from"../land/sign.js";import{placesIn as G}from"../fold/places.js";import{appliedHistoryIn as T}from"../fold/signed.js";import{storeOf as M}from"../land/ledger.js";import{hostClockSelection as J}from"./place-clock-selection.js";import{library3_readPlaceActContext as B,library3_placeActReceiptsOf as re}from"../vendor/runtime.js";import{placeActResult as oe,placeActHeads as ie}from"./ports/place-act.js";import{rootAuthorityProtection as se}from"../land/act.js";import{readOnly as ce}from"./read-only.js";const o=y(e=>{throw Error("REFUSE\xB7Root cursor "+e)},"fail");function z(e){const t=e?.resources;return(!t||Object.keys(t).sort().join(",")!=="acts,bytes,lines,places"||Object.values(t).some(r=>!Number.isSafeInteger(r)||r<1)||typeof e.selector!="string"||!e.selector||typeof e.profileSelector!="string"||!e.profileSelector||!e.bootstrapCheckpoint||Object.keys(e.bootstrapCheckpoint).sort().join(",")!=="measure,scope"||typeof e.bootstrapCheckpoint.scope!="string"||!e.bootstrapCheckpoint.scope||typeof e.bootstrapCheckpoint.measure!="string"||!e.bootstrapCheckpoint.measure)&&o("missing admitted source selection"),(!e.inherited?.clock||Object.keys(e.inherited.clock).sort().join(",")!=="bytes,placements,policies,proofs,records,signers"||!e.inherited.storage||Object.keys(e.inherited.storage).sort().join(",")!=="acts,bytes"||[...Object.values(e.inherited.clock),...Object.values(e.inherited.storage)].some(r=>!Number.isSafeInteger(r)||r<1))&&o("missing inherited source budgets"),t}y(z,"sourceSelection");function ne(e,t){const r=z(t),p=M(e);let k=0,g=0,a=0;const b=r.acts*(r.places+2)+r.lines;Number.isSafeInteger(b)||o("source file bound");const n=y(d=>{if(!U(d))return;const l=V(d);if(l.isDirectory()){const f=Q(d);try{for(let m=f.readSync();m;m=f.readSync())++a>b&&o("source entry budget"),!(Y(d)==="ledger"&&m.name==="place-acts")&&n(_(d,m.name))}finally{f.closeSync()}}else(!l.isFile()||++g>b||(k+=l.size)>r.bytes)&&o("source file/byte budget")},"visit");n(_(p,"ledger")),n(_(p,te()+".keys"))}y(ne,"boundedInventory");function P(e,t){const r=z(t);ne(e,t);const p=L(e,Number.MAX_SAFE_INTEGER);p.length>r.lines&&o("history line budget");let k=0;for(const c of p)(k+=Buffer.byteLength(c))>r.bytes&&o("history byte budget");const g=Z(M(e));g.length>r.acts&&o("act inventory budget");const a=g.flatMap(c=>{const h=c.bundle.placements?.filter(v=>v.place===".")??[];return h.some(v=>K(v.records).some(A))?[{...$(e,c.identity),identity:c.identity,roots:h}]:[]});a.sort((c,h)=>c.epoch-h.epoch),a.some((c,h)=>h>0&&c.epoch===a[h-1].epoch&&c.identity!==a[h-1].identity)&&o("ambiguous committed Root cursor");const b=a.at(-1),n=I(e);let d;if(b){const c=b.roots.flatMap(h=>K(h.records)).filter(A);c.length||o("verified Root act has no configuration checkpoint"),d=c[0]}else{const c=p.filter(h=>i(h,"scope")===t.bootstrapCheckpoint.scope&&i(h,"measure")===t.bootstrapCheckpoint.measure);c.length!==1&&o("bootstrap checkpoint missing/ambiguous"),d=c[0]}const l=Number(i(d,"epoch"));(!Number.isSafeInteger(l)||l<0)&&o("unknown committed Root epoch");const f=p.filter(c=>!A(c)||Number(i(c,"epoch"))<=l),m=H(F(f.filter(c=>i(c,"scope").startsWith("wire/"))).standing).admitted;ee(f,n,m).of(d).kind!=="admitted"&&o("checkpoint is not authenticated");const w=G(e);w.length>r.places&&o("legacy namespace inventory budget");const u=[...new Set([...p,...w.flatMap(c=>T(e,c))])];return(u.length+f.length>r.lines||u.reduce((c,h)=>c+Buffer.byteLength(h),0)+f.reduce((c,h)=>c+Buffer.byteLength(h),0)>r.bytes)&&o("legacy origin inventory budget"),{history:f,anchor:n,algorithms:m,authorityCheckpoint:d,legacyOriginals:u}}y(P,"current");function ae(e,t){const r=P(e,t),p=new Set,k=new Map,g=y(l=>{const f=k.get(l);if(f)return f;(p.has(l)||p.size>=t.resources.acts)&&o("historical predecessor cycle/budget"),p.add(l);try{const m=B(l,t.inherited.clock),O=m.root.epoch,w=L(e,Number(O)),u=w.filter(v=>A(v)&&i(v,"epoch")===O);u.length||o("historical checkpoint absent");const c=J({...a,history:w,authorityCheckpoint:u[0],legacyOriginals:w,authenticatedReceiverHeads:[],readCurrent:y(()=>P(e,t),"readCurrent")}),h=m.places.map(v=>{const N=c.trust.initialHeads.find(s=>s.place===v.place);if(N?.identity===v.previous)return N;const x=oe(a.store,v.previous,{historical:g},c.selection.storageBudget);x||o("historical predecessor absent");const j=B(x.context,t.inherited.clock),E=j.places.find(s=>s.place===v.place);return E||o("historical predecessor namespace absent"),{place:v.place,identity:v.previous,epoch:E.epoch}}),S=J({...a,history:w,authorityCheckpoint:u[0],legacyOriginals:w,authenticatedReceiverHeads:h,readCurrent:y(()=>P(e,t),"readCurrent")}).trust.contract;return k.set(l,S),S}finally{p.delete(l)}},"historical"),a={store:M(e),selector:t.selector,profileSelector:t.profileSelector,...r,resources:{bytes:t.resources.bytes,lines:t.resources.lines,places:t.resources.places},inherited:t.inherited,readCurrent:y(()=>P(e,t),"readCurrent"),historical:g},b=J(a),n=Object.freeze({...r.anchor,coverage:Object.freeze([...r.anchor.coverage]),depth:Object.freeze({...r.anchor.depth}),admittedBy:Object.freeze([...r.anchor.admittedBy])}),d=Object.freeze({history:Object.freeze([...r.history]),anchor:n,algorithms:Object.freeze([...r.algorithms])});return{...b,observation:d,resources:Object.freeze({...t.resources}),authority:y(l=>{const f=B(l,t.inherited.clock),m=L(e,Number(f.root.epoch)),O=m.filter(w=>A(w)&&i(w,"epoch")===f.root.epoch);return O.length||o("public historical checkpoint absent"),{anchor:r.anchor,history:m,checkpoint:O[0]}},"authority")}}y(ae,"rootClockSource");function pe(e,t){const r=e.filter(b=>i(b,"scope")==="wire/place-act-context-source"&&i(b,"measure")==="text"&&i(b,"value")!=="withdraw");r.length!==1&&o("requires one admitted source mapping");const p=r[0],k=H(e).admitted;(i(p,"by")!==t.id||i(p,"role")!=="writes"||i(p,"form")!=="alphabet"||i(p,"value")!=="lock"||!q(p,t.publicKey,k))&&o("mapping is not Root signed");const g=i(p,"about");let a;try{a=JSON.parse(g)}catch{return o("mapping JSON invalid")}return(!a||Object.keys(a).sort().join(",")!=="bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector"||a.profile!=="place-act-context@1"||JSON.stringify(a)!==g)&&o("unknown/noncanonical mapping"),z(a),a}y(pe,"rootClockSelectionOf");function le(e){return ae(e,pe(W(e),I(e)))}y(le,"declaredRootClockSource");function Je(e){return W(e).some(t=>i(t,"scope")==="wire/place-act-context-source"&&i(t,"measure")==="text"&&i(t,"value")!=="withdraw")}y(Je,"hasRootClockSource");function He(e,t,r){z(r),(typeof e!="string"||Buffer.byteLength(e)>r.resources.bytes)&&o("portable byte budget");let p;try{p=JSON.parse(e)}catch{return o("portable JSON")}(!Array.isArray(p)||p.length>r.resources.acts)&&o("portable context inventory budget");const k=new Map;for(const n of p)(!n||typeof n.identity!="string"||typeof n.context!="string"||!n.authority||JSON.stringify(n.authority.anchor)!==JSON.stringify(t)||!Array.isArray(n.authority.history)||!Array.isArray(n.receipts)||k.has(n.identity))&&o("portable anchor/shape/identity"),k.set(n.identity,n);const g=new Map,a=new Set,b=y(n=>{const d=g.get(n);if(d)return d;const l=k.get(n);(!l||a.has(n))&&o("portable predecessor missing/cyclic"),a.add(n);try{const f=B(l.context,r.inherited.clock),m={store:"",selector:r.selector,profileSelector:r.profileSelector,history:l.authority.history,anchor:t,algorithms:H(F(l.authority.history.filter(S=>i(S,"scope").startsWith("wire/"))).standing).admitted,legacyOriginals:l.authority.history,authorityCheckpoint:l.authority.checkpoint,resources:{bytes:r.resources.bytes,lines:r.resources.lines,places:r.resources.places},inherited:r.inherited,authenticatedReceiverHeads:[],readCurrent:y(()=>o("portable inspection has no current Root authority"),"readCurrent"),historical:y(()=>o("portable inspection uses declared predecessor graph"),"historical")},O=J(m),w=f.places.map(S=>{const v=O.trust.initialHeads.find(j=>j.place===S.place);if(v?.identity===S.previous)return v;const N=b(S.previous),x=N.context.places.find(j=>j.place===S.place);return x||o("portable predecessor namespace"),{place:S.place,identity:S.previous,epoch:x.epoch}}),u=J({...m,authenticatedReceiverHeads:w}).trust.contract;re(l.context,l.receipts,u).identity!==n&&o("portable identity differs");const h={contract:u,context:f};return g.set(n,h),h}finally{a.delete(n)}},"inspect");for(const n of k.keys())b(n);return[...g].map(([n,d])=>({identity:n,context:d.context}))}y(He,"inspectPortablePlaceContexts");function ze(e,t){const r="wire/place-act-context-source",p=L(e,Number.MAX_SAFE_INTEGER),k=[r,"wire/place-act-context-selection","wire/place-act-context"];if(!p.some(s=>i(s,"scope")===r)&&!t.some(s=>k.includes(i(s,"scope"))))return;const g=W(e).filter(s=>i(s,"value")!=="withdraw"),a=g.find(s=>i(s,"scope")===r&&i(s,"measure")==="text");if(a){const s=I(e),R=H(g.filter(X=>i(X,"scope").startsWith("wire/"))).admitted;(i(a,"by")!==s.id||!q(a,s.publicKey,R))&&o("existing source mapping is not Root signed");let C;try{C=JSON.parse(i(a,"about"))}catch{return o("existing source mapping JSON invalid")}k.push(C.selector,C.profileSelector)}if(!t.some(s=>k.includes(i(s,"scope")))||t.every(s=>p.includes(s)))return;const b=M(e);ce()||se(b);const n=[...new Set([...p,...t])],d=F([...g,...t].filter(A)).standing.filter(s=>i(s,"value")!=="withdraw"),l=d.filter(s=>i(s,"scope")===r&&i(s,"measure")==="text");if(!l.length)return t.some(s=>i(s,"scope")===r&&i(s,"value")==="withdraw")?void 0:o("activation requires source and selection together");l.length!==1&&o("prospective source mapping ambiguous");const f=l[0],m=I(e),O=H(d.filter(s=>i(s,"scope").startsWith("wire/"))).admitted;(i(f,"by")!==m.id||i(f,"role")!=="writes"||i(f,"form")!=="alphabet"||i(f,"value")!=="lock"||!q(f,m.publicKey,O))&&o("prospective mapping is not Root signed");const w=i(f,"about");let u;try{u=JSON.parse(w)}catch{return o("prospective source mapping JSON invalid")}(!u||Object.keys(u).sort().join(",")!=="bootstrapCheckpoint,inherited,profile,profileSelector,resources,selector"||u.profile!=="place-act-context@1"||JSON.stringify(u)!==w)&&o("unsupported prospective source mapping"),z(u);const c=y((s,R)=>t.filter(C=>i(C,"scope")===s&&i(C,"measure")===R&&i(C,"value")!=="withdraw"),"positive");!a&&(c(r,"text").length!==1||c(u.selector,"text").length!==1)&&o("activation requires source and selection together");const h=t.filter(A).at(-1)??o("prospective authority checkpoint absent");!a&&c(u.bootstrapCheckpoint.scope,u.bootstrapCheckpoint.measure).length!==1&&o("initial authority checkpoint must land with selection");const S=a?le(e):void 0,v=[...new Set([...p,...G(e).flatMap(s=>T(e,s))])],N={history:n,anchor:m,algorithms:O,authorityCheckpoint:h,legacyOriginals:v},x=J({store:b,selector:u.selector,profileSelector:u.profileSelector,...N,resources:{bytes:u.resources.bytes,lines:u.resources.lines,places:u.resources.places},inherited:u.inherited,readCurrent:y(()=>N,"readCurrent"),historical:y(s=>S?S.trust.historical(s):o("committed receiver history precedes initial selection"),"historical")}),j=ie(b,x.trust.initialHeads,x.trust,x.selection.storageBudget),E=S?S.trust.current().heads:x.trust.initialHeads;for(const s of E){const R=j.find(C=>C.place===s.place);(!R||R.identity!==s.identity||R.epoch!==s.epoch)&&o("prospective selection resets committed receiver "+s.place)}}y(ze,"assertProspectiveRootClockSelection");export{ze as assertProspectiveRootClockSelection,le as declaredRootClockSource,Je as hasRootClockSource,He as inspectPortablePlaceContexts,pe as rootClockSelectionOf,ae as rootClockSource};