@lapxo/bound 0.1.8 → 0.1.9

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 (128) hide show
  1. package/README.md +56 -20
  2. package/TARGET.bound +25 -25
  3. package/dist/cli/accept.js +83 -59
  4. package/dist/cli/act-result.d.ts +9 -0
  5. package/dist/cli/act-result.js +68 -13
  6. package/dist/cli/args.d.ts +1 -0
  7. package/dist/cli/args.js +5 -2
  8. package/dist/cli/custodian.d.ts +1 -0
  9. package/dist/cli/custodian.js +29 -0
  10. package/dist/cli/native-descendant.d.ts +1 -0
  11. package/dist/cli/native-descendant.js +19 -0
  12. package/dist/cli/native.d.ts +1 -0
  13. package/dist/cli/native.js +60 -0
  14. package/dist/cli/observe.js +9 -2
  15. package/dist/cli/pass.js +6 -1
  16. package/dist/cli/standing-result.js +7 -26
  17. package/dist/cli/verb.js +24 -5
  18. package/dist/fold/admission-history.js +2 -2
  19. package/dist/fold/claims.d.ts +4 -0
  20. package/dist/fold/claims.js +23 -3
  21. package/dist/fold/closed.d.ts +1 -1
  22. package/dist/fold/closed.js +16 -4
  23. package/dist/fold/digests.d.ts +1 -1
  24. package/dist/fold/digests.js +3 -2
  25. package/dist/fold/evidence.d.ts +1 -1
  26. package/dist/fold/kept.js +2 -1
  27. package/dist/fold/observed.d.ts +1 -0
  28. package/dist/fold/observed.js +54 -9
  29. package/dist/fold/readers.d.ts +4 -3
  30. package/dist/fold/readers.js +18 -13
  31. package/dist/fold/resolved.js +11 -1
  32. package/dist/fold/signers.js +2 -2
  33. package/dist/host/adapters/modules/javascript.d.ts +2 -0
  34. package/dist/host/adapters/modules/javascript.js +8 -6
  35. package/dist/host/admitted-place.d.ts +5 -0
  36. package/dist/host/admitted-place.js +54 -0
  37. package/dist/host/capsule-process.d.ts +1 -1
  38. package/dist/host/capsule-process.js +4 -1
  39. package/dist/host/capsule.d.ts +4 -0
  40. package/dist/host/capsule.js +16 -2
  41. package/dist/host/contextual-place.d.ts +59 -0
  42. package/dist/host/contextual-place.js +77 -0
  43. package/dist/host/hash.d.ts +7 -0
  44. package/dist/host/hash.js +6 -0
  45. package/dist/host/io.d.ts +2 -0
  46. package/dist/host/io.js +2 -0
  47. package/dist/host/lifetime.js +61 -22
  48. package/dist/host/native/bounded-file.d.ts +8 -0
  49. package/dist/host/native/bounded-file.js +32 -0
  50. package/dist/host/native/command.d.ts +36 -0
  51. package/dist/host/native/command.js +91 -0
  52. package/dist/host/native/control-bridge-worker.d.ts +1 -0
  53. package/dist/host/native/control-bridge-worker.js +88 -0
  54. package/dist/host/native/control-bridge.d.ts +30 -0
  55. package/dist/host/native/control-bridge.js +110 -0
  56. package/dist/host/native/control-selection.d.ts +29 -0
  57. package/dist/host/native/control-selection.js +32 -0
  58. package/dist/host/native/custody.d.ts +5 -0
  59. package/dist/host/native/custody.js +91 -0
  60. package/dist/host/native/deadline.d.ts +12 -0
  61. package/dist/host/native/deadline.js +28 -0
  62. package/dist/host/native/descendants.d.ts +1 -0
  63. package/dist/host/native/descendants.js +528 -0
  64. package/dist/host/native/generated-custodian.d.ts +122 -0
  65. package/dist/host/native/generated-custodian.js +267 -0
  66. package/dist/host/native/generated.d.ts +217 -0
  67. package/dist/host/native/generated.js +379 -0
  68. package/dist/host/native/image.d.ts +36 -0
  69. package/dist/host/native/image.js +45 -0
  70. package/dist/host/native/jobs.d.ts +75 -0
  71. package/dist/host/native/jobs.js +148 -0
  72. package/dist/host/native/loader.d.ts +80 -0
  73. package/dist/host/native/loader.js +269 -0
  74. package/dist/host/native/materialize.d.ts +22 -0
  75. package/dist/host/native/materialize.js +175 -0
  76. package/dist/host/native/module-closure.d.ts +27 -0
  77. package/dist/host/native/module-closure.js +99 -0
  78. package/dist/host/native/module-syntax.d.ts +8 -0
  79. package/dist/host/native/module-syntax.js +63 -0
  80. package/dist/host/native/native-binary.d.ts +54 -0
  81. package/dist/host/native/native-binary.js +115 -0
  82. package/dist/host/native/native-program.d.ts +56 -0
  83. package/dist/host/native/native-program.js +72 -0
  84. package/dist/host/native/node-jobs.d.ts +84 -0
  85. package/dist/host/native/node-jobs.js +187 -0
  86. package/dist/host/native/node-launch.d.ts +72 -0
  87. package/dist/host/native/node-launch.js +159 -0
  88. package/dist/host/native/preload.d.ts +1 -0
  89. package/dist/host/native/preload.js +45 -0
  90. package/dist/host/native/reading.d.ts +91 -0
  91. package/dist/host/native/reading.js +249 -0
  92. package/dist/host/native/receipt-environment.d.ts +3 -0
  93. package/dist/host/native/receipt-environment.js +41 -0
  94. package/dist/host/native/references.d.ts +5 -0
  95. package/dist/host/native/references.js +30 -0
  96. package/dist/host/native/root.d.ts +17 -0
  97. package/dist/host/native/root.js +37 -0
  98. package/dist/host/native/route.d.ts +66 -0
  99. package/dist/host/native/route.js +216 -0
  100. package/dist/host/native/runtime.d.ts +4 -0
  101. package/dist/host/native/runtime.js +9 -0
  102. package/dist/host/native-reader-context.d.ts +3 -0
  103. package/dist/host/native-reader-context.js +8 -0
  104. package/dist/host/place-clock-selection.d.ts +55 -0
  105. package/dist/host/place-clock-selection.js +124 -0
  106. package/dist/host/ports/local-act.d.ts +10 -0
  107. package/dist/host/ports/local-act.js +78 -6
  108. package/dist/host/ports/place-act.d.ts +46 -0
  109. package/dist/host/ports/place-act.js +169 -0
  110. package/dist/host/process-lifetime.js +8 -3
  111. package/dist/host/provide.d.ts +25 -0
  112. package/dist/host/provide.js +115 -13
  113. package/dist/host/reading-closures.d.ts +15 -1
  114. package/dist/host/reading-closures.js +88 -6
  115. package/dist/host/reading-context.d.ts +29 -0
  116. package/dist/host/reading-context.js +53 -0
  117. package/dist/host/root-clock-source.d.ts +85 -0
  118. package/dist/host/root-clock-source.js +305 -0
  119. package/dist/host/selected-capsules.js +3 -2
  120. package/dist/land/act.d.ts +26 -0
  121. package/dist/land/act.js +96 -3
  122. package/dist/land/ledger.d.ts +4 -0
  123. package/dist/land/ledger.js +20 -2
  124. package/dist/observe/runner.d.ts +4 -0
  125. package/dist/observe/runner.js +9 -2
  126. package/docs/contracts/cli.md +13 -0
  127. package/package.json +2 -2
  128. package/receipts.bound +296 -256
