@lapxo/bound 0.1.7 → 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.
- package/README.md +56 -20
- package/TARGET.bound +27 -27
- package/dist/cli/accept.js +83 -59
- package/dist/cli/act-result.d.ts +9 -0
- package/dist/cli/act-result.js +68 -13
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +5 -2
- package/dist/cli/custodian.d.ts +1 -0
- package/dist/cli/custodian.js +29 -0
- package/dist/cli/native-descendant.d.ts +1 -0
- package/dist/cli/native-descendant.js +19 -0
- package/dist/cli/native.d.ts +1 -0
- package/dist/cli/native.js +60 -0
- package/dist/cli/observe.js +9 -2
- package/dist/cli/pass.js +6 -1
- package/dist/cli/standing-result.js +7 -26
- package/dist/cli/takes.js +5 -2
- package/dist/cli/verb.js +24 -5
- package/dist/fold/admission-history.js +2 -2
- package/dist/fold/claims.d.ts +4 -0
- package/dist/fold/claims.js +23 -3
- package/dist/fold/closed.d.ts +1 -1
- package/dist/fold/closed.js +16 -4
- package/dist/fold/digests.d.ts +1 -1
- package/dist/fold/digests.js +3 -2
- package/dist/fold/evidence.d.ts +1 -1
- package/dist/fold/kept.js +2 -1
- package/dist/fold/observed.d.ts +1 -0
- package/dist/fold/observed.js +54 -9
- package/dist/fold/readers.d.ts +4 -3
- package/dist/fold/readers.js +18 -13
- package/dist/fold/resolved.js +11 -1
- package/dist/fold/signers.js +2 -2
- package/dist/host/adapters/modules/javascript.d.ts +2 -0
- package/dist/host/adapters/modules/javascript.js +8 -6
- package/dist/host/admitted-place.d.ts +5 -0
- package/dist/host/admitted-place.js +54 -0
- package/dist/host/capsule-process.d.ts +1 -1
- package/dist/host/capsule-process.js +4 -1
- package/dist/host/capsule.d.ts +4 -0
- package/dist/host/capsule.js +16 -2
- package/dist/host/contextual-place.d.ts +59 -0
- package/dist/host/contextual-place.js +77 -0
- package/dist/host/hash.d.ts +7 -0
- package/dist/host/hash.js +6 -0
- package/dist/host/io.d.ts +2 -0
- package/dist/host/io.js +2 -0
- package/dist/host/lifetime.js +61 -22
- package/dist/host/native/bounded-file.d.ts +8 -0
- package/dist/host/native/bounded-file.js +32 -0
- package/dist/host/native/command.d.ts +36 -0
- package/dist/host/native/command.js +91 -0
- package/dist/host/native/control-bridge-worker.d.ts +1 -0
- package/dist/host/native/control-bridge-worker.js +88 -0
- package/dist/host/native/control-bridge.d.ts +30 -0
- package/dist/host/native/control-bridge.js +110 -0
- package/dist/host/native/control-selection.d.ts +29 -0
- package/dist/host/native/control-selection.js +32 -0
- package/dist/host/native/custody.d.ts +5 -0
- package/dist/host/native/custody.js +91 -0
- package/dist/host/native/deadline.d.ts +12 -0
- package/dist/host/native/deadline.js +28 -0
- package/dist/host/native/descendants.d.ts +1 -0
- package/dist/host/native/descendants.js +528 -0
- package/dist/host/native/generated-custodian.d.ts +122 -0
- package/dist/host/native/generated-custodian.js +267 -0
- package/dist/host/native/generated.d.ts +217 -0
- package/dist/host/native/generated.js +379 -0
- package/dist/host/native/image.d.ts +36 -0
- package/dist/host/native/image.js +45 -0
- package/dist/host/native/jobs.d.ts +75 -0
- package/dist/host/native/jobs.js +148 -0
- package/dist/host/native/loader.d.ts +80 -0
- package/dist/host/native/loader.js +269 -0
- package/dist/host/native/materialize.d.ts +22 -0
- package/dist/host/native/materialize.js +175 -0
- package/dist/host/native/module-closure.d.ts +27 -0
- package/dist/host/native/module-closure.js +99 -0
- package/dist/host/native/module-syntax.d.ts +8 -0
- package/dist/host/native/module-syntax.js +63 -0
- package/dist/host/native/native-binary.d.ts +54 -0
- package/dist/host/native/native-binary.js +115 -0
- package/dist/host/native/native-program.d.ts +56 -0
- package/dist/host/native/native-program.js +72 -0
- package/dist/host/native/node-jobs.d.ts +84 -0
- package/dist/host/native/node-jobs.js +187 -0
- package/dist/host/native/node-launch.d.ts +72 -0
- package/dist/host/native/node-launch.js +159 -0
- package/dist/host/native/preload.d.ts +1 -0
- package/dist/host/native/preload.js +45 -0
- package/dist/host/native/reading.d.ts +91 -0
- package/dist/host/native/reading.js +249 -0
- package/dist/host/native/receipt-environment.d.ts +3 -0
- package/dist/host/native/receipt-environment.js +41 -0
- package/dist/host/native/references.d.ts +5 -0
- package/dist/host/native/references.js +30 -0
- package/dist/host/native/root.d.ts +17 -0
- package/dist/host/native/root.js +37 -0
- package/dist/host/native/route.d.ts +66 -0
- package/dist/host/native/route.js +216 -0
- package/dist/host/native/runtime.d.ts +4 -0
- package/dist/host/native/runtime.js +9 -0
- package/dist/host/native-reader-context.d.ts +3 -0
- package/dist/host/native-reader-context.js +8 -0
- package/dist/host/place-clock-selection.d.ts +55 -0
- package/dist/host/place-clock-selection.js +124 -0
- package/dist/host/ports/local-act.d.ts +10 -0
- package/dist/host/ports/local-act.js +78 -6
- package/dist/host/ports/place-act.d.ts +46 -0
- package/dist/host/ports/place-act.js +169 -0
- package/dist/host/process-lifetime.js +8 -3
- package/dist/host/provide.d.ts +25 -0
- package/dist/host/provide.js +115 -13
- package/dist/host/reading-closures.d.ts +15 -1
- package/dist/host/reading-closures.js +88 -6
- package/dist/host/reading-context.d.ts +29 -0
- package/dist/host/reading-context.js +53 -0
- package/dist/host/root-clock-source.d.ts +85 -0
- package/dist/host/root-clock-source.js +305 -0
- package/dist/host/selected-capsules.js +3 -2
- package/dist/judge/take.d.ts +2 -0
- package/dist/judge/take.js +1 -1
- package/dist/land/act.d.ts +26 -0
- package/dist/land/act.js +96 -3
- package/dist/land/ledger.d.ts +4 -0
- package/dist/land/ledger.js +20 -2
- package/dist/observe/runner.d.ts +4 -0
- package/dist/observe/runner.js +9 -2
- package/docs/contracts/cli.md +13 -0
- package/package.json +2 -2
- package/receipts.bound +607 -567
package/dist/cli/verb.js
CHANGED
|
@@ -42,9 +42,9 @@ import { placesIn } from "../fold/places.js";
|
|
|
42
42
|
import { isCeiling } from "../fold/configures.js";
|
|
43
43
|
import { readingsIn } from "../land/inert.js";
|
|
44
44
|
import { pass } from "./pass.js";
|
|
45
|
-
import { underTheLock, underTheRegions } from "../land/act.js";
|
|
45
|
+
import { underTheLock, underTheRegions, underRootAuthority } from "../land/act.js";
|
|
46
46
|
import { ownerOf, renderInto, signedDemands, targetFile } from "./owner.js";
|
|
47
|
-
import { claimLinesIn, entryOf, namedPlaces, positionalsOf, valueOf } from "./args.js";
|
|
47
|
+
import { claimLinesIn, entryOf, namedPlaces, positionalIndexes, positionalsOf, valueOf } from "./args.js";
|
|
48
48
|
import { EXIT, FLAGS, NAMED_VIEWS, helpLines } from "./names.js";
|
|
49
49
|
import { accept, signedBatch } from "./accept.js";
|
|
50
50
|
import { renderWalk, landWalk, foreignWalkLines } from "../host/walk.js";
|
|
@@ -182,12 +182,18 @@ function beat(root, fold, ms) {
|
|
|
182
182
|
* as if the batch had landed: the batch is signed and folded, never landed, and nothing is written — `--as rank <batch>`
|
|
183
183
|
* is what a batch would move, read before land.
|
|
184
184
|
*/
|
|
185
|
-
async function view(root, under, asked, told = [], coord, key, includePolicy = true) {
|
|
185
|
+
async function view(root, under, asked, told = [], coord, key, includePolicy = true, authorityHeld = false) {
|
|
186
186
|
const [name = asked, depth] = asked.split('@');
|
|
187
187
|
if (name === NAMED_VIEWS.help) {
|
|
188
188
|
process.stdout.write(`${helpLines().join('\n')}\n`);
|
|
189
189
|
return EXIT.closed;
|
|
190
190
|
}
|
|
191
|
+
// A declared executable reader holds the acting Root's authority throughout
|
|
192
|
+
// observation and publication. This predicate acquires exclusion only; the
|
|
193
|
+
// native route authenticates the actual declaration before use.
|
|
194
|
+
if (!authorityHeld && !told.length && !readOnly() && lockStanding(storeOf(root)).some(line => /^reader\/.+\/port$/.test(fieldOf(line, 'scope')) && fieldOf(line, 'role') === 'writes' && fieldOf(line, 'value') !== 'withdraw')) {
|
|
195
|
+
return underRootAuthority(storeOf(root), 'view reader authority', () => view(root, under, asked, told, coord, key, includePolicy, true));
|
|
196
|
+
}
|
|
191
197
|
if (!lockLines(storeOf(root)).length) {
|
|
192
198
|
process.stderr.write(`${selfName()}: the owner ledger is empty; land it before rendering a view\n`);
|
|
193
199
|
return EXIT.refuse;
|
|
@@ -565,8 +571,21 @@ function main(argv) {
|
|
|
565
571
|
const run = verb in VERBS ? VERBS[verb] : undefined;
|
|
566
572
|
const places = positionalsOf(run === undefined ? argv : argv.slice(1)).filter((one) => existsSync(resolve(one)) && lstatSync(resolve(one)).isDirectory());
|
|
567
573
|
if (run !== undefined) {
|
|
568
|
-
const
|
|
569
|
-
|
|
574
|
+
const rest = argv.slice(1);
|
|
575
|
+
const roots = positionalIndexes(rest).filter(index => {
|
|
576
|
+
const path = resolve(rest[index]);
|
|
577
|
+
return existsSync(path) && lstatSync(path).isDirectory();
|
|
578
|
+
});
|
|
579
|
+
if (roots.length > 1) {
|
|
580
|
+
process.stderr.write(`${selfName()}: REFUSE·root name one acting directory\n`);
|
|
581
|
+
return EXIT.usage;
|
|
582
|
+
}
|
|
583
|
+
const selected = roots[0];
|
|
584
|
+
const root = resolve(selected === undefined ? '.' : rest[selected]);
|
|
585
|
+
const normalized = selected === undefined ? rest : rest.filter((_, index) => index !== selected);
|
|
586
|
+
return withContentStore(storeOf(root), () => verb === 'fold' && selected !== undefined
|
|
587
|
+
? foldAt(root, undefined, normalized)
|
|
588
|
+
: run(root, normalized, [verb, ...normalized]));
|
|
570
589
|
}
|
|
571
590
|
const under = argv[0] ?? '';
|
|
572
591
|
if (under && !under.startsWith('-') && places.includes(under)) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
-
import {
|
|
3
|
+
import { rootLedgerLines, storeOf } from "../land/ledger.js";
|
|
4
4
|
import { fieldOf, isConfig, selfName } from "./claims.js";
|
|
5
5
|
/** Local admission history only. Foreign ingress is evidence, never coverage.
|
|
6
6
|
* The clock cutoff is meaningful for configuration; object clocks stay intact. */
|
|
7
7
|
export function admissionHistoryAt(root, epoch) {
|
|
8
8
|
const store = storeOf(root);
|
|
9
|
-
return [...new Set(
|
|
9
|
+
return [...new Set(rootLedgerLines(store))]
|
|
10
10
|
.filter(line => fieldOf(line, 'sig') !== '' && (!isConfig(line) || Number(fieldOf(line, 'epoch')) <= epoch));
|
|
11
11
|
}
|
|
12
12
|
/** The existing bootstrap's public root of trust, not today's key selection. */
|
package/dist/fold/claims.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { parse } from '@lapxo/topos/wire';
|
|
1
2
|
export { CAPSULE, EXTENSION, LOCK, RECEIPTS } from '@lapxo/topos/wire';
|
|
3
|
+
/** Pure syntax is retained by the exact record bytes, never by a path, epoch or
|
|
4
|
+
* claimed digest. This does not retain admission, coverage or current grants. */
|
|
5
|
+
export declare function parsedLine(line: string): ReturnType<typeof parse>;
|
|
2
6
|
export declare const fieldOf: (line: string, name: string) => string;
|
|
3
7
|
export declare const fieldsOf: (line: string) => Record<string, string>;
|
|
4
8
|
export declare function selfName(): string;
|
package/dist/fold/claims.js
CHANGED
|
@@ -4,12 +4,31 @@ import { ENVELOPE, PROTOCOL, recordKind, signedBytes, parse } from '@lapxo/topos
|
|
|
4
4
|
export { CAPSULE, EXTENSION, LOCK, RECEIPTS } from '@lapxo/topos/wire';
|
|
5
5
|
import { ownRoot } from "../observe/runner.js";
|
|
6
6
|
const decoded = new Map();
|
|
7
|
+
const parsed = new Map();
|
|
8
|
+
const kinds = new Map();
|
|
9
|
+
/** Pure syntax is retained by the exact record bytes, never by a path, epoch or
|
|
10
|
+
* claimed digest. This does not retain admission, coverage or current grants. */
|
|
11
|
+
export function parsedLine(line) {
|
|
12
|
+
const held = parsed.get(line);
|
|
13
|
+
if (held !== undefined)
|
|
14
|
+
return held;
|
|
15
|
+
const got = parse(line);
|
|
16
|
+
if (got.kind === 'fact') {
|
|
17
|
+
Object.freeze(got.value.fields);
|
|
18
|
+
if (got.value.keys)
|
|
19
|
+
Object.freeze(got.value.keys);
|
|
20
|
+
Object.freeze(got.value);
|
|
21
|
+
}
|
|
22
|
+
Object.freeze(got);
|
|
23
|
+
parsed.set(line, got);
|
|
24
|
+
return got;
|
|
25
|
+
}
|
|
7
26
|
/** Field access uses the Topos wire parser; decoding is shared for this CLI process. */
|
|
8
27
|
function wireFields(line) {
|
|
9
28
|
const held = decoded.get(line);
|
|
10
29
|
if (held !== undefined)
|
|
11
30
|
return held;
|
|
12
|
-
const got =
|
|
31
|
+
const got = parsedLine(line);
|
|
13
32
|
const fields = got.kind === 'fact' ? got.value.fields : {};
|
|
14
33
|
decoded.set(line, fields);
|
|
15
34
|
return fields;
|
|
@@ -19,8 +38,9 @@ export const fieldsOf = (line) => ({ ...wireFields(line) });
|
|
|
19
38
|
export function selfName() { return basename(ownRoot()); }
|
|
20
39
|
export function isWire(line) { return line.startsWith(PROTOCOL); }
|
|
21
40
|
export const wireLinesOf = (text) => (text ?? '').split('\n').filter(isWire);
|
|
22
|
-
export const isConfig = (line) => { const
|
|
23
|
-
|
|
41
|
+
export const isConfig = (line) => { const held = kinds.get(line); if (held !== undefined)
|
|
42
|
+
return held; const k = recordKind(line), value = k.kind === 'fact' && k.value === 'config'; kinds.set(line, value); return value; };
|
|
43
|
+
export const sayingOf = (line) => !isConfig(line) ? ((p) => p.kind === 'fact' ? signedBytes(p.value.fields) : line)(parsedLine(line)) : Object.entries(fieldsOf(line)).filter(([k]) => !ENVELOPE.has(k)).sort(([a], [b]) => (a < b ? -1 : 1)).map(([k, v]) => `${k}=${v}`).join(' ');
|
|
24
44
|
/** One key per claim: scope, role and measure; what a line says, its envelope set aside, is one however often it landed. */
|
|
25
45
|
export function claimKey(line) {
|
|
26
46
|
const f = fieldsOf(line);
|
package/dist/fold/closed.d.ts
CHANGED
|
@@ -12,4 +12,4 @@ export declare function instrumentKeep(store: string, id: string): void;
|
|
|
12
12
|
export declare function receiptPlaces(root: string, store: string): readonly string[];
|
|
13
13
|
export declare function closeReceipts(root: string, store: string, semantic?: ReadonlyMap<string, string>): Promise<readonly string[]>;
|
|
14
14
|
/** One comparison. A place meets its receipt; the root meets when every receipt it names meets. */
|
|
15
|
-
export declare function meets(root: string, store: string, place: string): boolean;
|
|
15
|
+
export declare function meets(root: string, store: string, place: string, current?: boolean): boolean;
|
package/dist/fold/closed.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { actCosts } from "../host/act-cost.js";
|
|
2
2
|
import { verifiedReadingReceipts } from "../host/reading-closures.js";
|
|
3
|
+
import { readingProfile } from "../host/reading-context.js";
|
|
4
|
+
import { currentReadingEnvironment } from "../host/native/receipt-environment.js";
|
|
5
|
+
import { childFlags } from "../host/capsule.js";
|
|
3
6
|
import { dirname, existsSync, join, mkdirSync, relative, resolve, statSync, writeFileSync } from "../host/io.js";
|
|
4
7
|
import { byBytes, canonical, LOCK, parse, publicationOf, roleAt, roleClaimsOf } from '@lapxo/topos/wire';
|
|
5
8
|
import { RECEIPTS, fieldOf, foldClaims, isConfig, isWire } from "./claims.js";
|
|
@@ -60,13 +63,21 @@ function filesOf(root, place, region, inputs) {
|
|
|
60
63
|
return [...new Set([...owned, ...observed, ...own])].filter(rel => rel !== receipt && !rel.split('/').some(part => part === '.bound' || part === '.cache' || part === '.git'));
|
|
61
64
|
}
|
|
62
65
|
const rooted = new Map();
|
|
66
|
+
let lastConfiguration;
|
|
63
67
|
function publicConfiguration(lines) {
|
|
68
|
+
const input = lines.join('\n');
|
|
69
|
+
// Retain only this pure projection. Callers still read and hash the actual
|
|
70
|
+
// files; neither a timestamp nor a previous seal proves their current bytes.
|
|
71
|
+
if (lastConfiguration?.input === input)
|
|
72
|
+
return lastConfiguration.output;
|
|
64
73
|
if (!lines.length || !lines.every(isConfig))
|
|
65
74
|
return lines;
|
|
66
75
|
const folded = foldClaims(lines);
|
|
67
76
|
if (folded.forks.length)
|
|
68
77
|
throw Error('REFUSE·receipt disputed configuration standing');
|
|
69
|
-
|
|
78
|
+
const output = publicationOf(folded.standing.filter(line => fieldOf(line, 'value') !== 'withdraw'));
|
|
79
|
+
lastConfiguration = { input, output };
|
|
80
|
+
return output;
|
|
70
81
|
}
|
|
71
82
|
function rootLines(root, place) {
|
|
72
83
|
const name = place.replace(/\/$/, '');
|
|
@@ -167,7 +178,7 @@ export async function closeReceipts(root, store, semantic) {
|
|
|
167
178
|
return open;
|
|
168
179
|
}
|
|
169
180
|
/** One comparison. A place meets its receipt; the root meets when every receipt it names meets. */
|
|
170
|
-
export function meets(root, store, place) {
|
|
181
|
+
export function meets(root, store, place, current = true) {
|
|
171
182
|
const name = place.replace(/\/$/, '');
|
|
172
183
|
if (name === '') {
|
|
173
184
|
const at = Date.now();
|
|
@@ -178,11 +189,12 @@ export function meets(root, store, place) {
|
|
|
178
189
|
let closed = (own.length > 0 ? ownClosed : named.size > 0) && [...named].every(([one, expected]) => {
|
|
179
190
|
const bytes = observeFile(join(root, one, RECEIPTS));
|
|
180
191
|
const algorithm = expected.split(':')[0];
|
|
181
|
-
return bytes !== undefined && hashBytes(bytes, algorithm) === expected && meets(root, store, `${one}
|
|
192
|
+
return bytes !== undefined && hashBytes(bytes, algorithm) === expected && meets(root, store, `${one}/`, current);
|
|
182
193
|
});
|
|
183
194
|
if (closed && carried) {
|
|
184
195
|
const algorithm = algorithmIn(own.join('\n'));
|
|
185
|
-
const
|
|
196
|
+
const profile = readingProfile((observeText(join(root, LOCK)) ?? '').split('\n').filter(isWire), fieldOf);
|
|
197
|
+
const readings = verifiedReadingReceipts(store, carried, text => hashFull(text, algorithm), current && profile ? currentReadingEnvironment(store, root, text => hashFull(text, algorithm), childFlags()) : undefined, profile);
|
|
186
198
|
if (readings === undefined)
|
|
187
199
|
closed = false;
|
|
188
200
|
else if (readings)
|
package/dist/fold/digests.d.ts
CHANGED
|
@@ -26,4 +26,4 @@ export declare function memoized(store: string, key: string, compute: () => stri
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function instrumentOf(store: string, entry: string, standing?: readonly string[], root?: string, releases?: readonly string[]): string;
|
|
28
28
|
export declare function instrumentDigest(store: string, entry: string, standing?: readonly string[], root?: string, releases?: readonly string[]): string;
|
|
29
|
-
export declare function instrumentCoordinates(store: string, entry: string, standing?: readonly string[], root?: string, releases?: readonly string[]): ReadonlyMap<string, string>;
|
|
29
|
+
export declare function instrumentCoordinates(store: string, entry: string, standing?: readonly string[], root?: string, releases?: readonly string[], fresh?: boolean): ReadonlyMap<string, string>;
|
package/dist/fold/digests.js
CHANGED
|
@@ -167,12 +167,13 @@ export function instrumentDigest(store, entry, standing = [], root = dirname(res
|
|
|
167
167
|
const seen = instrumentCoordinates(store, entry, standing, root, releases);
|
|
168
168
|
return bytesDigest(store, new TextEncoder().encode([...seen.values()].sort().join('\n')));
|
|
169
169
|
}
|
|
170
|
-
export function instrumentCoordinates(store, entry, standing = [], root = dirname(resolve(entry)), releases = []) {
|
|
170
|
+
export function instrumentCoordinates(store, entry, standing = [], root = dirname(resolve(entry)), releases = [], fresh = false) {
|
|
171
171
|
const seen = new Map();
|
|
172
172
|
const walk = (at) => {
|
|
173
173
|
if (seen.has(at))
|
|
174
174
|
return;
|
|
175
|
-
const
|
|
175
|
+
const bytes = fresh ? observeFile(at) : undefined;
|
|
176
|
+
const digest = fresh ? (bytes === undefined ? undefined : bytesDigest(store, bytes)) : coordinateDigest(store, at);
|
|
176
177
|
if (digest === undefined)
|
|
177
178
|
return;
|
|
178
179
|
// The host loader resolves links before loading a module. Count that module
|
package/dist/fold/evidence.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface SnapshotInput {
|
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
export declare function snapshotVerdict(input: SnapshotInput): {
|
|
25
|
-
verdict: "none" | "grey" | "
|
|
25
|
+
verdict: "none" | "grey" | "stale" | "agrees" | "forks";
|
|
26
26
|
external: boolean;
|
|
27
27
|
epoch: number;
|
|
28
28
|
};
|
package/dist/fold/kept.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeUTF8 } from "../host/io.js";
|
|
1
2
|
import { actLines, committedLocalActs } from "../host/ports/local-act.js";
|
|
2
3
|
import { readOnly } from "../host/read-only.js";
|
|
3
4
|
import { createHash, dirname, existsSync, join, mkdirSync, readdirSync, renameSync, writeFileSync } from "../host/io.js";
|
|
@@ -10,7 +11,7 @@ import { keyFor, keyOf, lockSigners } from "./keys.js";
|
|
|
10
11
|
import { sourceOf, vouching } from "../land/vouched.js";
|
|
11
12
|
import { wordsOf } from "./wire.js";
|
|
12
13
|
import { ownLock } from "../observe/runner.js";
|
|
13
|
-
const text =
|
|
14
|
+
const text = encodeUTF8;
|
|
14
15
|
const atOf = (line) => {
|
|
15
16
|
if (line.includes(' about='))
|
|
16
17
|
return fieldOf(line, 'at');
|
package/dist/fold/observed.d.ts
CHANGED
package/dist/fold/observed.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { authenticatedStanding } from "../host/admitted-place.js";
|
|
1
2
|
import { actCosts } from "../host/act-cost.js";
|
|
2
3
|
import { readingClosures } from "../host/reading-closures.js";
|
|
4
|
+
import { readingExecution, readingProfile } from "../host/reading-context.js";
|
|
5
|
+
import { withNativeReaderRoute } from "../host/native-reader-context.js";
|
|
6
|
+
import { prepareNativeReaderRoute, assertNativeRouteCurrent, materializeNativeRoute } from "../host/native/route.js";
|
|
7
|
+
import { verifiedContent } from "../host/content-store.js";
|
|
8
|
+
import { ownLock, ownRoot, ownStore } from "../observe/runner.js";
|
|
3
9
|
import { readerLifetime, readerAllowsEmpty, assertReaderInputs, parse as parseLifetime } from '@lapxo/topos/wire';
|
|
4
10
|
import { readOnly } from "../host/read-only.js";
|
|
5
|
-
import { basename, dirname, existsSync, mkdirSync, resolve, rmSync, spawn, statSync, writeFileSync } from "../host/io.js";
|
|
11
|
+
import { basename, dirname, relative, existsSync, mkdirSync, resolve, rmSync, spawn, statSync, writeFileSync } from "../host/io.js";
|
|
6
12
|
import { bundleOf, processOf } from "../observe/runner.js";
|
|
7
13
|
import { canonical, parse, PROTOCOL } from '@lapxo/topos/wire';
|
|
8
|
-
import { bytesDigest, memoized } from "./digests.js";
|
|
14
|
+
import { bytesDigest, memoized, instrumentCoordinates } from "./digests.js";
|
|
9
15
|
import { land, landMemo, landShard, memoAt, shardLines, storeAt } from "../land/ledger.js";
|
|
10
16
|
import { fieldOf, isWire } from "./claims.js";
|
|
11
17
|
import { rolesOf } from "./roles.js";
|
|
@@ -154,8 +160,10 @@ function carried(store) {
|
|
|
154
160
|
return carriedIn(store).flatMap((file) => (observeText(file) ?? '').split('\n'))
|
|
155
161
|
.filter((line) => isWire(line) && speaker !== undefined && fieldOf(line, 'by').split(':')[0] === speaker);
|
|
156
162
|
}
|
|
163
|
+
const readerSelection = (reader) => reader.capsule?.selected?.selection === undefined ? undefined : 'sha256:' + sha(JSON.stringify([reader.capsule.selected.selection, reader.capsule.region, reader.module, reader.where, [...reader.capsule.globs]]));
|
|
164
|
+
const readerScope = (reader) => reader.capsule?.selected?.selection === undefined ? `reader/${reader.module}` : `reader/${reader.module}/${sha(`${reader.capsule.selected.selection}\n${reader.capsule.region}`)}`;
|
|
157
165
|
const lineagesOf = (store, speaker, readers, regions) => new Set(readers.flatMap((reader) => shardLines(store, speaker, regionIn(regions, reader.module))
|
|
158
|
-
.filter((line) => fieldOf(line, 'scope') ===
|
|
166
|
+
.filter((line) => fieldOf(line, 'scope') === readerScope(reader) && /(?:^|\s)measure=(?:identity|extension)\s/.test(line)).map((line) => fieldOf(line, 'value'))));
|
|
159
167
|
export function readerLines(store) {
|
|
160
168
|
const readers = readersOf(store);
|
|
161
169
|
const regions = [...new Set(readers.flatMap((reader) => reader.where.map(trimmed)))];
|
|
@@ -188,8 +196,28 @@ export function observedClaims(root, store, options = {}) {
|
|
|
188
196
|
const standing = lockStanding(store);
|
|
189
197
|
const lines = readersOf(store);
|
|
190
198
|
const readers = [...lines, ...capsuleReadersOf(root, store, standing, lines)];
|
|
199
|
+
if (options.selected !== undefined) {
|
|
200
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(options.selected) || options.only === undefined)
|
|
201
|
+
throw Error('REFUSE·reader invalid internal background selector');
|
|
202
|
+
if (readers.filter(reader => reader.module === options.only && readerSelection(reader) === options.selected).length !== 1)
|
|
203
|
+
throw Error('REFUSE·reader internal background selector is not uniquely adopted');
|
|
204
|
+
}
|
|
191
205
|
if (!readers.length)
|
|
192
206
|
return carried(store);
|
|
207
|
+
const modern = readingProfile(standing, fieldOf);
|
|
208
|
+
const implementation = modern ? instrumentCoordinates(store, processOf('verb'), [], ownRoot(), ownLock(), true) : undefined;
|
|
209
|
+
if (implementation)
|
|
210
|
+
for (const module of [processOf('host'), processOf('lifetime', 'host')])
|
|
211
|
+
for (const [coordinate, digest] of instrumentCoordinates(store, module, [], ownRoot(), ownLock(), true))
|
|
212
|
+
implementation.set(coordinate, digest);
|
|
213
|
+
const code = implementation ? bytesDigest(store, new TextEncoder().encode([...implementation.values()].sort().join('\n'))) : undefined;
|
|
214
|
+
const transitive = implementation ? [...implementation].map(([coordinate, digest]) => [relative(ownRoot(), coordinate), digest]) : [];
|
|
215
|
+
if (modern)
|
|
216
|
+
for (const line of ownLock().filter(line => fieldOf(line, 'scope').startsWith('dep/') && fieldOf(line, 'role') === 'reads' && fieldOf(line, 'value') !== 'withdraw')) {
|
|
217
|
+
const digest = fieldOf(line, 'value');
|
|
218
|
+
verifiedContent(digest, store, ownStore());
|
|
219
|
+
transitive.push([fieldOf(line, 'scope'), digest]);
|
|
220
|
+
}
|
|
193
221
|
const speaker = keyFor(store, 'read');
|
|
194
222
|
const regions = [...new Set(readers.flatMap((reader) => reader.where.map(trimmed)))];
|
|
195
223
|
const whole = options.only === undefined && options.reach === undefined;
|
|
@@ -258,6 +286,8 @@ export function observedClaims(root, store, options = {}) {
|
|
|
258
286
|
for (const reader of readers) {
|
|
259
287
|
if (options.only !== undefined && reader.module !== options.only)
|
|
260
288
|
continue;
|
|
289
|
+
if (options.selected !== undefined && readerSelection(reader) !== options.selected)
|
|
290
|
+
continue;
|
|
261
291
|
if (!readerReaches(options.reach, reader.where))
|
|
262
292
|
continue;
|
|
263
293
|
const started = Date.now();
|
|
@@ -274,12 +304,23 @@ export function observedClaims(root, store, options = {}) {
|
|
|
274
304
|
if (projection.length !== 1 || !asked)
|
|
275
305
|
throw Error('REFUSE·input projection is ambiguous ' + reader.module);
|
|
276
306
|
const files = [...new Set(reader.where.flatMap(where => handedAt(root, reader, roleOf, look, where)))].sort().map(place => ({ place, text: observeText(resolve(root, place)) ?? '' }));
|
|
277
|
-
const
|
|
307
|
+
const hash = (text) => bytesDigest(store, Buffer.from(text));
|
|
308
|
+
const allowsEmpty = readerAllowsEmpty(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader.module);
|
|
309
|
+
const native = prepareNativeReaderRoute(store, { id: reader.id, module: reader.module });
|
|
310
|
+
if (native && !modern)
|
|
311
|
+
throw Error('REFUSE·native reader requires complete reading identity ' + reader.module);
|
|
312
|
+
if (native && asked.digest !== native.profile.selection.archive)
|
|
313
|
+
throw Error('REFUSE·native reader implementation differs ' + reader.module);
|
|
314
|
+
const image = native ? materializeNativeRoute(native) : undefined;
|
|
315
|
+
const execution = modern ? readingExecution({ instrument: code, implementation: asked.digest, policy: hash(JSON.stringify([reader.id, reader.module, reader.kind, reader.shape, reader.where, lifetime ?? null, allowsEmpty])), inputs: [...transitive, ...(native?.inputs ?? []), ...(image ? [['native/mount', image.mountIdentity]] : [])] }, hash, { env: process.env, cwd: image?.cwd ?? root, flags: native ? [] : childFlags() }) : undefined;
|
|
316
|
+
const result = withNativeReaderRoute(native, () => readingClosures({ root, store, ...(execution ? { context: execution.context, execution } : {}), reader: reader.module, speaker, digest: text => bytesDigest(store, Buffer.from(text)), capsule: asked, ...(native ? { assertCurrent: () => assertNativeRouteCurrent(native) } : {}), region: reader.capsule.region, projection: fieldOf(projection[0], 'value'), livePlace: () => authenticatedStanding(root), readerReference: { id: reader.id, module: reader.module }, lifetime, allowsEmpty, files, emit: line => process.stderr.write(line + '\n') }));
|
|
278
317
|
out.push(...result.lines);
|
|
279
318
|
ran += result.executed;
|
|
280
319
|
read += result.read - result.opened;
|
|
281
320
|
return false;
|
|
282
321
|
}
|
|
322
|
+
if (modern)
|
|
323
|
+
throw Error('REFUSE·reader ' + reader.module + ' complete reading identity requires a declared input projection');
|
|
283
324
|
const endAct = actCosts.begin();
|
|
284
325
|
try {
|
|
285
326
|
const source = reader.capsule === undefined ? observeFile(module) : asked === undefined ? undefined : new Uint8Array();
|
|
@@ -287,9 +328,11 @@ export function observedClaims(root, store, options = {}) {
|
|
|
287
328
|
const offered = offersOf(root, store, reader, roleOf, look, (where) => reached(trimmed(where)));
|
|
288
329
|
assertReaderInputs(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader.module, offered.length);
|
|
289
330
|
const vectors = reader.capsule === undefined ? observeText(resolve(root, vectorsOf(reader.module, standing))) : undefined;
|
|
331
|
+
const selectedBy = reader.capsule?.selected?.selection === undefined ? undefined : sha(`${reader.capsule.selected.selection}\n${reader.capsule.region}${lifetime === undefined ? "" : "\n" + JSON.stringify(lifetime)}`);
|
|
290
332
|
const kept = whole && legacy === undefined && answered === undefined && source ? sha([
|
|
291
333
|
speaker, reader.module, JSON.stringify(lifetime), reader.kind, reader.shape.join('|'), reader.where.join('|'), bundle, vectors === undefined ? 'no vectors' : sha(vectors),
|
|
292
334
|
reader.capsule === undefined ? '' : `${reader.capsule.selected?.selection ?? reader.capsule.digest} ${reader.capsule.region} ${reader.capsule.reads.join('|')} ${[...reader.capsule.globs].map(([place, globs]) => `${place}${globs.join('|')}`).join(' ')}`,
|
|
335
|
+
...(selectedBy === undefined ? [] : [selectedBy]),
|
|
293
336
|
...offered.map((offer) => `${offer.place} ${offer.digest?.() ?? bytesDigest(store, offer.bytes())}`),
|
|
294
337
|
].join('\n')) : undefined;
|
|
295
338
|
const stored = kept === undefined ? undefined : observeText(memoAt(store, `observed/${kept}`));
|
|
@@ -301,10 +344,12 @@ export function observedClaims(root, store, options = {}) {
|
|
|
301
344
|
}
|
|
302
345
|
const mark = out.length;
|
|
303
346
|
const home = regionIn(regions, reader.module);
|
|
304
|
-
const own = shardLines(store, speaker, home).filter((line) => fieldOf(line, 'scope') ===
|
|
305
|
-
|
|
347
|
+
const own = shardLines(store, speaker, home).filter((line) => fieldOf(line, 'scope') === readerScope(reader));
|
|
348
|
+
// Short selected-origin identities remain stored history; they cannot name
|
|
349
|
+
// an unambiguous current origin or supply its held observations.
|
|
350
|
+
const lineage = own.filter((line) => fieldOf(line, 'measure') === 'identity').map((line) => fieldOf(line, 'value')).filter(value => selectedBy === undefined || /^[a-f0-9]{64}$/.test(value));
|
|
306
351
|
const sum = source && reader.capsule === undefined ? bytesDigest(store, new TextEncoder().encode(`${reader.module} ${bundle}${lifetime === undefined ? "" : "\n" + JSON.stringify(lifetime)}`)).replace(/^sha256:/, '').slice(0, 12) : 'none';
|
|
307
|
-
const measured = reader.capsule !== undefined ? { by: source ? sha(`${reader.capsule.
|
|
352
|
+
const measured = reader.capsule !== undefined ? { by: source ? selectedBy ?? sha(`${reader.capsule.digest}\n${reader.capsule.region}${lifetime === undefined ? "" : "\n" + JSON.stringify(lifetime)}`).slice(0, 12) : 'none', lines: [] }
|
|
308
353
|
: source ? identityOf({ store, root, speaker, module: reader.module, kind: reader.kind, code: sum, lineage, lifetime, kept: own.filter((line) => /(?:^|\s)measure=(?:extension|emits)\s/.test(line)) })
|
|
309
354
|
: { by: 'none', lines: [] };
|
|
310
355
|
const by = measured.by;
|
|
@@ -400,7 +445,7 @@ export function observedClaims(root, store, options = {}) {
|
|
|
400
445
|
const running = existsSync(marker) && Date.now() - statSync(marker).mtimeMs < 30 * 60 * 1000 && Number.isInteger(pid) && pid > 0 && alive(pid);
|
|
401
446
|
if (!running) {
|
|
402
447
|
mkdirSync(dirname(marker), { recursive: true });
|
|
403
|
-
const child = spawn(process.execPath, [...childFlags(), processOf('observe'), root, reader.module], { cwd: root, detached: true, stdio: 'ignore' });
|
|
448
|
+
const child = spawn(process.execPath, [...childFlags(), processOf('observe'), root, reader.module, ...(readerSelection(reader) === undefined ? [] : [readerSelection(reader)])], { cwd: root, detached: true, stdio: 'ignore' });
|
|
404
449
|
child.unref();
|
|
405
450
|
writeFileSync(marker, `${reader.module}\n${child.pid ?? ''}\n`);
|
|
406
451
|
}
|
|
@@ -455,7 +500,7 @@ export function observedClaims(root, store, options = {}) {
|
|
|
455
500
|
const over = bytesDigest(store, new TextEncoder().encode(due.map((d) => d.digest).sort().join('\n'))).replace(/^sha256:/, '');
|
|
456
501
|
level.push(canonical({ scope: `cost/reader/${reader.module}`, role: 'writes', form: 'interval', measure: 'ms', value: `${spent}..${spent}`, by: `${speaker}:${by}`, at: `place:${over}` }));
|
|
457
502
|
if (lineage.at(-1) !== by)
|
|
458
|
-
level.push(canonical({ scope:
|
|
503
|
+
level.push(canonical({ scope: readerScope(reader), role: 'writes', form: 'alphabet', measure: 'identity', value: by, by: `${speaker}:${by}`, at: `place:${by}` }));
|
|
459
504
|
rmSync(storeAt(store, 'pending', by), { force: true });
|
|
460
505
|
}
|
|
461
506
|
for (const plan of plans.filter((one) => one.said !== undefined)) {
|
package/dist/fold/readers.d.ts
CHANGED
|
@@ -30,9 +30,10 @@ export type Look = {
|
|
|
30
30
|
export declare function lookOf(root: string, roleOf: (coordinate: string) => CoordinateRole): Look;
|
|
31
31
|
/**
|
|
32
32
|
* The reader regions of the capsules a place can run, where the place's own lock offers them or, at a world, the wire's offers/world list does for a
|
|
33
|
-
* region its own lock does not name:
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* region its own lock does not name: each selected capsule that writes a declared region reads the coordinates
|
|
34
|
+
* it declares among the place's offered coordinates. Selection identifies attribution, never independence.
|
|
35
|
+
* An existing reader suppresses only that same selected provider, region and place; an ambiguous legacy
|
|
36
|
+
* owner refuses instead of hiding another provider or invoking the same provider twice.
|
|
36
37
|
* A newly declared region does not need a retired reader in private history to be observed.
|
|
37
38
|
*/
|
|
38
39
|
export declare function capsuleReadersOf(root: string, store: string, standing: readonly string[], lines: readonly ReaderClaim[], resolve?: typeof capsulesFor): readonly ReaderClaim[];
|
package/dist/fold/readers.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolve } from "../host/io.js";
|
|
2
|
-
import { canonical, fields, matches } from '@lapxo/topos/wire';
|
|
2
|
+
import { canonical, fields, matches, byBytes } from '@lapxo/topos/wire';
|
|
3
3
|
import { namesAt, wordOn, wordsOf, wireLine } from "./wire.js";
|
|
4
4
|
import { fits } from "../observe/match.js";
|
|
5
5
|
import { bytesDigest, coordinateDigest } from "./digests.js";
|
|
@@ -36,13 +36,14 @@ const reachOfLine = (module, where) => {
|
|
|
36
36
|
};
|
|
37
37
|
/**
|
|
38
38
|
* The reader regions of the capsules a place can run, where the place's own lock offers them or, at a world, the wire's offers/world list does for a
|
|
39
|
-
* region its own lock does not name:
|
|
40
|
-
*
|
|
41
|
-
*
|
|
39
|
+
* region its own lock does not name: each selected capsule that writes a declared region reads the coordinates
|
|
40
|
+
* it declares among the place's offered coordinates. Selection identifies attribution, never independence.
|
|
41
|
+
* An existing reader suppresses only that same selected provider, region and place; an ambiguous legacy
|
|
42
|
+
* owner refuses instead of hiding another provider or invoking the same provider twice.
|
|
42
43
|
* A newly declared region does not need a retired reader in private history to be observed.
|
|
43
44
|
*/
|
|
44
45
|
export function capsuleReadersOf(root, store, standing, lines, resolve = capsulesFor) {
|
|
45
|
-
const
|
|
46
|
+
const origin = (capsule) => capsule?.selected?.selection;
|
|
46
47
|
const coordinateMeasure = wordOn(standing, 'region-coordinate', ownLock());
|
|
47
48
|
const localMeasures = wordsOf(standing, 'region-measures');
|
|
48
49
|
const pins = standing.filter(line => fieldOf(line, 'scope').startsWith('uses/') && fieldOf(line, 'value') !== 'withdraw');
|
|
@@ -59,16 +60,20 @@ export function capsuleReadersOf(root, store, standing, lines, resolve = capsule
|
|
|
59
60
|
const capsules = resolve(standing, place, root);
|
|
60
61
|
for (const line of offered) {
|
|
61
62
|
const region = fieldOf(line, 'scope').slice('region/'.length);
|
|
62
|
-
const by
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
for (const by of capsules.filter(({ capsule }) => capsule.lines.some((one) => fieldOf(one, 'scope') === `region/${region}` && fieldOf(one, 'measure') === 'writes'))) {
|
|
64
|
+
const pkg = fieldOf(by.offer, 'scope').split('/')[1] ?? '';
|
|
65
|
+
const reaches = lines.filter(reader => reachOfLine(reader.module, reader.where).includes(`${pkg} ${region} ${where || '/'}`));
|
|
66
|
+
if (reaches.some(reader => origin(reader.capsule) === undefined))
|
|
67
|
+
throw Error(`REFUSE·reader ${pkg}/${region} existing provider origin unmeasured at ${where || '/'}`);
|
|
68
|
+
if (by.capsule.selection !== undefined && lines.some(reader => origin(reader.capsule) === by.capsule.selection && reader.capsule?.region === region && reader.where.some(place => place.replace(/\/?$/, '/') === (where || '/'))))
|
|
69
|
+
continue;
|
|
70
|
+
const key = `${by.capsule.selection ?? by.capsule.digest} ${region}`;
|
|
71
|
+
const group = groups.get(key) ?? groups.set(key, { module: `${pkg}/${region}`, digest: by.capsule.digest, entry: fieldOf(by.offer, 'shape'), selected: by.capsule, region, reads: by.capsule.declaration.regions[region] ?? [], globs: new Map() }).get(key);
|
|
72
|
+
group.globs.set(where, fieldOf(line, 'value').split('|').filter(Boolean));
|
|
73
|
+
}
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
|
-
return [...groups.
|
|
76
|
+
return [...groups.entries()].sort(([a], [b]) => byBytes(a, b)).map(([, group]) => ({ id: group.module, shape: ['run'], where: [...group.globs.keys()], module: group.module, kind: 'process',
|
|
72
77
|
capsule: { selected: group.selected, digest: group.digest, entry: group.entry, region: group.region, reads: group.reads, globs: group.globs } }));
|
|
73
78
|
}
|
|
74
79
|
export function handedAt(root, reader, roleOf, look, place) {
|
package/dist/fold/resolved.js
CHANGED
|
@@ -41,7 +41,17 @@ export function receiptsOf(fold) {
|
|
|
41
41
|
const ran = [...new Set(fold.observed.filter((line) => fieldOf(line, 'measure') !== 'observed'
|
|
42
42
|
&& mine.has(`${fieldOf(line, 'by')} ${fieldOf(line, 'at')}`)))];
|
|
43
43
|
const ranAt = new Set(ran.map((line) => `${fieldOf(line, 'by')} ${fieldOf(line, 'at')}`));
|
|
44
|
-
const cones = new Set(fold.observed.filter((line) =>
|
|
44
|
+
const cones = new Set(fold.observed.filter((line) => {
|
|
45
|
+
if (fieldOf(line, 'measure') !== 'observed')
|
|
46
|
+
return false;
|
|
47
|
+
const at = fieldOf(line, 'at'), by = fieldOf(line, 'by');
|
|
48
|
+
// A physical input witnesses an owned reading's base, without becoming
|
|
49
|
+
// another location of its result. Even a foreign input belongs in that
|
|
50
|
+
// reading's portable carrier; its own child place is not the reader.
|
|
51
|
+
if (at.startsWith('witness:'))
|
|
52
|
+
return ranAt.has(`${by} place:${at.slice('witness:'.length)}`);
|
|
53
|
+
return own(fieldOf(line, 'scope')) && ranAt.has(`${by} ${at}`);
|
|
54
|
+
}).map(bytesOnly));
|
|
45
55
|
const paid = ledgerLines(fold.store, 'judge').filter((line) => fold.paid.some((demand) => fieldOf(demand, 'scope') === fieldOf(line, 'scope')
|
|
46
56
|
&& !(closed.length > 0 && fieldOf(demand, 'needs').split('|').filter(Boolean).length > 0
|
|
47
57
|
&& fieldOf(demand, 'needs').split('|').filter(Boolean).every((need) => closed.some((one) => need.startsWith(one))))));
|
package/dist/fold/signers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, join } from "../host/io.js";
|
|
2
|
-
import { authorityOf, isKeyClaim,
|
|
2
|
+
import { authorityOf, isKeyClaim, signersOf } from '@lapxo/topos/wire';
|
|
3
3
|
import { verifiesFields } from "../land/sign.js";
|
|
4
|
-
import { fieldOf, selfName } from "./claims.js";
|
|
4
|
+
import { fieldOf, selfName, parsedLine as parse } from "./claims.js";
|
|
5
5
|
import { keyFor, lockStanding } from "./keys.js";
|
|
6
6
|
import { storeRoot } from "../host/ports/store.js";
|
|
7
7
|
import { storeOf } from "../land/ledger.js";
|
|
@@ -15,12 +15,7 @@ export function moduleImports(source, coordinate) {
|
|
|
15
15
|
throw Error('REFUSE·source module syntax cannot be read: ' + coordinate);
|
|
16
16
|
}
|
|
17
17
|
const found = new Set();
|
|
18
|
-
const literal =
|
|
19
|
-
if (!node || typeof node !== 'object')
|
|
20
|
-
return;
|
|
21
|
-
const n = node;
|
|
22
|
-
return n['type'] === 'Literal' && typeof n['value'] === 'string' ? n['value'] : undefined;
|
|
23
|
-
};
|
|
18
|
+
const literal = stringLiteral;
|
|
24
19
|
const visit = (node) => {
|
|
25
20
|
if (!node || typeof node !== 'object')
|
|
26
21
|
return;
|
|
@@ -46,3 +41,10 @@ export function moduleImports(source, coordinate) {
|
|
|
46
41
|
visit(tree);
|
|
47
42
|
return [...found];
|
|
48
43
|
}
|
|
44
|
+
/** A JavaScript literal value; syntax inventory never executes it. */
|
|
45
|
+
export const stringLiteral = (node) => {
|
|
46
|
+
if (!node || typeof node !== 'object')
|
|
47
|
+
return;
|
|
48
|
+
const n = node;
|
|
49
|
+
return n['type'] === 'Literal' && typeof n['value'] === 'string' ? n['value'] : undefined;
|
|
50
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Existing admission-snapshot authentication and folded live projection.
|
|
2
|
+
* Consumer configuration evidence only; never a new authority lock. */
|
|
3
|
+
export declare function authenticatedRootStanding(root: string): readonly string[];
|
|
4
|
+
/** Legacy Root lines remain unchanged; contextual public output has its own carrier. */
|
|
5
|
+
export declare const authenticatedStanding: typeof authenticatedRootStanding;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { hashText } from "./hash.js";
|
|
2
|
+
import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
|
|
3
|
+
import { authorityFor } from "../fold/signers.js";
|
|
4
|
+
import { eraOf } from "../fold/digests.js";
|
|
5
|
+
import { fieldOf, foldClaims, isConfig } from "../fold/claims.js";
|
|
6
|
+
import { foldedEvidence } from '@lapxo/topos/contract';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
// One bounded historical projection, never a current authority/lease verdict.
|
|
9
|
+
// Its complete inputs are reread before lookup, including original signatures.
|
|
10
|
+
let historicalProjection;
|
|
11
|
+
function historicalIdentity(history, anchor) {
|
|
12
|
+
const hash = createHash('sha256'), text = hashText.bind(undefined, hash);
|
|
13
|
+
text('authenticated-root-history/1');
|
|
14
|
+
text(String(history.length));
|
|
15
|
+
for (const line of history)
|
|
16
|
+
text(line);
|
|
17
|
+
text(JSON.stringify(anchor));
|
|
18
|
+
return hash.digest('hex');
|
|
19
|
+
}
|
|
20
|
+
/** Existing admission-snapshot authentication and folded live projection.
|
|
21
|
+
* Consumer configuration evidence only; never a new authority lock. */
|
|
22
|
+
export function authenticatedRootStanding(root) {
|
|
23
|
+
const history = admissionHistoryAt(root, Number.MAX_SAFE_INTEGER), anchor = admissionAnchor(root);
|
|
24
|
+
const identity = historicalIdentity(history, anchor);
|
|
25
|
+
if (historicalProjection?.identity === identity)
|
|
26
|
+
return [...historicalProjection.standing];
|
|
27
|
+
const snapshots = new Map();
|
|
28
|
+
const admitted = history.filter(line => {
|
|
29
|
+
const epoch = Number(fieldOf(line, 'epoch')) || 0;
|
|
30
|
+
let authority = snapshots.get(epoch);
|
|
31
|
+
if (!authority) {
|
|
32
|
+
// This invocation has freshly read the complete signed inventory. Derive
|
|
33
|
+
// the same historical cutoff without reopening it for every epoch; the
|
|
34
|
+
// next invocation still rereads every authority input.
|
|
35
|
+
const snapshot = history.filter(record => !isConfig(record) || Number(fieldOf(record, 'epoch')) <= epoch);
|
|
36
|
+
// The signature vocabulary is configuration, not the as-yet
|
|
37
|
+
// unauthenticated evidence whose values are about to be folded.
|
|
38
|
+
const wire = snapshot.filter(record => fieldOf(record, 'scope').startsWith('wire/'));
|
|
39
|
+
authority = authorityFor(snapshot, anchor, eraOf(foldClaims(wire).standing).admitted);
|
|
40
|
+
snapshots.set(epoch, authority);
|
|
41
|
+
}
|
|
42
|
+
return authority.of(line).kind === 'admitted';
|
|
43
|
+
});
|
|
44
|
+
// The host authenticates against historical authority; Topos projects the
|
|
45
|
+
// authenticated history using the instrument's existing fold.
|
|
46
|
+
const { standing } = foldedEvidence(admitted, {
|
|
47
|
+
classification: () => 'local',
|
|
48
|
+
fold: lines => foldClaims(lines).standing,
|
|
49
|
+
});
|
|
50
|
+
historicalProjection = { identity, standing: Object.freeze([...standing]) };
|
|
51
|
+
return [...standing];
|
|
52
|
+
}
|
|
53
|
+
/** Legacy Root lines remain unchanged; contextual public output has its own carrier. */
|
|
54
|
+
export const authenticatedStanding = authenticatedRootStanding;
|
|
@@ -18,4 +18,4 @@ export declare class CapsuleProcessError extends Error {
|
|
|
18
18
|
}
|
|
19
19
|
export declare function validResponse(value: unknown, request: Request): value is Response;
|
|
20
20
|
/** Last-line JSON is the wire framing. No partial batch, failed exit or synthetic missing response is accepted. */
|
|
21
|
-
export declare function executeCapsule(command: string, args: readonly string[], requests: readonly Request[], limits?: Pick<SpawnSyncOptionsWithStringEncoding, 'timeout' | 'maxBuffer'>, lifetime?: ReaderLifetime): readonly Response[];
|
|
21
|
+
export declare function executeCapsule(command: string, args: readonly string[], requests: readonly Request[], limits?: Pick<SpawnSyncOptionsWithStringEncoding, 'timeout' | 'maxBuffer' | 'env' | 'cwd'>, lifetime?: ReaderLifetime): readonly Response[];
|