@lapxo/bound 0.1.4 → 0.1.6
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/LICENSE +0 -1
- package/README.md +26 -5
- package/TARGET.bound +84 -19
- package/dist/cli/accept.d.ts +5 -0
- package/dist/cli/accept.js +60 -14
- package/dist/cli/act-result.d.ts +3 -0
- package/dist/cli/act-result.js +48 -0
- package/dist/cli/owner.d.ts +2 -0
- package/dist/cli/owner.js +18 -2
- package/dist/cli/pass.js +15 -10
- package/dist/cli/place.js +8 -1
- package/dist/cli/verb.js +48 -2
- package/dist/fold/admission-history.d.ts +6 -0
- package/dist/fold/admission-history.js +22 -0
- package/dist/fold/claims.js +5 -1
- package/dist/fold/closed.js +44 -13
- package/dist/fold/digests.js +18 -11
- package/dist/fold/kept.js +9 -4
- package/dist/fold/keys.js +9 -6
- package/dist/fold/observed.js +196 -174
- package/dist/fold/resolved.js +6 -2
- package/dist/fold/signed.d.ts +5 -0
- package/dist/fold/signed.js +19 -15
- package/dist/host/act-cost.d.ts +13 -0
- package/dist/host/act-cost.js +25 -0
- package/dist/host/adapters/modules/javascript.d.ts +1 -0
- package/dist/host/adapters/modules/javascript.js +48 -0
- package/dist/host/capsule-process.js +22 -19
- package/dist/host/capsule.d.ts +2 -1
- package/dist/host/capsule.js +13 -12
- package/dist/host/io.d.ts +1 -1
- package/dist/host/io.js +1 -1
- package/dist/host/ports/ingress.d.ts +24 -0
- package/dist/host/ports/ingress.js +57 -0
- package/dist/host/ports/local-act.d.ts +34 -0
- package/dist/host/ports/local-act.js +100 -0
- package/dist/host/ports/publication.d.ts +10 -0
- package/dist/host/ports/publication.js +56 -0
- package/dist/host/reading-closures.d.ts +31 -0
- package/dist/host/reading-closures.js +146 -0
- package/dist/host/release.js +24 -2
- package/dist/host/selected-capsules.js +4 -4
- package/dist/host/walk.d.ts +6 -0
- package/dist/host/walk.js +193 -0
- package/dist/land/ledger.d.ts +1 -0
- package/dist/land/ledger.js +21 -2
- package/dist/land/owned.d.ts +1 -1
- package/dist/land/owned.js +5 -4
- package/dist/observe/files.js +12 -2
- package/dist/observe/regions.js +3 -5
- package/dist/render/field.d.ts +1 -7
- package/dist/render/field.js +6 -12
- package/dist/render/names.d.ts +1 -1
- package/dist/render/names.js +1 -1
- package/dist/render/view.js +16 -11
- package/docs/contracts/archive.md +11 -0
- package/docs/contracts/cli.md +19 -0
- package/docs/contracts/documents.md +35 -0
- package/docs/contracts/evidence-view.md +6 -0
- package/docs/contracts/publication.md +17 -0
- package/docs/contracts/typed-object.md +27 -0
- package/docs/contracts/view-walk.md +7 -0
- package/docs/contracts/views.md +29 -0
- package/docs/usage.md +6 -0
- package/package.json +6 -4
- package/receipts.bound +367 -77
package/dist/cli/accept.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export type Batch = {
|
|
2
2
|
readonly kind: 'signed';
|
|
3
3
|
readonly signed: readonly string[];
|
|
4
|
+
readonly records?: readonly string[];
|
|
4
5
|
readonly epoch: number;
|
|
6
|
+
readonly base: {
|
|
7
|
+
readonly digest: string;
|
|
8
|
+
readonly places: readonly string[];
|
|
9
|
+
};
|
|
5
10
|
readonly keyId?: string;
|
|
6
11
|
} | {
|
|
7
12
|
readonly kind: 'once';
|
package/dist/cli/accept.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { commitLocalAct, intactLocalAct, committedLocalActs } from "../host/ports/local-act.js";
|
|
1
2
|
import { withoutEffects } from "../host/read-only.js";
|
|
2
3
|
import { selectedWireAt } from "../host/selected-topos.js";
|
|
3
4
|
import { timing } from "../host/timing.js";
|
|
@@ -6,12 +7,12 @@ import { existsSync, rmdirSync, rmSync } from 'node:fs';
|
|
|
6
7
|
import { dirname, join, basename } from 'node:path';
|
|
7
8
|
import { closeReceipts, instrumentKeep, instrumentKept } from "../fold/closed.js";
|
|
8
9
|
import { bytesDigest, fullDigest, instrumentOf, signaturesOf } from "../fold/digests.js";
|
|
9
|
-
import { canonical, fromLine, LOCK, parse } from '@lapxo/topos/wire';
|
|
10
|
+
import { canonical, fromLine, LOCK, parse, actIdentity, actResultProfile } from '@lapxo/topos/wire';
|
|
10
11
|
import { fieldOf, isConfig, foldClaims, sayingOf, selfName } from "../fold/claims.js";
|
|
11
12
|
import { isReaderLock } from "../fold/observed.js";
|
|
12
13
|
import { authorityFor, publicKeyOf, rootSigner, writerFor } from "../fold/signers.js";
|
|
13
14
|
import { lockLines, lockStanding } from "../fold/keys.js";
|
|
14
|
-
import { keepVerdict, keptVerdict, land, landBlob, storeOf } from "../land/ledger.js";
|
|
15
|
+
import { keepVerdict, keptVerdict, land, landBlob, ledgerLines, storeOf } from "../land/ledger.js";
|
|
15
16
|
import { signConsentLot, verifiesOwnerLine } from "../land/sign.js";
|
|
16
17
|
import { signerFor } from "../host/signing.js";
|
|
17
18
|
import { appliedIn, ownLockOf } from "../fold/signed.js";
|
|
@@ -19,7 +20,7 @@ import { wordOf } from "../fold/wire.js";
|
|
|
19
20
|
import { hasVouchedTree, landCoordinates, locationsOf, vouchedCoordinates } from "../land/vouched.js";
|
|
20
21
|
import { entriesIn, observeFile } from "../observe/files.js";
|
|
21
22
|
import { coneOf, standingOf } from "./place.js";
|
|
22
|
-
import { ownLock, ownRoot
|
|
23
|
+
import { ownLock, ownRoot } from "../observe/runner.js";
|
|
23
24
|
import { widenedWithoutWitness } from "../judge/widening.js";
|
|
24
25
|
import { retractedTogether, withdrawal } from "../land/withdraw.js";
|
|
25
26
|
import { isCeiling } from "../fold/configures.js";
|
|
@@ -36,7 +37,7 @@ import { lateFacts } from "../judge/late.js";
|
|
|
36
37
|
import { paidOf } from "../fold/paid.js";
|
|
37
38
|
import { aliasPinned } from "../land/alias.js";
|
|
38
39
|
import { coordinatesWithdrawn } from "../land/leaving.js";
|
|
39
|
-
import { ownerOf, epochOf, forksItself, forksJoinedBy, renderInto, sideOf, signedDemands, signedOwnerLines } from "./owner.js";
|
|
40
|
+
import { ownerOf, epochOf, localActLines, forksItself, forksJoinedBy, renderInto, sideOf, signedDemands, signedOwnerLines } from "./owner.js";
|
|
40
41
|
import { claimLinesIn, entryOf, namedPlaces, placesOfFile, positionalsOf, valueOf } from "./args.js";
|
|
41
42
|
import { FLAGS } from "./names.js";
|
|
42
43
|
import { judgeDemand, landTake } from "./takes.js";
|
|
@@ -53,7 +54,15 @@ function admissionEvidence(root, signed, standing) {
|
|
|
53
54
|
if (coordinates.refused.length)
|
|
54
55
|
throw Error(`REFUSE·withdraw a file is still read: ${coordinates.refused.map(one => `${one.path} by ${one.by}`).join(' · ')}`);
|
|
55
56
|
const view = `${wordOf(standing, 'families', 'view')}/`;
|
|
56
|
-
const judged = signed.filter(one => isConfig(one) && fieldOf(one, 'role') === 'demands' && fieldOf(one, 'value') !== 'withdraw' && !fieldOf(one, 'scope').startsWith(view)).map(one =>
|
|
57
|
+
const judged = signed.filter(one => isConfig(one) && fieldOf(one, 'role') === 'demands' && fieldOf(one, 'value') !== 'withdraw' && !fieldOf(one, 'scope').startsWith(view)).map(one => {
|
|
58
|
+
const scope = fieldOf(one, 'scope');
|
|
59
|
+
const alreadyDemanded = standing.some(held => isConfig(held) && fieldOf(held, 'role') === 'demands'
|
|
60
|
+
&& fieldOf(held, 'value') !== 'withdraw' && fieldOf(held, 'scope') === scope
|
|
61
|
+
&& fieldOf(held, 'form') === fieldOf(one, 'form') && fieldOf(held, 'measure') === fieldOf(one, 'measure'));
|
|
62
|
+
// Declaring an obligation is not a claim to have discharged it.
|
|
63
|
+
return alreadyDemanded ? judgeDemand(root, one)
|
|
64
|
+
: { kind: 'owed', scope, why: 'new demand declared; no conformance reading is claimed' };
|
|
65
|
+
});
|
|
57
66
|
const refused = judged.filter(one => one.kind === 'refused' || (one.kind === 'judged' && one.verdict.kind === 'refuse'));
|
|
58
67
|
if (refused.length)
|
|
59
68
|
throw Error(`REFUSE·takes ${refused.map(one => `${one.scope}: ${one.kind === 'judged' ? one.verdict.why : one.kind === 'refused' ? one.why : ''}`).join(' · ')}`);
|
|
@@ -126,7 +135,9 @@ export async function signedBatch(root, args, files) {
|
|
|
126
135
|
process.stderr.write(`${selfName()}: REFUSE·place nothing landed · ${stray.join(', ')} is no place of the tree, and no line of the root founds it: --place names the own lock a line lands in\n`);
|
|
127
136
|
return { kind: 'exit', code: 2 };
|
|
128
137
|
}
|
|
129
|
-
const
|
|
138
|
+
const local = localActLines(root, destinations);
|
|
139
|
+
const base = { digest: fullDigest(store, local), places: destinations };
|
|
140
|
+
const epoch = epochOf(local) + 1;
|
|
130
141
|
const stale = proposed.filter((line) => fieldOf(line, 'sig')).flatMap((line) => ((by, key) => (key === undefined || !verifiesOwnerLine(line, key, signaturesOf(store).admitted)
|
|
131
142
|
? [`${fieldOf(line, 'scope')} · the signature does not hold for ${by || '∅'}`]
|
|
132
143
|
: []))(fieldOf(line, 'by'), publicKeyOf(root, fieldOf(line, 'by'))));
|
|
@@ -139,9 +150,13 @@ export async function signedBatch(root, args, files) {
|
|
|
139
150
|
const [named, places] = [namedPlaces(args), placesIn(root)];
|
|
140
151
|
const inLock = new Set(lockLines(store).map(sayingOf));
|
|
141
152
|
const inPlace = new Map(places.map((place) => [place, new Set(appliedIn(root, place).map(sayingOf))]));
|
|
153
|
+
const deliveryAuthority = authorityFor(lockLines(store), rootSigner(root), signaturesOf(store).admitted);
|
|
154
|
+
const delivered = (line) => Boolean(fieldOf(line, 'sig'))
|
|
155
|
+
&& deliveryAuthority.of(line).kind === 'admitted'
|
|
156
|
+
&& ledgerLines(store, fieldOf(line, 'by')).includes(line);
|
|
142
157
|
const held = (line, at = named.length ? named : byFile.flatMap(([heads, lines]) => (lines.includes(line) ? heads : []))) => at.length
|
|
143
158
|
? at.every((place) => inPlace.get(place)?.has(sayingOf(line)))
|
|
144
|
-
: inLock.has(sayingOf(line)) || places.some((place) => inPlace.get(place)?.has(sayingOf(line)));
|
|
159
|
+
: delivered(line) || inLock.has(sayingOf(line)) || places.some((place) => inPlace.get(place)?.has(sayingOf(line)));
|
|
145
160
|
if (proposed.every((line) => held(line))) {
|
|
146
161
|
if (proposed.some(line => !isConfig(line))) {
|
|
147
162
|
try {
|
|
@@ -237,7 +252,7 @@ export async function signedBatch(root, args, files) {
|
|
|
237
252
|
process.stderr.write(`${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
|
|
238
253
|
return { kind: 'exit', code: 1 };
|
|
239
254
|
}
|
|
240
|
-
return { kind: 'signed', signed, epoch, ...(drafted.length ? { keyId } : {}) };
|
|
255
|
+
return { kind: 'signed', signed, epoch, base, ...(drafted.length ? { keyId } : {}) };
|
|
241
256
|
}
|
|
242
257
|
/**
|
|
243
258
|
* accept is one act under the descent of its lines. Every place, then the root where they join, only when those lines
|
|
@@ -273,7 +288,7 @@ export async function accept(root, args) {
|
|
|
273
288
|
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';
|
|
274
289
|
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`);
|
|
275
290
|
const asked = Date.now();
|
|
276
|
-
return underTheRegions(store, every ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger }, started, owned, asked, regions));
|
|
291
|
+
return underTheRegions(store, every ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions));
|
|
277
292
|
}
|
|
278
293
|
async function acceptNow(root, batch, started, owned, asked = started, descent = []) {
|
|
279
294
|
const waited = Date.now() - asked;
|
|
@@ -283,6 +298,11 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
283
298
|
const took = timing(spent);
|
|
284
299
|
const { signed, epoch } = batch;
|
|
285
300
|
const store = storeOf(root);
|
|
301
|
+
const currentBase = fullDigest(store, localActLines(root, batch.base.places));
|
|
302
|
+
if (currentBase !== batch.base.digest) {
|
|
303
|
+
process.stderr.write(`${selfName()}: REFUSE·base moved ${batch.base.digest} → ${currentBase} · nothing landed\n`);
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
286
306
|
if (signed.some(line => !isConfig(line))) {
|
|
287
307
|
try {
|
|
288
308
|
await admittedObjects(root, [...signedOwnerLines(store), ...signed]);
|
|
@@ -335,10 +355,32 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
335
355
|
for (const name of openedBy(descent, told.standing, signed))
|
|
336
356
|
process.stderr.write(`OPEN ${name}\n`);
|
|
337
357
|
if (owned.own.size)
|
|
338
|
-
layReleases(store,
|
|
339
|
-
const
|
|
358
|
+
layReleases(store, root, told.standing, store);
|
|
359
|
+
const resultProfile = actResultProfile(snapshot.standing);
|
|
360
|
+
let landed = [];
|
|
361
|
+
if (resultProfile) {
|
|
362
|
+
const records = batch.records ?? signed;
|
|
363
|
+
const placements = [...(signed.length ? [{ place: '.', records: signed }] : []), ...[...owned.own].map(([place, records]) => ({ place: `${place}/${LOCK}`, records }))];
|
|
364
|
+
const digest = (bytes) => bytesDigest(store, new TextEncoder().encode(bytes));
|
|
365
|
+
const identity = actIdentity(records, { ...resultProfile, digest, placements, localEpoch: epoch });
|
|
366
|
+
const folder = writerFor(snapshot.standing, 'fold');
|
|
367
|
+
if (!folder)
|
|
368
|
+
throw Error('REFUSE·act native result requires a declared fold identity');
|
|
369
|
+
const receipt = canonical({ scope: resultProfile.scope, at: resultProfile.context, role: 'writes', form: 'alphabet', measure: 'digest', value: identity, by: folder, epoch: String(epoch) });
|
|
370
|
+
const bundle = { records: records.join('\n') + '\n', receipt: receipt + '\n', placements: placements.map(p => ({ place: p.place, records: p.records.join('\n') + '\n' })) };
|
|
371
|
+
await commitLocalAct(store, identity, descent, async () => bundle, intactLocalAct);
|
|
372
|
+
// Commitment precedes projections and conformance work. If either fails,
|
|
373
|
+
// this public identity still names the admitted act; it is never rollback.
|
|
374
|
+
process.stdout.write(`ACT ${identity} · epoch ${epoch} · admitted · conformance not asserted\n`);
|
|
375
|
+
for (const line of records)
|
|
376
|
+
process.stdout.write(line + '\n');
|
|
377
|
+
process.stdout.write(receipt + '\n');
|
|
378
|
+
landed = [{ at: 'committed act ' + identity, appended: records.length }];
|
|
379
|
+
}
|
|
380
|
+
else
|
|
381
|
+
landed = took('land', () => [...new Set(signed.map(line => fieldOf(line, 'by')))].map(by => land(store, by, signed.filter(line => fieldOf(line, 'by') === by))));
|
|
340
382
|
for (const [place, lines] of owned.own)
|
|
341
|
-
landOwned(root, place, lines);
|
|
383
|
+
landOwned(root, place, lines, resultProfile !== undefined);
|
|
342
384
|
landSaid(store, writerFor(told.standing, 'fold'), []);
|
|
343
385
|
for (const line of signed) {
|
|
344
386
|
const value = fieldOf(line, 'value');
|
|
@@ -392,7 +434,7 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
392
434
|
if (folder !== undefined)
|
|
393
435
|
land(store, folder, [beat(perLine, 'line'), beat(1, fold.kept ? 'kept' : 'folded'), beat(Date.now() - started, 'total')]);
|
|
394
436
|
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`
|
|
395
|
-
+ `TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · ${Date.now() - started} ms\n`
|
|
437
|
+
+ `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`
|
|
396
438
|
+ `SPENT ${spent.join(' ms · ')} ms · ${signed.length} lines\n`);
|
|
397
439
|
if (withdrawn) {
|
|
398
440
|
process.stdout.write(`WITHDRAW ${withdrawn.taken.length} taken · ${withdrawn.exact.length} exact on distributive scales · ${withdrawn.open.length} on open scales, not computed\n`);
|
|
@@ -407,6 +449,10 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
407
449
|
*/
|
|
408
450
|
function repaid(root, proposed, started) {
|
|
409
451
|
const store = storeOf(root);
|
|
452
|
+
const prior = committedLocalActs(store).find(act => { const lines = act.bundle.records.trimEnd().split('\n'); return lines.length === proposed.length && lines.every((line, i) => line === proposed[i]); });
|
|
453
|
+
if (prior) {
|
|
454
|
+
process.stdout.write(`ACT ${prior.identity} · epoch ${fieldOf(prior.bundle.receipt.trim(), 'epoch')} · admitted · replay\n` + prior.bundle.records + prior.bundle.receipt);
|
|
455
|
+
}
|
|
410
456
|
const asked = new Set(proposed.filter((line) => fieldOf(line, 'role') === 'demands').map((line) => fieldOf(line, 'scope')));
|
|
411
457
|
const view = `${wordOf(foldClaims(signedOwnerLines(store)).standing, 'families', 'view')}/`;
|
|
412
458
|
const standingAsked = signedDemands(store).filter((line) => asked.has(fieldOf(line, 'scope')) && !fieldOf(line, 'scope').startsWith(view));
|
|
@@ -444,7 +490,7 @@ function repaid(root, proposed, started) {
|
|
|
444
490
|
}
|
|
445
491
|
const rendered = renderInto(root, store);
|
|
446
492
|
process.stdout.write(`RENDER 0\n`);
|
|
447
|
-
process.stdout.write(`TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · ${Date.now() - started} ms\n`);
|
|
493
|
+
process.stdout.write(`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`);
|
|
448
494
|
return 0;
|
|
449
495
|
}
|
|
450
496
|
function packTake(one) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { actResultOf, actResultProfile } from '@lapxo/topos/wire';
|
|
2
|
+
import { actLines, intactLocalAct, localActResult } from "../host/ports/local-act.js";
|
|
3
|
+
import { storeOf } from "../land/ledger.js";
|
|
4
|
+
import { lockStanding } from "../fold/keys.js";
|
|
5
|
+
import { authorityFor, writerFor } from "../fold/signers.js";
|
|
6
|
+
import { eraOf } from "../fold/digests.js";
|
|
7
|
+
import { fieldOf, isConfig, foldClaims } from "../fold/claims.js";
|
|
8
|
+
import { ownRecordAdmitted } from "../fold/signed.js";
|
|
9
|
+
import { admissionAnchor, admissionHistoryAt } from "../fold/admission-history.js";
|
|
10
|
+
import { fullDigest } from "../host/digest.js";
|
|
11
|
+
import { wordOf, wireLine } from "../fold/wire.js";
|
|
12
|
+
import { viewsOf } from "../fold/views.js";
|
|
13
|
+
/** Public query of a committed identity, using the same admission authority.
|
|
14
|
+
* 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);
|
|
23
|
+
if (!bundle)
|
|
24
|
+
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);
|
|
29
|
+
const admittedBefore = foldClaims(before.filter(line => authority.of(line).kind === 'admitted')).standing;
|
|
30
|
+
const profile = actResultProfile(admittedBefore);
|
|
31
|
+
if (!profile)
|
|
32
|
+
throw Error('REFUSE·act no result profile at admission');
|
|
33
|
+
const family = authority.admitted.some(signer => !signer.coverage.includes('*')) ? wordOf(admittedBefore, 'families', 'region') : '';
|
|
34
|
+
const algorithm = identity.slice(0, identity.indexOf(':'));
|
|
35
|
+
const named = fieldOf(wireLine(admittedBefore, 'digest-algorithms') ?? '', 'value').split('|').map(value => value.split(':')[0]);
|
|
36
|
+
if (!named.includes(algorithm))
|
|
37
|
+
throw Error('REFUSE·act digest algorithm was not admitted at commitment');
|
|
38
|
+
const result = actResultOf(records, bundle.receipt.trim(), { ...profile, localEpoch: epoch,
|
|
39
|
+
digest: bytes => fullDigest(bytes, algorithm),
|
|
40
|
+
placements: bundle.placements.map(p => ({ place: p.place, records: actLines(p.records) })),
|
|
41
|
+
admitsRecord: line => {
|
|
42
|
+
if (isConfig(line) && Number(fieldOf(line, 'epoch')) !== epoch)
|
|
43
|
+
return false;
|
|
44
|
+
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
|
+
},
|
|
46
|
+
verifiesReceipt: line => fieldOf(line, 'by') === writerFor(admittedBefore, 'fold') && intactLocalAct(identity, bundle), });
|
|
47
|
+
process.stdout.write(`ACT ${result.identity} · epoch ${result.epoch} · admitted · conformance not asserted\n` + bundle.records + bundle.receipt);
|
|
48
|
+
}
|
package/dist/cli/owner.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare function historyOf(root: string, store: string, demands: readonly
|
|
|
14
14
|
};
|
|
15
15
|
export declare function signedDemands(store: string, root?: string): string[];
|
|
16
16
|
export declare function forkedScopes(store: string): Set<string>;
|
|
17
|
+
/** Import receipts are local acts; foreign evidence and sender epochs never enter this input. */
|
|
18
|
+
export declare function localActLines(root: string, placed?: readonly string[]): readonly string[];
|
|
17
19
|
export declare function epochOf(lines: readonly string[]): number;
|
|
18
20
|
export declare function targetFile(root: string): string;
|
|
19
21
|
export declare function renderInto(root: string, store: string): {
|
package/dist/cli/owner.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { committedLocalActs } from "../host/ports/local-act.js";
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { ingressReceipts } from "../host/ports/ingress.js";
|
|
1
4
|
import { readOnly } from "../host/read-only.js";
|
|
2
5
|
import { keyFor, lockLines, releaseOf, signedLockLines } from "../fold/keys.js";
|
|
3
6
|
import { existsSync, renameSync, writeFileSync } from 'node:fs';
|
|
4
7
|
import { join, resolve } from 'node:path';
|
|
5
|
-
import { bytesDigest, fullDigest, memoized, signaturesOf } from "../fold/digests.js";
|
|
8
|
+
import { bytesDigest, fullDigest, memoized, signaturesOf, instrumentDigest } from "../fold/digests.js";
|
|
6
9
|
import { claimKey, isConfig, fieldOf, foldClaims, LOCK, selfName } from "../fold/claims.js";
|
|
7
10
|
import { asksThePlace } from "../fold/configures.js";
|
|
8
11
|
import { receiptsIn } from "../fold/receipts.js";
|
|
@@ -10,6 +13,7 @@ import { authorityFor, releaseSigner, rootSigner } from "../fold/signers.js";
|
|
|
10
13
|
import { storeRoot } from "../host/ports/store.js";
|
|
11
14
|
import { blobAt, landBlob, ledgerLines, storeOf } from "../land/ledger.js";
|
|
12
15
|
import { observeText } from "../observe/files.js";
|
|
16
|
+
import { appliedHistoryIn } from "../fold/signed.js";
|
|
13
17
|
import { everLanded, inside, locationsOf } from "../land/vouched.js";
|
|
14
18
|
import { renderTarget } from "../render/target.js";
|
|
15
19
|
export const ownerOf = (store, told = []) => keyFor(store, 'authorize', told);
|
|
@@ -47,6 +51,18 @@ export function forkedScopes(store) {
|
|
|
47
51
|
const lines = signedOwnerLines(store);
|
|
48
52
|
return new Set(foldClaims(lines, forkReceipts(store, lines)).forks.flatMap((f) => f.lines.map((line) => fieldOf(line, 'scope'))));
|
|
49
53
|
}
|
|
54
|
+
/** Import receipts are local acts; foreign evidence and sender epochs never enter this input. */
|
|
55
|
+
export function localActLines(root, placed = []) {
|
|
56
|
+
const store = storeOf(root), local = lockLines(store), receipts = ingressReceipts(store);
|
|
57
|
+
if (receipts.length) {
|
|
58
|
+
const authority = authorityFor(local, rootSigner(root), signaturesOf(store).admitted);
|
|
59
|
+
for (const receipt of receipts) {
|
|
60
|
+
if (fieldOf(receipt, 'scope') !== 'receipts' || fieldOf(receipt, 'role') !== 'writes' || fieldOf(receipt, 'form') !== 'alphabet' || fieldOf(receipt, 'measure') !== 'digest' || authority.of(receipt).kind !== 'admitted')
|
|
61
|
+
throw Error('REFUSE·ingress local import receipt is not admitted');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [...local, ...receipts, ...committedLocalActs(store).map(act => act.bundle.receipt.trim()), ...placed.flatMap(place => appliedHistoryIn(root, place))];
|
|
65
|
+
}
|
|
50
66
|
export function epochOf(lines) {
|
|
51
67
|
return lines.reduce((top, line) => Math.max(top, Number(fieldOf(line, 'epoch')) || 0), 0);
|
|
52
68
|
}
|
|
@@ -92,7 +108,7 @@ export function renderedLock(root, store, told = []) {
|
|
|
92
108
|
}
|
|
93
109
|
const anchor = [join(storeRoot(root), `${selfName()}.keys`), join(storeOf(root), `${selfName()}.keys`)].map((at) => observeText(at) ?? '').join('\n');
|
|
94
110
|
const judged = ledgerLines(store, 'judge');
|
|
95
|
-
const key = `lock ${fullDigest(store, [new Date().toISOString().slice(0, 10), anchor, `${judged.length} ${judged.at(-1) ?? ''}`, ...owned])}`;
|
|
111
|
+
const key = `lock ${fullDigest(store, [instrumentDigest(store, fileURLToPath(import.meta.url), [], root), new Date().toISOString().slice(0, 10), anchor, `${judged.length} ${judged.at(-1) ?? ''}`, ...owned])}`;
|
|
96
112
|
const held = memoized(store, key, () => {
|
|
97
113
|
const bytes = new TextEncoder().encode(JSON.stringify(render()));
|
|
98
114
|
const digest = bytesDigest(store, bytes);
|
package/dist/cli/pass.js
CHANGED
|
@@ -6,9 +6,9 @@ import { foldPlace, standingOf } from "./place.js";
|
|
|
6
6
|
import { fieldOf, selfName } from "../fold/claims.js";
|
|
7
7
|
import { observedClaims } from "../fold/observed.js";
|
|
8
8
|
import { layReleases, resolvedOf } from "../host/release.js";
|
|
9
|
-
import { ownLock, ownRoot,
|
|
9
|
+
import { ownLock, ownRoot, processOf } from "../observe/runner.js";
|
|
10
10
|
import { landLaid } from "../fold/laid.js";
|
|
11
|
-
import { actSeconds, idleCount, instrumentKeep, instrumentKept, meets, noteIdle, receiptPlaces, receiptsSeen } from "../fold/closed.js";
|
|
11
|
+
import { actSeconds, closeReceipts, idleCount, instrumentKeep, instrumentKept, meets, noteIdle, receiptPlaces, receiptsSeen } from "../fold/closed.js";
|
|
12
12
|
import { algorithmCost, instrumentOf, instrumentDigest } from "../fold/digests.js";
|
|
13
13
|
import { writerFor } from "../fold/signers.js";
|
|
14
14
|
import { defaulted, placesOf, viewsOf } from "../fold/views.js";
|
|
@@ -24,6 +24,7 @@ import { releasePolicyLines } from "../fold/release-policy.js";
|
|
|
24
24
|
import { carriedFrom } from "../fold/resolved.js";
|
|
25
25
|
import { wireLine } from "../fold/wire.js";
|
|
26
26
|
import { isWire, RECEIPTS } from "../fold/claims.js";
|
|
27
|
+
import { withoutEffects } from "../host/read-only.js";
|
|
27
28
|
/**
|
|
28
29
|
* The pass of a place, in one process: the releases its instrument names are laid out first, its readers observe
|
|
29
30
|
* once, each place a view writes in is folded once, the places before the root whose door reads them, and every coordinate a
|
|
@@ -74,7 +75,7 @@ async function checked(root, entry, under) {
|
|
|
74
75
|
export async function pass(root, entry, under, check) {
|
|
75
76
|
if (check) {
|
|
76
77
|
// Verification reads existing evidence; it grants no admission authority.
|
|
77
|
-
return emptyLedger(storeOf(root)) ? checked(root, entry, under) : underTheLock(storeOf(root), 'check', () => checked(root, entry, under));
|
|
78
|
+
return withoutEffects(() => emptyLedger(storeOf(root)) ? checked(root, entry, under) : underTheLock(storeOf(root), 'check', () => checked(root, entry, under)));
|
|
78
79
|
}
|
|
79
80
|
if (!under && emptyLedger(storeOf(root))) {
|
|
80
81
|
sayStranger(root);
|
|
@@ -85,8 +86,11 @@ export async function pass(root, entry, under, check) {
|
|
|
85
86
|
const swept = await sweep(root, entry, under, check, []);
|
|
86
87
|
for (const coordinate of check ? swept.written : [])
|
|
87
88
|
process.stdout.write(`DIFFERS ${coordinate}\n`);
|
|
89
|
+
// Byte seals describe the completed render pass, not its intermediate outputs.
|
|
90
|
+
if (!swept.written.length)
|
|
91
|
+
await closeReceipts(root, storeOf(root));
|
|
88
92
|
const closed = meets(root, storeOf(root), under ?? '') && !swept.written.length;
|
|
89
|
-
const fold = foldPlace(root, entry, under, false, { standing: swept.standingAt.get(under ?? '') ?? standingOf(root, under), observed: swept.observed.length ? swept.observed : observedClaims(root, storeOf(root), { wait: false }), free:
|
|
93
|
+
const fold = foldPlace(root, entry, under, false, { standing: swept.standingAt.get(under ?? '') ?? standingOf(root, under), observed: swept.settled ? [] : swept.observed.length ? swept.observed : observedClaims(root, storeOf(root), { wait: false }), free: !swept.settled });
|
|
90
94
|
for (const line of releasePolicyLines(fold.release))
|
|
91
95
|
process.stdout.write(`${line}\n`);
|
|
92
96
|
if (closed) {
|
|
@@ -140,14 +144,15 @@ async function sweep(root, entry, under, check, told, journal, given, keep, reac
|
|
|
140
144
|
process.stderr.write(`CLOSED ${under} · lock ${lock} ms · hash ${hash} ms\n`);
|
|
141
145
|
const seconds = actSeconds();
|
|
142
146
|
process.stderr.write(`COST act ${seconds} s\n`);
|
|
143
|
-
return { same: 0, written: [], places: under === undefined ? read.count : 1, observed: [], standingAt: new Map(), folds: new Map(), said: [] };
|
|
147
|
+
return { same: 0, written: [], places: under === undefined ? read.count : 1, observed: [], standingAt: new Map(), folds: new Map(), said: [], settled: true };
|
|
144
148
|
}
|
|
145
|
-
|
|
149
|
+
const lock = standingOf(root, undefined, told);
|
|
150
|
+
// A place asks for its own mounts; the instrument catalog is not a consumer dependency.
|
|
151
|
+
layReleases(store, root, lock.standing, store);
|
|
146
152
|
const descent = reach ?? (under ? [under.replace(/\/$/, '')] : undefined);
|
|
147
153
|
const limited = descent !== undefined;
|
|
148
154
|
const observed = given ?? observedClaims(root, store, { wait: true, ...(descent !== undefined ? { reach: descent } : {}) });
|
|
149
|
-
|
|
150
|
-
landLaid(store, ownRoot(), ownLock(), writerFor(lock.standing, 'fold'));
|
|
155
|
+
landLaid(store, root, lock.standing, writerFor(lock.standing, 'fold'));
|
|
151
156
|
const shaped = (standing) => [...viewsOf(standing).values()].flatMap((view) => ('regions' in view && view.shape ? [view] : []));
|
|
152
157
|
const views = shaped(lock.standing);
|
|
153
158
|
const tree = viewsOf(lock.lock);
|
|
@@ -264,8 +269,8 @@ async function sweep(root, entry, under, check, told, journal, given, keep, reac
|
|
|
264
269
|
}
|
|
265
270
|
const said = [...answered.values()].flat();
|
|
266
271
|
if (!check && journal === undefined)
|
|
267
|
-
landSaid(store, writerFor(lock.standing, 'fold'), [...resolvedOf(store,
|
|
268
|
-
return { same, written, places: places.length + (under ? 0 : 1), observed, standingAt, folds, said: journal === undefined ? [] : said };
|
|
272
|
+
landSaid(store, writerFor(lock.standing, 'fold'), [...resolvedOf(store, root, lock.standing, store), ...said]);
|
|
273
|
+
return { same, written, places: places.length + (under ? 0 : 1), observed, standingAt, folds, said: journal === undefined ? [] : said, settled: false };
|
|
269
274
|
}
|
|
270
275
|
function drawParallel(jobs) {
|
|
271
276
|
const out = new Map();
|
package/dist/cli/place.js
CHANGED
|
@@ -7,7 +7,7 @@ import { basename, join } from 'node:path';
|
|
|
7
7
|
import { observeText } from "../observe/files.js";
|
|
8
8
|
import { placesOf, viewsOf } from "../fold/views.js";
|
|
9
9
|
import { prerender, viewOf } from "../render/view.js";
|
|
10
|
-
import { meets } from "../fold/closed.js";
|
|
10
|
+
import { meets, instrumentKept } from "../fold/closed.js";
|
|
11
11
|
import { foldPoints, keepFold, keptPlace } from "../fold/kept.js";
|
|
12
12
|
import { openCells } from "../render/open.js";
|
|
13
13
|
import { boundOf, packRegions, regionsNamed, regionsOf, unlikeReplay } from "../fold/region.js";
|
|
@@ -165,6 +165,13 @@ function inheritedOf(root, standing) {
|
|
|
165
165
|
export function coneOf(root, entry, ceilings, descent) {
|
|
166
166
|
const store = storeOf(root);
|
|
167
167
|
const { standing, rendered } = standingOf(root);
|
|
168
|
+
// Admission consumes the verified completed fold; it does not ask providers again.
|
|
169
|
+
// Receipt bytes, reader history and instrument identity must all still agree.
|
|
170
|
+
if (meets(root, store, '') && instrumentKept(store) === instrumentOf(store, entry, standing, root, ownLock())) {
|
|
171
|
+
const held = keptPlace(store);
|
|
172
|
+
if (held !== undefined)
|
|
173
|
+
return { standing, observed: held.observed, own: held.own, kept: true };
|
|
174
|
+
}
|
|
168
175
|
const named = regionsNamed(standing);
|
|
169
176
|
const reach = descent !== undefined && descent.length ? [...descent] : [...new Set(ceilings.map((line) => boundOf(named, line).region))];
|
|
170
177
|
const read = observedClaims(root, store, { wait: false, ...(reach.includes('') ? {} : { reach }) });
|
package/dist/cli/verb.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { printActResult } from "./act-result.js";
|
|
2
3
|
import { withContentStore } from "../host/content-store.js";
|
|
3
4
|
import { withoutEffects } from "../host/read-only.js";
|
|
4
5
|
import { resolveSources, resolveRequested } from "../host/source-content.js";
|
|
@@ -10,6 +11,7 @@ import { objectFold } from "../fold/object.js";
|
|
|
10
11
|
import { OBJECT_VIEW } from '@lapxo/topos/wire';
|
|
11
12
|
import { toposStanding } from '@lapxo/topos/standing';
|
|
12
13
|
import { wireAt, parse as parseWire } from '@lapxo/topos/wire';
|
|
14
|
+
import { lockStanding } from "../fold/keys.js";
|
|
13
15
|
import { ownLock } from "../observe/runner.js";
|
|
14
16
|
import { signedOwnerLines } from "./owner.js";
|
|
15
17
|
import { existsSync, lstatSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
@@ -42,6 +44,9 @@ import { ownerOf, renderInto, signedDemands, targetFile } from "./owner.js";
|
|
|
42
44
|
import { claimLinesIn, entryOf, namedPlaces, positionalsOf, valueOf } from "./args.js";
|
|
43
45
|
import { EXIT, FLAGS, NAMED_VIEWS, helpLines } from "./names.js";
|
|
44
46
|
import { accept, signedBatch } from "./accept.js";
|
|
47
|
+
import { renderWalk, landWalk, foreignWalkLines } from "../host/walk.js";
|
|
48
|
+
import { isWalkHeader } from '@lapxo/topos/walk';
|
|
49
|
+
import { ingressBundles, ingressReceipts } from "../host/ports/ingress.js";
|
|
45
50
|
/** The first land of a lock: TARGET.bound into an empty owner ledger, then the sources that lock names. */
|
|
46
51
|
async function firstLand(root, args) {
|
|
47
52
|
const started = Date.now();
|
|
@@ -184,6 +189,23 @@ async function view(root, under, asked, told = [], coord, key, includePolicy = t
|
|
|
184
189
|
process.stderr.write(`${selfName()}: the owner ledger is empty; land it before rendering a view\n`);
|
|
185
190
|
return EXIT.refuse;
|
|
186
191
|
}
|
|
192
|
+
if (name === 'act') {
|
|
193
|
+
if (under || told.length)
|
|
194
|
+
throw Error('REFUSE·act committed identity query only');
|
|
195
|
+
printActResult(root, coord);
|
|
196
|
+
return EXIT.closed;
|
|
197
|
+
}
|
|
198
|
+
if (name === 'evidence' && !under && !told.length && ingressBundles(storeOf(root)).length) {
|
|
199
|
+
const declared = viewsOf(lockStanding(storeOf(root))).get(name);
|
|
200
|
+
if (!declared || declared.shape || !declared.regions.some(region => region.name === 'evidence'))
|
|
201
|
+
throw Error('REFUSE·view foreign evidence requires a declared evidence view');
|
|
202
|
+
const received = await foreignWalkLines(root), authority = authorityFor(lockLines(storeOf(root)), rootSigner(root), signaturesOf(storeOf(root)).admitted);
|
|
203
|
+
const local = [...new Set(authority.admitted.flatMap(key => ledgerLines(storeOf(root), key.id)))].filter(line => authority.of(line).kind === 'admitted');
|
|
204
|
+
for (const line of [...new Set([...local, ...ingressReceipts(storeOf(root)), ...received])])
|
|
205
|
+
process.stdout.write(line + '\n');
|
|
206
|
+
process.stdout.write('FACT authenticated histories · readings · no encounters computed\n');
|
|
207
|
+
return EXIT.closed;
|
|
208
|
+
}
|
|
187
209
|
const at = Date.now();
|
|
188
210
|
const place = under?.replace(/\/$/, '');
|
|
189
211
|
const released = releaseOf(storeOf(root));
|
|
@@ -367,6 +389,12 @@ async function landVerb(root, rest) {
|
|
|
367
389
|
process.once('uncaughtException', (error) => die(error instanceof Error ? error.message : String(error)));
|
|
368
390
|
process.once('unhandledRejection', (error) => die(error instanceof Error ? error.message : String(error)));
|
|
369
391
|
try {
|
|
392
|
+
const proposed = batch.flatMap(claimLinesIn);
|
|
393
|
+
if (proposed.some(isWalkHeader)) {
|
|
394
|
+
for (const line of await landWalk(root, proposed, valueOf(rest, FLAGS.key), valueOf(rest, FLAGS.keyFile), valueOf(rest, FLAGS.signer)))
|
|
395
|
+
process.stdout.write(line + '\n');
|
|
396
|
+
return EXIT.closed;
|
|
397
|
+
}
|
|
370
398
|
return await accept(root, rest);
|
|
371
399
|
}
|
|
372
400
|
catch (error) {
|
|
@@ -395,7 +423,7 @@ function foldFile(at) {
|
|
|
395
423
|
process.stdout.write(`${line}\n`);
|
|
396
424
|
for (const fork of folded.forks)
|
|
397
425
|
process.stdout.write(`FORK ${fork.key}${fork.state ? ` · ${fork.state}` : ''}\n`);
|
|
398
|
-
process.stdout.write(`FACT ${folded.standing.length} standing · ${folded.forks.length} forks · ${basename(at)}\n`);
|
|
426
|
+
process.stdout.write(`FACT ${folded.standing.length} standing · ${folded.forks.length} forks · ${basename(at)} · readings · no encounters computed\n`);
|
|
399
427
|
return EXIT.closed;
|
|
400
428
|
}
|
|
401
429
|
function foldVerb(root, rest) {
|
|
@@ -419,7 +447,7 @@ async function foldAt(root, under, argv) {
|
|
|
419
447
|
async function foldRequested(root, under, argv) {
|
|
420
448
|
const as = valueOf(argv, FLAGS.as);
|
|
421
449
|
const prospective = as !== undefined && positionalsOf(argv).some(one => existsSync(resolve(one)) && !lstatSync(resolve(one)).isDirectory());
|
|
422
|
-
if (as === NAMED_VIEWS.help || prospective || argv.includes(FLAGS.check))
|
|
450
|
+
if (as?.split('@')[0] === 'act' || as === NAMED_VIEWS.help || prospective || argv.includes(FLAGS.check))
|
|
423
451
|
return foldResolved(root, under, argv);
|
|
424
452
|
return resolveRequested(digest => resolveSources(standingOf(root, under).standing, [digest]), () => foldResolved(root, under, argv));
|
|
425
453
|
}
|
|
@@ -429,6 +457,19 @@ async function foldResolved(root, under, argv) {
|
|
|
429
457
|
// Public content is authenticated by its digest, independently of owner history.
|
|
430
458
|
// A named view does not request mounting every release of the instrument.
|
|
431
459
|
const receiptCheck = as === undefined && argv.includes(FLAGS.check);
|
|
460
|
+
if (as?.split('@')[0] === 'walk') {
|
|
461
|
+
if (argv.includes(FLAGS.check))
|
|
462
|
+
throw Error('REFUSE·walk check does not initiate or sign an exchange');
|
|
463
|
+
if (under || inputs.length > 1)
|
|
464
|
+
throw Error('REFUSE·walk one place and one authenticated peer inventory per fold');
|
|
465
|
+
const depth = as.split('@')[1];
|
|
466
|
+
if (depth !== undefined && (!/^(0|[1-9][0-9]*)$/.test(depth) || !Number.isSafeInteger(Number(depth))))
|
|
467
|
+
throw Error('REFUSE·walk invalid resolution');
|
|
468
|
+
const peer = inputs.length ? claimLinesIn(inputs[0]) : [];
|
|
469
|
+
for (const line of await renderWalk(root, depth === undefined ? undefined : Number(depth), peer, valueOf(argv, FLAGS.key), valueOf(argv, FLAGS.keyFile), valueOf(argv, FLAGS.signer)))
|
|
470
|
+
process.stdout.write(line + '\n');
|
|
471
|
+
return EXIT.closed;
|
|
472
|
+
}
|
|
432
473
|
if (as === OBJECT_VIEW) {
|
|
433
474
|
const inputs = positionalsOf(argv).filter(one => existsSync(resolve(one)) && !lstatSync(resolve(one)).isDirectory());
|
|
434
475
|
if (inputs.length > 1)
|
|
@@ -463,6 +504,11 @@ async function foldResolved(root, under, argv) {
|
|
|
463
504
|
}
|
|
464
505
|
const named = inputs;
|
|
465
506
|
const coord = positionalsOf(argv).find((one) => one !== under?.replace(/\/$/, '') && !one.startsWith('-') && !(existsSync(resolve(one)) && lstatSync(resolve(one)).isDirectory()));
|
|
507
|
+
if (as === 'act') {
|
|
508
|
+
if (positionalsOf(argv).length !== 1)
|
|
509
|
+
throw Error('REFUSE·act name one committed identity');
|
|
510
|
+
return view(root, under, as, [], coord);
|
|
511
|
+
}
|
|
466
512
|
if ((as === 'why' || as === NAMED_VIEWS.because || as.split('@')[0] === 'why') && coord !== undefined)
|
|
467
513
|
return view(root, under, as === NAMED_VIEWS.because ? 'why' : as, [], coord, key);
|
|
468
514
|
if (!named.length)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Signer } from '@lapxo/topos/wire';
|
|
2
|
+
/** Local admission history only. Foreign ingress is evidence, never coverage.
|
|
3
|
+
* The clock cutoff is meaningful for configuration; object clocks stay intact. */
|
|
4
|
+
export declare function admissionHistoryAt(root: string, epoch: number): readonly string[];
|
|
5
|
+
/** The existing bootstrap's public root of trust, not today's key selection. */
|
|
6
|
+
export declare function admissionAnchor(root: string): Signer;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { ledgerLines, ledgerWriters, storeOf } from "../land/ledger.js";
|
|
4
|
+
import { fieldOf, isConfig, selfName } from "./claims.js";
|
|
5
|
+
/** Local admission history only. Foreign ingress is evidence, never coverage.
|
|
6
|
+
* The clock cutoff is meaningful for configuration; object clocks stay intact. */
|
|
7
|
+
export function admissionHistoryAt(root, epoch) {
|
|
8
|
+
const store = storeOf(root);
|
|
9
|
+
return [...new Set(ledgerWriters(store).flatMap(writer => ledgerLines(store, writer)))]
|
|
10
|
+
.filter(line => fieldOf(line, 'sig') !== '' && (!isConfig(line) || Number(fieldOf(line, 'epoch')) <= epoch));
|
|
11
|
+
}
|
|
12
|
+
/** The existing bootstrap's public root of trust, not today's key selection. */
|
|
13
|
+
export function admissionAnchor(root) {
|
|
14
|
+
const at = join(storeOf(root), `${selfName()}.keys`);
|
|
15
|
+
const keys = JSON.parse(readFileSync(at, 'utf8'));
|
|
16
|
+
const anchors = Object.values(keys);
|
|
17
|
+
if (anchors.length !== 1 || !anchors[0]?.publicKey || !anchors[0].coverage.length)
|
|
18
|
+
throw Error('REFUSE·act historical admission requires one bootstrap anchor');
|
|
19
|
+
const anchor = anchors[0];
|
|
20
|
+
return { id: anchor.id, keyClass: 'authorize', publicKey: anchor.publicKey, coverage: anchor.coverage,
|
|
21
|
+
depth: { lo: 1, hi: 16 }, admittedBy: [] };
|
|
22
|
+
}
|
package/dist/fold/claims.js
CHANGED
|
@@ -38,7 +38,11 @@ function regionsOf(line) {
|
|
|
38
38
|
}
|
|
39
39
|
function contracted(line, sides, receipts) {
|
|
40
40
|
const scope = fieldsOf(line)['scope'] ?? '';
|
|
41
|
-
const
|
|
41
|
+
const named = regionsOf(line);
|
|
42
|
+
// File receipts can discharge only declared file regions. No region is not an empty proof.
|
|
43
|
+
if (named.length === 0)
|
|
44
|
+
return false;
|
|
45
|
+
const mine = exclusive(named, sides.filter((other) => other !== line).map(regionsOf));
|
|
42
46
|
const touched = receipts.filter((r) => r.scope === scope).flatMap((r) => r.moved.map((coordinate) => point(coordinate)));
|
|
43
47
|
return contracts(mine, touched);
|
|
44
48
|
}
|