@@ -1,3 +1,5 @@
1
+ import { assertProspectiveRootClockSelection } from "../host/root-clock-source.js";
2
+ import { hasContextualPlace, landContextualPlace } from "../host/contextual-place.js";
1
3
  import { commitLocalAct, intactLocalAct, committedLocalActs } from "../host/ports/local-act.js";
2
4
  import { withoutEffects } from "../host/read-only.js";
3
5
  import { admittedLine } from "../host/selected-topos.js";
@@ -26,7 +28,7 @@ import { retractedTogether, withdrawal } from "../land/withdraw.js";
26
28
  import { isCeiling } from "../fold/configures.js";
27
29
  import { inertIn } from "../land/inert.js";
28
30
  import { landSaid } from "./pass.js";
29
- import { underTheLock, underTheRegions } from "../land/act.js";
31
+ import { underTheLock, underTheRegions, underAuthorityRegions, underRootProjection } from "../land/act.js";
30
32
  import { openedBy } from "../fold/opened.js";
31
33
  import { touchedBy } from "../fold/reach.js";
32
34
  import { landOwned, ownedOf } from "../land/owned.js";
@@ -46,6 +48,7 @@ import { judgeDemand, landTake } from "./takes.js";
46
48
  function admissionEvidence(root, signed, standing) {
47
49
  return withoutEffects(() => {
48
50
  const store = storeOf(root);
51
+ assertProspectiveRootClockSelection(root, signed);
49
52
  const reads = !signed.some(isReaderLock) && signed.some(isCeiling);
50
53
  const fold = reads ? coneOf(root, entryOf(root), signed.filter(isCeiling)) : { standing, observed: [], own: {}, kept: true };
51
54
  const inert = inertIn(signed, { standing: fold.standing, ceiling: isCeiling, signsReader: !reads, observed: fold.observed, own: fold.own, epoch: epochOf([...lockLines(store), ...signed]) });
@@ -277,6 +280,14 @@ export async function signedBatch(root, args, files) {
277
280
  */
278
281
  export async function accept(root, args) {
279
282
  const started = Date.now();
283
+ const contextFiles = positionalsOf(args);
284
+ if (contextFiles.length === 1 && hasContextualPlace(root) && !claimLinesIn(contextFiles[0]).length) {
285
+ if (namedPlaces(args).length || valueOf(args, FLAGS.key) || valueOf(args, FLAGS.keyFile) || valueOf(args, FLAGS.signer))
286
+ throw Error('REFUSE·contextual place accepts one already signed context envelope');
287
+ const result = await landContextualPlace(root, contextFiles[0]);
288
+ process.stdout.write(JSON.stringify({ profile: 'place-act-context@1', identity: result.identity, status: result.kind, context: result.context, receipts: result.receipts }) + '\n');
289
+ return 0;
290
+ }
280
291
  const named = namedPlaces(args);
281
292
  const placed = positionalsOf(args).reduce((all, file) => ((places) => (places.length ? claimLinesIn(file).reduce((got, line) => got.set(sayingOf(line), [...new Set([...(got.get(sayingOf(line)) ?? []), ...places])]), all) : all))(placesOfFile(file)), new Map());
282
293
  const batch = await signedBatch(root, args, positionalsOf(args));
@@ -301,7 +312,11 @@ export async function accept(root, args) {
301
312
  const where = every ? 'every place · then the root, where the places join' : regions.length ? regions.map((one) => `${one}/`).join(' · ') : 'the root alone, where the places join';
302
313
  process.stdout.write(`ACTS ${where} · ${owned.ledger.length} lines to the ledger${[...owned.own].map(([place, lines]) => ` · ${lines.length} to ${place}'s own lock`).join('')} · before the lock\n`);
303
314
  const asked = Date.now();
304
- return underTheRegions(store, every ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions));
315
+ // Root-ledger writes may change grants or inherited policies. Child-only
316
+ // lots read that authority through publication, without excluding siblings.
317
+ const rootWrite = owned.ledger.length > 0 || every || regions.length === 0;
318
+ const enter = rootWrite ? underTheRegions : underAuthorityRegions;
319
+ return enter(store, rootWrite ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions));
305
320
  }
306
321
  async function acceptNow(root, batch, started, owned, asked = started, descent = []) {
307
322
  const waited = Date.now() - asked;
@@ -318,6 +333,13 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
318
333
  process.stderr.write(`${selfName()}: REFUSE·base moved ${batch.base.digest} → ${currentBase} · nothing landed\n`);
319
334
  return 1;
320
335
  }
336
+ try {
337
+ assertProspectiveRootClockSelection(root, signed);
338
+ }
339
+ catch (error) {
340
+ process.stderr.write(`${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
341
+ return 1;
342
+ }
321
343
  if (signed.some(line => !isConfig(line))) {
322
344
  try {
323
345
  await admittedObjects(root, [...signedOwnerLines(store), ...signed]);
@@ -396,67 +418,69 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
396
418
  landed = took('land', () => [...new Set(signed.map(line => fieldOf(line, 'by')))].map(by => land(store, by, signed.filter(line => fieldOf(line, 'by') === by))));
397
419
  for (const [place, lines] of owned.own)
398
420
  landOwned(root, place, lines, resultProfile !== undefined);
399
- landSaid(store, writerFor(told.standing, 'fold'), []);
400
- for (const line of signed) {
401
- const value = fieldOf(line, 'value');
402
- if (!/^[a-z0-9-]+:[0-9a-f]{32,}$/.test(value))
403
- continue;
404
- for (const need of locationsOf(fieldOf(line, 'needs')).locations) {
405
- const at = join(root, need);
406
- const bytes = observeFile(at);
407
- if (bytes === undefined)
421
+ return underRootProjection(store, 'projection', async () => {
422
+ landSaid(store, writerFor(told.standing, 'fold'), []);
423
+ for (const line of signed) {
424
+ const value = fieldOf(line, 'value');
425
+ if (!/^[a-z0-9-]+:[0-9a-f]{32,}$/.test(value))
408
426
  continue;
409
- if (bytesDigest(store, bytes) === value)
410
- landBlob(store, value, bytes);
427
+ for (const need of locationsOf(fieldOf(line, 'needs')).locations) {
428
+ const at = join(root, need);
429
+ const bytes = observeFile(at);
430
+ if (bytes === undefined)
431
+ continue;
432
+ if (bytesDigest(store, bytes) === value)
433
+ landBlob(store, value, bytes);
434
+ }
411
435
  }
412
- }
413
- if (coordinates.taken.length) {
414
- for (const path of coordinates.taken) {
415
- if (existsSync(join(root, path)))
416
- rmSync(join(root, path), { recursive: true });
417
- for (let dir = dirname(path); dir !== '.' && existsSync(join(root, dir)) && !entriesIn(join(root, dir)).length; dir = dirname(dir))
418
- rmdirSync(join(root, dir));
436
+ if (coordinates.taken.length) {
437
+ for (const path of coordinates.taken) {
438
+ if (existsSync(join(root, path)))
439
+ rmSync(join(root, path), { recursive: true });
440
+ for (let dir = dirname(path); dir !== '.' && existsSync(join(root, dir)) && !entriesIn(join(root, dir)).length; dir = dirname(dir))
441
+ rmdirSync(join(root, dir));
442
+ }
443
+ landCoordinates(store, new Map(), coordinates.taken, `withdraw:epoch-${epoch}`);
444
+ for (const path of coordinates.taken)
445
+ process.stdout.write(`GONE ${path} · withdrawn by its line\n`);
419
446
  }
420
- landCoordinates(store, new Map(), coordinates.taken, `withdraw:epoch-${epoch}`);
421
- for (const path of coordinates.taken)
422
- process.stdout.write(`GONE ${path} · withdrawn by its line\n`);
423
- }
424
- for (const one of judged)
425
- landTake(root, one, []);
426
- const standingAsked = [...signed, ...fold.standing].filter((line) => fieldOf(line, 'role') === 'demands' && fieldOf(line, 'value') !== 'withdraw');
427
- const paidScopes = new Set(paidOf(store, standingAsked, fold.observed, view).paid.map((line) => fieldOf(line, 'scope')));
428
- const late = lateFacts(store, [...signed, ...fold.standing], paidScopes);
429
- if (late.length) {
430
- land(store, 'judge', late);
431
- for (const line of late)
432
- if (fieldOf(line, 'measure') !== 'cone')
433
- process.stdout.write(`LATE ${fieldOf(line, 'scope')} · answered from the landed cone\n`);
434
- }
435
- if (signed.some((line) => fieldOf(line, 'scope').startsWith(`${basename(ownRoot())}/`) && !fieldOf(line, 'scope').split('/').slice(1, -1).includes('uses'))) {
436
- const code = instrumentOf(store, entryOf(root), told.standing, root, ownLock());
437
- if (instrumentKept(store) !== code) {
438
- const open = await closeReceipts(root, store);
439
- instrumentKeep(store, code);
440
- process.stderr.write(`open by instrument · ${open.length}\n`);
447
+ for (const one of judged)
448
+ landTake(root, one, []);
449
+ const standingAsked = [...signed, ...fold.standing].filter((line) => fieldOf(line, 'role') === 'demands' && fieldOf(line, 'value') !== 'withdraw');
450
+ const paidScopes = new Set(paidOf(store, standingAsked, fold.observed, view).paid.map((line) => fieldOf(line, 'scope')));
451
+ const late = lateFacts(store, [...signed, ...fold.standing], paidScopes);
452
+ if (late.length) {
453
+ land(store, 'judge', late);
454
+ for (const line of late)
455
+ if (fieldOf(line, 'measure') !== 'cone')
456
+ process.stdout.write(`LATE ${fieldOf(line, 'scope')} · answered from the landed cone\n`);
441
457
  }
442
- }
443
- const rendered = took('render', () => renderInto(root, store));
444
- process.stdout.write(`RENDER 0\n`);
445
- const own = new Set([...owned.own.values()].flat()).size;
446
- const perLine = Math.round((Date.now() - started) / Math.max(1, signed.length + own));
447
- const folder = writerFor(fold.standing, 'fold');
448
- const beat = (ms, at) => canonical({ scope: 'beat/accept', role: 'writes', form: 'interval', measure: 'ms', value: `${ms}..${ms}`, by: folder ?? '', at: `place:${at}` });
449
- if (folder !== undefined)
450
- land(store, folder, [beat(perLine, 'line'), beat(1, fold.kept ? 'kept' : 'folded'), beat(Date.now() - started, 'total')]);
451
- process.stdout.write(`FACT signed ${signed.length + own} · landed ${landed.length ? landed.map((one) => `${one.appended} in ${one.at}`).join(' · ') : '0'}${[...owned.own].map(([place, lines]) => ` · ${lines.length} in ${place}/${LOCK}`).join('')} · epoch ${epoch}\n`
452
- + `TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · readings · no encounters computed · ${Date.now() - started} ms\n`
453
- + `SPENT ${spent.join(' ms · ')} ms · ${signed.length} lines\n`);
454
- if (withdrawn) {
455
- process.stdout.write(`WITHDRAW ${withdrawn.taken.length} taken · ${withdrawn.exact.length} exact on distributive scales · ${withdrawn.open.length} on open scales, not computed\n`);
456
- for (const line of withdrawn.inexact)
457
- process.stdout.write(`INEXACT ${fieldOf(line, 'scope')} · its scale is not distributive: what the others hold is not what they held without it\n`);
458
- }
459
- return 0;
458
+ if (signed.some((line) => fieldOf(line, 'scope').startsWith(`${basename(ownRoot())}/`) && !fieldOf(line, 'scope').split('/').slice(1, -1).includes('uses'))) {
459
+ const code = instrumentOf(store, entryOf(root), told.standing, root, ownLock());
460
+ if (instrumentKept(store) !== code) {
461
+ const open = await closeReceipts(root, store);
462
+ instrumentKeep(store, code);
463
+ process.stderr.write(`open by instrument · ${open.length}\n`);
464
+ }
465
+ }
466
+ const rendered = took('render', () => renderInto(root, store));
467
+ process.stdout.write(`RENDER 0\n`);
468
+ const own = new Set([...owned.own.values()].flat()).size;
469
+ const perLine = Math.round((Date.now() - started) / Math.max(1, signed.length + own));
470
+ const folder = writerFor(fold.standing, 'fold');
471
+ const beat = (ms, at) => canonical({ scope: 'beat/accept', role: 'writes', form: 'interval', measure: 'ms', value: `${ms}..${ms}`, by: folder ?? '', at: `place:${at}` });
472
+ if (folder !== undefined)
473
+ land(store, folder, [beat(perLine, 'line'), beat(1, fold.kept ? 'kept' : 'folded'), beat(Date.now() - started, 'total')]);
474
+ process.stdout.write(`FACT signed ${signed.length + own} · landed ${landed.length ? landed.map((one) => `${one.appended} in ${one.at}`).join(' · ') : '0'}${[...owned.own].map(([place, lines]) => ` · ${lines.length} in ${place}/${LOCK}`).join('')} · epoch ${epoch}\n`
475
+ + `TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · readings · no encounters computed · ${Date.now() - started} ms\n`
476
+ + `SPENT ${spent.join(' ms · ')} ms · ${signed.length} lines\n`);
477
+ if (withdrawn) {
478
+ process.stdout.write(`WITHDRAW ${withdrawn.taken.length} taken · ${withdrawn.exact.length} exact on distributive scales · ${withdrawn.open.length} on open scales, not computed\n`);
479
+ for (const line of withdrawn.inexact)
480
+ process.stdout.write(`INEXACT ${fieldOf(line, 'scope')} · its scale is not distributive: what the others hold is not what they held without it\n`);
481
+ }
482
+ return 0;
483
+ });
460
484
  }
461
485
  /**
462
486
  * A batch landed twice is one: nothing of it lands again. What it left unpaid is judged again, the way it would be now:
@@ -1,3 +1,12 @@
1
+ import { type ActResult } from '@lapxo/topos/wire';
2
+ import { type LocalActBundle } from '../host/ports/local-act.ts';
1
3
  /** Public query of a committed identity, using the same admission authority.
2
4
  * Native fold evidence has store provenance; it never grants coverage. */
5
+ export declare function verifiedLegacyActResult(root: string, identity: string): {
6
+ result: ActResult;
7
+ bundle: LocalActBundle;
8
+ records: readonly string[];
9
+ epoch: number;
10
+ history: readonly string[];
11
+ };
3
12
  export declare function printActResult(root: string, identity: string | undefined): void;
@@ -1,31 +1,68 @@
1
+ import { hashText } from "../host/hash.js";
2
+ import { hasContextualPlace, contextualAct } from "../host/contextual-place.js";
3
+ import { createHash } from 'node:crypto';
1
4
  import { actResultOf, actResultProfile } from '@lapxo/topos/wire';
2
5
  import { actLines, intactLocalAct, localActResult } from "../host/ports/local-act.js";
3
6
  import { storeOf } from "../land/ledger.js";
4
7
  import { lockStanding } from "../fold/keys.js";
5
8
  import { authorityFor, writerFor } from "../fold/signers.js";
6
9
  import { eraOf } from "../fold/digests.js";
7
- import { fieldOf, isConfig, foldClaims } from "../fold/claims.js";
10
+ import { fieldOf, isConfig, foldClaims, parsedLine } from "../fold/claims.js";
8
11
  import { ownRecordAdmitted } from "../fold/signed.js";
9
12
  import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
10
13
  import { fullDigest } from "../host/digest.js";
11
14
  import { wordOf, wireLine } from "../fold/wire.js";
12
15
  import { viewsOf } from "../fold/views.js";
16
+ /** Historical proof only. Exact current bodies, admission history and bootstrap
17
+ * anchor are reread before lookup; this never retains a current command grant. */
18
+ const historicalProofs = new WeakMap();
19
+ function proofFingerprint(identity, bundle, history, anchor, algorithms) {
20
+ const hash = createHash('sha256'), text = hashText.bind(undefined, hash);
21
+ text('legacy-act-historical-proof/1');
22
+ text(identity);
23
+ text(bundle.records);
24
+ text(bundle.receipt);
25
+ text(bundle.placements === undefined ? 'absent' : 'present');
26
+ text(String(bundle.placements?.length ?? 0));
27
+ for (const placement of bundle.placements ?? []) {
28
+ text(placement.place);
29
+ text(placement.records);
30
+ }
31
+ text(String(history.length));
32
+ for (const line of history)
33
+ text(line);
34
+ text(JSON.stringify(anchor));
35
+ text(String(algorithms.length));
36
+ for (const algorithm of algorithms)
37
+ text(algorithm);
38
+ return hash.digest('hex');
39
+ }
40
+ const freshResult = (result) => ({ ...result, records: [...result.records] });
13
41
  /** Public query of a committed identity, using the same admission authority.
14
42
  * Native fold evidence has store provenance; it never grants coverage. */
15
- export function printActResult(root, identity) {
16
- const store = storeOf(root), standing = lockStanding(store);
17
- const view = viewsOf(standing).get('act');
18
- if (!view || view.shape || !view.regions.some(region => region.name === 'act'))
19
- throw Error('REFUSE·view act requires an admitted result profile and act view');
20
- if (!identity)
21
- throw Error('REFUSE·act name one committed identity');
22
- const bundle = localActResult(store, identity, intactLocalAct);
43
+ export function verifiedLegacyActResult(root, identity) {
44
+ const store = storeOf(root);
45
+ let observation;
46
+ const bundle = localActResult(store, identity, (id, current) => {
47
+ const receipt = parsedLine(current.receipt.trim());
48
+ if (receipt.kind !== 'fact')
49
+ return intactLocalAct(id, current);
50
+ const epoch = Number(fieldOf(current.receipt.trim(), 'epoch'));
51
+ const history = admissionHistoryAt(root, epoch), before = history.filter(line => !isConfig(line) || Number(fieldOf(line, 'epoch')) < epoch);
52
+ const algorithms = eraOf(foldClaims(before).standing).admitted, anchor = admissionAnchor(root);
53
+ const fingerprint = proofFingerprint(id, current, history, anchor, algorithms), cached = historicalProofs.get(receipt.value);
54
+ const retained = cached?.fingerprint === fingerprint ? cached.result : undefined;
55
+ observation = { epoch, history, before, algorithms, anchor, key: receipt.value, fingerprint, ...(retained ? { retained } : {}) };
56
+ return retained !== undefined || intactLocalAct(id, current);
57
+ });
23
58
  if (!bundle)
24
59
  throw Error('REFUSE·act no committed result ' + identity);
25
- const records = actLines(bundle.records), epoch = Number(fieldOf(bundle.receipt.trim(), 'epoch'));
26
- const history = admissionHistoryAt(root, epoch), before = history.filter(line => !isConfig(line) || Number(fieldOf(line, 'epoch')) < epoch);
27
- const algorithms = eraOf(foldClaims(before).standing).admitted;
28
- const authority = authorityFor(history, admissionAnchor(root), algorithms);
60
+ if (!observation)
61
+ throw Error('REFUSE·act historical proof observation absent');
62
+ const records = actLines(bundle.records), { epoch, history, before, algorithms, anchor, retained } = observation;
63
+ if (retained)
64
+ return { result: freshResult(retained), bundle, records, epoch, history };
65
+ const authority = authorityFor(history, anchor, algorithms);
29
66
  const admittedBefore = foldClaims(before.filter(line => authority.of(line).kind === 'admitted')).standing;
30
67
  const profile = actResultProfile(admittedBefore);
31
68
  if (!profile)
@@ -44,5 +81,23 @@ export function printActResult(root, identity) {
44
81
  return bundle.placements.some(p => actLines(p.records).includes(line) && (p.place === '.' ? authority.of(line).kind === 'admitted' : ownRecordAdmitted(line, p.place.slice(0, p.place.lastIndexOf('/')), family, authority.admitted, algorithms)));
45
82
  },
46
83
  verifiesReceipt: line => fieldOf(line, 'by') === writerFor(admittedBefore, 'fold') && intactLocalAct(identity, bundle), });
84
+ const kept = Object.freeze({ ...result, records: Object.freeze([...result.records]) });
85
+ historicalProofs.set(observation.key, { fingerprint: observation.fingerprint, result: kept });
86
+ return { result: freshResult(kept), bundle, records, epoch, history };
87
+ }
88
+ export function printActResult(root, identity) {
89
+ const store = storeOf(root), standing = lockStanding(store), view = viewsOf(standing).get('act');
90
+ if (!view || view.shape || !view.regions.some(region => region.name === 'act'))
91
+ throw Error('REFUSE·view act requires an admitted result profile and act view');
92
+ if (!identity)
93
+ throw Error('REFUSE·act name one committed identity');
94
+ if (hasContextualPlace(root)) {
95
+ const contextual = contextualAct(root, identity);
96
+ if (contextual) {
97
+ process.stdout.write(JSON.stringify(contextual) + '\n');
98
+ return;
99
+ }
100
+ }
101
+ const { result, bundle } = verifiedLegacyActResult(root, identity);
47
102
  process.stdout.write(`ACT ${result.identity} · epoch ${result.epoch} · admitted · conformance not asserted\n` + bundle.records + bundle.receipt);
48
103
  }
@@ -5,6 +5,7 @@ export declare function entryOf(root: string): string;
5
5
  export declare function valueOf(args: readonly string[], flag: string): string | undefined;
6
6
  export declare const valuesOf: (args: readonly string[], flag: string) => readonly string[];
7
7
  export declare const namedPlaces: (args: readonly string[]) => readonly string[];
8
+ export declare function positionalIndexes(args: readonly string[]): number[];
8
9
  export declare function positionalsOf(args: readonly string[]): string[];
9
10
  export declare const placesOfFile: (file: string) => readonly string[];
10
11
  export declare function claimLinesIn(file: string): string[];
package/dist/cli/args.js CHANGED
@@ -14,7 +14,7 @@ export function valueOf(args, flag) {
14
14
  }
15
15
  export const valuesOf = (args, flag) => args.flatMap((arg, i) => (arg === flag && args[i + 1] !== undefined ? [args[i + 1]] : []));
16
16
  export const namedPlaces = (args) => valuesOf(args, FLAGS.place).map((one) => one.replace(/\/$/, ''));
17
- export function positionalsOf(args) {
17
+ export function positionalIndexes(args) {
18
18
  const out = [];
19
19
  for (let i = 0; i < args.length; i += 1) {
20
20
  const arg = args[i] ?? '';
@@ -23,10 +23,13 @@ export function positionalsOf(args) {
23
23
  continue;
24
24
  }
25
25
  if (!arg.startsWith('--'))
26
- out.push(arg);
26
+ out.push(i);
27
27
  }
28
28
  return out;
29
29
  }
30
+ export function positionalsOf(args) {
31
+ return positionalIndexes(args).map(index => args[index]);
32
+ }
30
33
  export const placesOfFile = (file) => (observeText(file) ?? '').split('\n').map((line) => line.trim())
31
34
  .filter((line) => line.startsWith('# place ')).map((line) => line.slice('# place '.length).trim().replace(/\/$/, '')).filter(Boolean);
32
35
  export function claimLinesIn(file) {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ /** Fixed sync-to-async transport executable. It never resets producer counters
2
+ * or grants. Its stdin budget comes from the fixed host argv before reading.
3
+ */
4
+ import { createConnection } from 'node:net';
5
+ import { scheduleDeadline } from "../host/native/deadline.js";
6
+ const cap = Number(process.argv[2]);
7
+ if (!Number.isSafeInteger(cap) || cap < 1)
8
+ throw Error('REFUSE·producer sync client input resources');
9
+ const chunks = [];
10
+ let size = 0;
11
+ for await (const chunk of process.stdin) {
12
+ const raw = Buffer.from(chunk);
13
+ size += raw.length;
14
+ if (size > cap)
15
+ throw Error('REFUSE·producer sync client input bytes');
16
+ chunks.push(raw);
17
+ }
18
+ const { endpoint, frame } = JSON.parse(Buffer.concat(chunks).toString('utf8'));
19
+ if (!endpoint || typeof endpoint.socket !== 'string' || !Number.isSafeInteger(endpoint.timeoutMs) || endpoint.timeoutMs < 1 || !Number.isSafeInteger(endpoint.responseBytes) || endpoint.responseBytes < 1 || typeof frame !== 'string' || Buffer.byteLength(frame) > endpoint.responseBytes)
20
+ throw Error('REFUSE·producer sync client binding');
21
+ const socket = createConnection(endpoint.socket), remaining = Math.max(1, Math.min(endpoint.timeoutMs, endpoint.deadlineMs - Date.now())), cancel = scheduleDeadline(process.hrtime.bigint() + BigInt(remaining) * 1000000n, () => socket.destroy(Error('REFUSE·producer IPC timeout')));
22
+ let bytes = 0;
23
+ socket.once('connect', () => socket.write(frame + '\n'));
24
+ socket.on('data', chunk => { bytes += chunk.length; if (bytes > endpoint.responseBytes)
25
+ socket.destroy(Error('REFUSE·producer IPC response bytes'));
26
+ else
27
+ process.stdout.write(chunk); });
28
+ socket.once('error', error => { process.stderr.write(error.message); process.exitCode = 1; });
29
+ socket.once('close', cancel);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ /** Fixed descendant entry. The caller supplies only a sealed operational envelope;
2
+ * actual grants/counters live in the retained Root producer service.
3
+ */
4
+ import { createHash } from 'node:crypto';
5
+ import { readBoundedFile } from "../host/native/bounded-file.js";
6
+ import { installDescendantBoundary } from "../host/native/descendants.js";
7
+ import { protectNativeLoaderRegistration } from "../host/native/loader.js";
8
+ import { assertNativeRuntime } from "../host/native/runtime.js";
9
+ const url = new URL(import.meta.url), path = url.searchParams.get('config'), cap = Number(url.searchParams.get('bytes')), expected = url.searchParams.get('digest');
10
+ if (!path || !expected || !Number.isSafeInteger(cap) || cap < 1)
11
+ throw Error('REFUSE·native descendant inherited envelope absent');
12
+ assertNativeRuntime(process.env, []);
13
+ const raw = readBoundedFile(path, cap, 'descendant host envelope');
14
+ if ('sha256:' + createHash('sha256').update(raw).digest('hex') !== expected)
15
+ throw Error('REFUSE·native descendant envelope changed');
16
+ const config = JSON.parse(raw.toString('utf8'));
17
+ config.custodian.preload = url.href;
18
+ installDescendantBoundary(config);
19
+ protectNativeLoaderRegistration();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ import { sha256 as hash } from "../host/hash.js";
2
+ /** Fixed instrument child. Root authority and reading identity arrive through the
3
+ * inherited supervisor argv; request JSON supplies neither verifier nor grant.
4
+ */
5
+ import { realpathSync } from 'node:fs';
6
+ import { readingIdentityBytes } from '@lapxo/topos/contract';
7
+ import { readBoundedBatch } from "../host/native/bounded-file.js";
8
+ import { inheritedNativeGuard, inheritedNativeRoot } from "../host/native/root.js";
9
+ import { authenticateNativeProfile, loadSignedNativeReading, answerNativeReading } from "../host/native/reading.js";
10
+ import { readingEnvironment } from "../host/reading-context.js";
11
+ import { assertNativeRuntime } from "../host/native/runtime.js";
12
+ import { nativeReferenceInputs } from "../host/native/references.js";
13
+ const fail = (why) => { throw Error('REFUSE·native dispatcher ' + why); };
14
+ const ordered = (value) => [...value].sort(([a], [b]) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
15
+ async function run() {
16
+ const inherited = { store: process.argv[2], parent: Number(process.argv[8]), expected: JSON.parse(process.argv[4]) }, coordinate = process.argv[3], inputLimit = Number(process.argv[6]), phase = process.argv[7], execution = JSON.parse(process.argv[9]);
17
+ if (phase !== 'act')
18
+ fail('effects forbidden during ' + phase);
19
+ if (!Number.isSafeInteger(inputLimit) || inputLimit < 1)
20
+ fail('missing inherited input limit');
21
+ if (!execution?.context || !execution.env || !Array.isArray(execution.flags) || !execution.cwd)
22
+ fail('missing complete inherited reading identity');
23
+ assertNativeRuntime(process.env, process.execArgv);
24
+ const identity = hash(readingIdentityBytes(execution.context));
25
+ if (JSON.stringify(ordered(readingEnvironment(hash, execution.env, execution.cwd, execution.flags))) !== JSON.stringify(ordered(execution.context.environment)))
26
+ fail('inherited execution differs from context');
27
+ if (JSON.stringify(ordered(readingEnvironment(hash))) !== JSON.stringify(ordered(execution.context.environment)))
28
+ fail('actual child environment differs from inherited identity');
29
+ const chunks = [];
30
+ let bytes = 0;
31
+ for await (const chunk of process.stdin) {
32
+ const got = Buffer.from(chunk);
33
+ bytes += got.length;
34
+ if (bytes > inputLimit)
35
+ fail('input bytes exceeded');
36
+ chunks.push(got);
37
+ }
38
+ const frame = JSON.parse(Buffer.concat(chunks).toString('utf8'));
39
+ const current = inheritedNativeRoot(inherited, frame.proof), guard = inheritedNativeGuard(inherited, frame.proof), authenticated = authenticateNativeProfile(current, coordinate), profile = authenticated.profile;
40
+ const selected = authenticated.selection;
41
+ if (execution.context.implementation !== profile.selection.archive)
42
+ fail('reading implementation differs from selected archive');
43
+ if (!Array.isArray(frame.locations) || frame.locations.length !== profile.selection.members.length || frame.locations.length > profile.selection.budget.members)
44
+ fail('image member budget/count');
45
+ const runtime = frame.locations.find((location) => location.member === profile.selection.runtime);
46
+ if (!runtime || realpathSync(runtime.path) !== realpathSync(process.execPath))
47
+ fail('runtime differs from executing interpreter');
48
+ nativeReferenceInputs(execution.context.inputs, profile.selection.members.map(member => ['native/' + member.coordinate, member.digest]));
49
+ const imagePaths = profile.selection.members.map(member => { const rows = frame.locations.filter((location) => location.member === member.coordinate); if (rows.length !== 1)
50
+ return fail('missing/ambiguous actual member ' + member.coordinate); return { path: rows[0].path, coordinate: member.coordinate }; });
51
+ const raw = readBoundedBatch([...imagePaths, { path: frame.archive, coordinate: 'implementation archive' }], profile.selection.budget.bytes), archive = raw.at(-1), members = profile.selection.members.map((member, index) => ({ ...member, bytes: new Uint8Array(raw[index]) }));
52
+ if (hash(archive) !== profile.selection.archive)
53
+ fail('implementation archive differs');
54
+ const handoff = { place: inherited.expected.namespace, instrument: execution.context.instrument, policy: execution.context.policy, authoritySnapshot: identity, budget: profile.selection.budget, members, bindings: profile.selection.bindings, loads: profile.selection.loads };
55
+ const image = { cwd: execution.cwd, locations: frame.locations, runtime: profile.selection.runtime, preload: profile.selection.preload ?? undefined, nativePreloadMembers: profile.selection.nativePreloadMembers, condition: profile.selection.condition, commandPort: profile.port };
56
+ const handle = await loadSignedNativeReading(selected, { profile, handoff, image, archive }, guard);
57
+ const results = await answerNativeReading(handle, frame.requests, profile.selection.reader, { phase: 'act', guard });
58
+ process.stdout.write(JSON.stringify(results) + '\n');
59
+ }
60
+ run().catch(error => { process.stderr.write((error instanceof Error ? error.message : String(error)) + '\n'); process.exitCode = 1; });
@@ -1,6 +1,13 @@
1
1
  import { resolve } from 'node:path';
2
2
  import { observedClaims } from "../fold/observed.js";
3
3
  import { storeOf } from "../land/ledger.js";
4
+ import { withContentStore } from "../host/content-store.js";
5
+ import { lockStanding } from "../fold/keys.js";
6
+ import { underRootAuthority } from "../land/act.js";
4
7
  /** One reader, run to the end behind a fold that did not wait for it: what it lands, the next fold reads. */
5
- const [root = '.', only] = process.argv.slice(2);
6
- observedClaims(resolve(root), storeOf(resolve(root)), { wait: true, ...(only === undefined ? {} : { only }) });
8
+ const args = process.argv.slice(2);
9
+ if (args.length > 3 || (args[2] !== undefined && (args[1] === undefined || !/^sha256:[a-f0-9]{64}$/.test(args[2]))))
10
+ throw Error('REFUSE·reader invalid internal background arguments');
11
+ const [root = '.', only, selected] = args;
12
+ const place = resolve(root), store = storeOf(place);
13
+ await underRootAuthority(store, 'reader authority', () => withContentStore(store, () => observedClaims(place, store, { wait: true, ...(only === undefined ? {} : { only }), ...(selected === undefined ? {} : { selected }) }), () => lockStanding(store)));
package/dist/cli/pass.js CHANGED
@@ -25,6 +25,7 @@ import { carriedFrom } from "../fold/resolved.js";
25
25
  import { wireLine } from "../fold/wire.js";
26
26
  import { isWire, RECEIPTS } from "../fold/claims.js";
27
27
  import { withoutEffects } from "../host/read-only.js";
28
+ import { assertNativeReaderGrantsCurrent } from "../host/native/route.js";
28
29
  /**
29
30
  * The pass of a place, in one process: the releases its instrument names are laid out first, its readers observe
30
31
  * once, each place a view writes in is folded once, the places before the root whose door reads them, and every coordinate a
@@ -43,7 +44,7 @@ import { withoutEffects } from "../host/read-only.js";
43
44
  async function checked(root, entry, under) {
44
45
  const started = Date.now();
45
46
  const store = storeOf(root);
46
- if (!meets(root, store, under ?? '')) {
47
+ if (!meets(root, store, under ?? '', false)) {
47
48
  process.stderr.write(`${selfName()}: REFUSE·check receipts are open; a fold must close them\n`);
48
49
  return 1;
49
50
  }
@@ -132,6 +133,10 @@ async function sweep(root, entry, under, check, told, journal, given, keep, reac
132
133
  };
133
134
  const settled = met && sameInstrument && !missingView();
134
135
  if (settled) {
136
+ // File and receipt seals authenticate retained evidence. A normal quiet
137
+ // fold also needs today's acting Root grant; --check uses checked() above
138
+ // and intentionally performs historical inspection without fresh effects.
139
+ assertNativeReaderGrantsCurrent(store);
135
140
  const ms = Date.now() - digestAt;
136
141
  const read = receiptsSeen();
137
142
  const lock = algorithmCost();
@@ -1,11 +1,8 @@
1
+ import { hasContextualPlace, contextualStanding } from "../host/contextual-place.js";
2
+ import { authenticatedStanding } from "../host/admitted-place.js";
1
3
  import { viewsOf } from "../fold/views.js";
2
4
  import { lockStanding } from "../fold/keys.js";
3
- import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
4
- import { authorityFor } from "../fold/signers.js";
5
- import { eraOf } from "../fold/digests.js";
6
- import { fieldOf, foldClaims } from "../fold/claims.js";
7
5
  import { storeOf } from "../land/ledger.js";
8
- import { foldedEvidence } from '@lapxo/topos/contract';
9
6
  /** An evidence projection, never a new authority lock. Authentication uses
10
7
  * the admission snapshot at each local epoch; the existing fold applies
11
8
  * supersessions and withdrawals across all declared classes. Storage layout
@@ -14,26 +11,10 @@ export function printStandingResult(root) {
14
11
  const store = storeOf(root), view = viewsOf(lockStanding(store)).get('standing');
15
12
  if (!view || view.shape || !view.regions.some(region => region.name === 'standing'))
16
13
  throw Error('REFUSE·view standing requires a declared standing view');
17
- const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER), anchor = admissionAnchor(root);
18
- const snapshots = new Map();
19
- const admitted = history.filter(line => {
20
- const epoch = Number(fieldOf(line, 'epoch')) || 0;
21
- let authority = snapshots.get(epoch);
22
- if (!authority) {
23
- const snapshot = admissionHistoryAt(root, epoch);
24
- // The signature vocabulary is configuration, not the as-yet
25
- // unauthenticated evidence whose values are about to be folded.
26
- const wire = snapshot.filter(record => fieldOf(record, 'scope').startsWith('wire/'));
27
- authority = authorityFor(snapshot, anchor, eraOf(foldClaims(wire).standing).admitted);
28
- snapshots.set(epoch, authority);
29
- }
30
- return authority.of(line).kind === 'admitted';
31
- });
32
- // The host authenticates against historical authority; Topos projects the
33
- // authenticated history using the instrument's existing fold.
34
- const { standing } = foldedEvidence(admitted, {
35
- classification: () => 'local',
36
- fold: lines => foldClaims(lines).standing,
37
- });
14
+ if (hasContextualPlace(root)) {
15
+ process.stdout.write(JSON.stringify(contextualStanding(root)) + '\n');
16
+ return;
17
+ }
18
+ const standing = authenticatedStanding(root);
38
19
  process.stdout.write(standing.length ? standing.join('\n') + '\n' : '');
39
20
  }