@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/host/release.js
CHANGED
|
@@ -48,11 +48,33 @@ function layDir(from, into) {
|
|
|
48
48
|
writeFileSync(dest, bytes, { flag: 'wx' });
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
function swapPlace(place, next, digest) {
|
|
51
|
+
function swapPlace(place, next, digest, ready) {
|
|
52
52
|
const old = `${next}.old`;
|
|
53
53
|
if (lstatSync(old, { throwIfNoEntry: false }) !== undefined)
|
|
54
54
|
throw new Error(`REFUSE·release recovery required for ${old}`);
|
|
55
55
|
writeFileSync(join(next, laidMark), digest, { flag: 'wx' });
|
|
56
|
+
if (ready !== undefined) {
|
|
57
|
+
// Digest-addressed trees publish once. A verified concurrent winner is reused,
|
|
58
|
+
// never moved aside while another process may be importing its members.
|
|
59
|
+
if (ready()) {
|
|
60
|
+
rmSync(next, { recursive: true, force: true });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
renameSync(next, place);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const code = error.code;
|
|
69
|
+
if (code !== 'EEXIST' && code !== 'ENOTEMPTY')
|
|
70
|
+
throw error;
|
|
71
|
+
if (ready()) {
|
|
72
|
+
rmSync(next, { recursive: true, force: true });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// An existing invalid cache retains the repair/rollback path below.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
let moved = false;
|
|
57
79
|
try {
|
|
58
80
|
if (lstatSync(place, { throwIfNoEntry: false }) !== undefined) {
|
|
@@ -224,7 +246,7 @@ export function runtimeTreeAt(store, own, digest, contentStore = own, root = '')
|
|
|
224
246
|
const next = nextPlace(place);
|
|
225
247
|
try {
|
|
226
248
|
layFiles(store, own, files, next);
|
|
227
|
-
swapPlace(place, next, digest);
|
|
249
|
+
swapPlace(place, next, digest, () => observeText(join(place, laidMark)) === digest && layoutMatches(place, files));
|
|
228
250
|
}
|
|
229
251
|
catch (error) {
|
|
230
252
|
rmSync(next, { recursive: true, force: true });
|
|
@@ -25,12 +25,12 @@ export function selectedCapsules(pins, resolve = capsuleAt, select = toposAt, ho
|
|
|
25
25
|
const descriptor = capsuleDescriptor(topos, offer.scope ?? '');
|
|
26
26
|
const loaded = descriptor === undefined ? load() : undefined;
|
|
27
27
|
const capsule = loaded?.capsule ?? {
|
|
28
|
-
digest: offer.value, selection: `${digest} ${offer.scope}`, ...descriptor, ask: requests => {
|
|
28
|
+
digest: offer.value, selection: `${digest} ${offer.scope}`, ...descriptor, ask: (requests, reader) => {
|
|
29
29
|
if (!requests.length)
|
|
30
30
|
return [];
|
|
31
31
|
const actual = load();
|
|
32
32
|
assertCapsuleDescriptor(descriptor.lines, actual.capsule.lines);
|
|
33
|
-
return actual.capsule.ask(requests);
|
|
33
|
+
return actual.capsule.ask(requests, reader);
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
const projected = loaded?.offer ?? canonical(offer);
|
|
@@ -40,10 +40,10 @@ export function selectedCapsules(pins, resolve = capsuleAt, select = toposAt, ho
|
|
|
40
40
|
const wire = wireAt(fields, Number.MAX_SAFE_INTEGER), algorithm = wire && [...wire.digests][0];
|
|
41
41
|
if (!algorithm)
|
|
42
42
|
throw Error('REFUSE·input instrument has no admitted digest algorithm');
|
|
43
|
-
const selected = { ...capsule, selection: `${digest} ${offer.scope}`, ask: requests => capsule.ask(requests.map(request => {
|
|
43
|
+
const selected = { ...capsule, selection: `${digest} ${offer.scope}`, ask: (requests, reader) => capsule.ask(requests.map(request => {
|
|
44
44
|
const provider = providerInputs(topos, digest, offer.scope ?? '', offer.value, request.region ?? '', bytes => fullDigest(bytes, algorithm));
|
|
45
45
|
return provider === undefined ? request : { ...request, provider };
|
|
46
|
-
})) };
|
|
46
|
+
}), reader) };
|
|
47
47
|
return { capsule: selected, offer: projected };
|
|
48
48
|
}));
|
|
49
49
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Verified foreign histories stay separate from the local authority and local epoch inputs. */
|
|
2
|
+
export declare function foreignWalkLines(root: string): Promise<readonly string[]>;
|
|
3
|
+
/** A declared origin exports its own signed history. Foreign packets are retained with their contexts, never re-signed as local history. */
|
|
4
|
+
export declare function renderWalk(root: string, resolution: number | undefined, peer: readonly string[], key?: string, keyFile?: string, signer?: string): Promise<readonly string[]>;
|
|
5
|
+
/** The SDK proves the foreign prefix; the host admits the pair at one publication point. */
|
|
6
|
+
export declare function landWalk(root: string, lines: readonly string[], key?: string, keyFile?: string, signer?: string): Promise<readonly string[]>;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { walkProjection, walkAt, walkSnapshot, walkHeaders, readWalkHeaders, readForeignWalk, walkAuthority, walkOrigin, walkInventory, walkSelection, isWalkHeader } from '@lapxo/topos/walk';
|
|
2
|
+
import { canonical, parse, fromLine, wireAt, matches, objectHistory, validateObjectContext } from '@lapxo/topos/wire';
|
|
3
|
+
import { objectProvider } from '@lapxo/topos/object-provider';
|
|
4
|
+
import { standingBytes } from '@lapxo/topos/standing';
|
|
5
|
+
import { fieldOf } from "../fold/claims.js";
|
|
6
|
+
import { lockStanding, lockLines } from "../fold/keys.js";
|
|
7
|
+
import { authorityFor, rootSigner } from "../fold/signers.js";
|
|
8
|
+
import { publicKeyOf } from "../fold/signers.js";
|
|
9
|
+
import { signaturesOf } from "../fold/digests.js";
|
|
10
|
+
import { fullDigest } from "./digest.js";
|
|
11
|
+
import { toposAt, contextModuleAt } from "./selected-topos.js";
|
|
12
|
+
import { signerFor } from "./signing.js";
|
|
13
|
+
import { signConsentLot, verifiesFields } from "../land/sign.js";
|
|
14
|
+
import { storeOf } from "../land/ledger.js";
|
|
15
|
+
import { signedOwnerLines, epochOf, localActLines, ownerOf } from "../cli/owner.js";
|
|
16
|
+
import { commitIngress, ingressBundles, ingressCost } from "./ports/ingress.js";
|
|
17
|
+
const refuse = (why) => { throw Error('REFUSE·walk ' + why); };
|
|
18
|
+
const fields = (line) => { const p = parse(line); if (p.kind !== 'fact')
|
|
19
|
+
return refuse('not a wire line'); return p.value.fields; };
|
|
20
|
+
const one = (lines, scope, measure) => {
|
|
21
|
+
const rows = lines.filter(line => fieldOf(line, 'scope') === scope && fieldOf(line, 'measure') === measure && fieldOf(line, 'value') !== 'withdraw');
|
|
22
|
+
if (rows.length !== 1)
|
|
23
|
+
return refuse('missing or conflicting ' + scope + ' ' + measure);
|
|
24
|
+
return fieldOf(rows[0], 'value');
|
|
25
|
+
};
|
|
26
|
+
function policy(root) {
|
|
27
|
+
const store = storeOf(root), standing = lockStanding(store), contract = walkSelection(standing);
|
|
28
|
+
const view = one(standing, 'view/walk', 'id');
|
|
29
|
+
if (view !== 'walk@*')
|
|
30
|
+
refuse('view/walk must declare walk@*');
|
|
31
|
+
const masks = standing.filter(line => fieldOf(line, 'scope') === 'view/walk').flatMap(line => fieldOf(line, 'needs').split('|')).filter(Boolean);
|
|
32
|
+
if (!masks.length)
|
|
33
|
+
refuse('view/walk has no declared history inputs');
|
|
34
|
+
const wire = wireAt(standing.map(fields), Number.MAX_SAFE_INTEGER), algorithm = wire?.digests.values().next().value;
|
|
35
|
+
if (!algorithm)
|
|
36
|
+
refuse('no admitted digest algorithm');
|
|
37
|
+
return { store, standing, contract, masks, digest: (bytes) => fullDigest(bytes, algorithm) };
|
|
38
|
+
}
|
|
39
|
+
function context(root, pin, own = false) {
|
|
40
|
+
const p = policy(root);
|
|
41
|
+
if (!own && !p.standing.some(line => fieldOf(line, 'scope').startsWith('uses/') && fieldOf(line, 'value') === pin))
|
|
42
|
+
refuse('origin context is not selected by digest');
|
|
43
|
+
const topos = toposAt(pin, p.store), authority = walkAuthority(topos.parts, verifiesFields);
|
|
44
|
+
const configuration = topos.parts.map(f => canonical({ ...f, by: 'target', epoch: '0' }));
|
|
45
|
+
return { pin, topos, authority, configuration, origin: walkOrigin(topos.parts), contract: walkSelection(topos.lines) };
|
|
46
|
+
}
|
|
47
|
+
function local(root) {
|
|
48
|
+
const p = policy(root), authority = authorityFor(lockLines(p.store), rootSigner(root), signaturesOf(p.store).admitted);
|
|
49
|
+
const history = signedOwnerLines(p.store).filter(line => p.masks.some(mask => matches(mask, fieldOf(line, 'scope'))));
|
|
50
|
+
if (history.some(line => authority.of(line).kind !== 'admitted'))
|
|
51
|
+
refuse('local history is not authenticated');
|
|
52
|
+
const selected = context(root, one(p.standing, 'walk/context', 'digest'), true);
|
|
53
|
+
return walkSnapshot(history, selected.contract.fields, p.digest, selected.origin, selected.contract.partition);
|
|
54
|
+
}
|
|
55
|
+
const headerInfo = (lines) => {
|
|
56
|
+
const roots = lines.filter(line => isWalkHeader(line) && fieldOf(line, 'scope') === 'walk/root');
|
|
57
|
+
if (roots.length !== 1)
|
|
58
|
+
return refuse('one signed root is required');
|
|
59
|
+
const at = fieldOf(roots[0], 'at');
|
|
60
|
+
if (!at.startsWith('receipt:'))
|
|
61
|
+
return refuse('root has no declared context');
|
|
62
|
+
return { pin: at.slice(8), base: fieldOf(roots[0], 'condition') };
|
|
63
|
+
};
|
|
64
|
+
async function validateHistory(root, selected, history) {
|
|
65
|
+
if (!selected.authority.authenticate(history))
|
|
66
|
+
refuse('origin history is not authenticated');
|
|
67
|
+
for (const line of history) {
|
|
68
|
+
const f = fields(line), decoded = fromLine(line, wireAt(selected.topos.parts, Number(f.epoch)));
|
|
69
|
+
if (decoded.kind !== 'fact')
|
|
70
|
+
refuse('origin wire ' + decoded.why);
|
|
71
|
+
}
|
|
72
|
+
const decoded = objectHistory([...selected.configuration, ...history]);
|
|
73
|
+
if (decoded.kind !== 'fact')
|
|
74
|
+
return refuse('origin history ' + decoded.why);
|
|
75
|
+
if (!decoded.value.objects.length)
|
|
76
|
+
return;
|
|
77
|
+
const pinned = new Set(selected.topos.lines);
|
|
78
|
+
const admit = (f) => Boolean(f.sig) ? selected.authority.of(canonical(f)).kind === 'admitted' : pinned.has(standingBytes([canonical(f)]).trim());
|
|
79
|
+
const provider = await objectProvider(decoded.value.objects, decoded.value.configuration, { admit, emit: () => { }, topos: pin => toposAt(pin, storeOf(root)), module: pin => contextModuleAt(pin, storeOf(root)) });
|
|
80
|
+
const checked = validateObjectContext(decoded.value.objects, provider);
|
|
81
|
+
if (checked.kind !== 'fact')
|
|
82
|
+
refuse('origin context ' + checked.why);
|
|
83
|
+
}
|
|
84
|
+
async function readBundle(root, bundle, earlier = []) {
|
|
85
|
+
const lines = bundle.evidence.split('\n').filter(Boolean), info = headerInfo(lines), p = policy(root);
|
|
86
|
+
const receipts = bundle.receipt.split('\n').filter(Boolean), authority = authorityFor(lockLines(p.store), rootSigner(root), signaturesOf(p.store).admitted);
|
|
87
|
+
if (receipts.length !== 1 || fieldOf(receipts[0], 'scope') !== 'receipts' || fieldOf(receipts[0], 'role') !== 'writes' || fieldOf(receipts[0], 'form') !== 'alphabet' || fieldOf(receipts[0], 'measure') !== 'digest' || fieldOf(receipts[0], 'at') !== 'receipt:' + info.pin || authority.of(receipts[0]).kind !== 'admitted')
|
|
88
|
+
refuse('retained import receipt is not authenticated');
|
|
89
|
+
const selected = context(root, info.pin);
|
|
90
|
+
const got = readForeignWalk(lines, { origin: selected.origin, regionOrigin: selected.origin, context: info.pin, base: info.base, fields: selected.contract.fields, partition: selected.contract.partition, digest: p.digest, previousPrefixes: [], authenticatePrefix: selected.authority.authenticate, authenticateEvidence: selected.authority.authenticate, validateOriginHistory: () => true });
|
|
91
|
+
if (fieldOf(receipts[0], 'value') !== got.identity)
|
|
92
|
+
refuse('retained import receipt does not authenticate this ingress');
|
|
93
|
+
await validateHistory(root, selected, [...new Set([...earlier, ...got.records])]);
|
|
94
|
+
return { ...got, context: info.pin, regions: walkSnapshot(got.records, selected.contract.fields, p.digest, got.origin, selected.contract.partition).regions };
|
|
95
|
+
}
|
|
96
|
+
async function verifiedIngress(root) {
|
|
97
|
+
const result = [], history = new Map();
|
|
98
|
+
const bundles = [...ingressBundles(storeOf(root))].sort((a, b) => epochOf(a.receipt.split('\n')) - epochOf(b.receipt.split('\n')));
|
|
99
|
+
for (const bundle of bundles) {
|
|
100
|
+
const info = headerInfo(bundle.evidence.split('\n').filter(Boolean)), origin = context(root, info.pin).origin;
|
|
101
|
+
if (result.some(prior => prior.origin === origin && prior.context !== info.pin))
|
|
102
|
+
refuse('origin context changed without declared lineage');
|
|
103
|
+
const prior = history.get(origin) ?? new Set();
|
|
104
|
+
const got = await readBundle(root, bundle, [...prior]);
|
|
105
|
+
for (const line of got.records)
|
|
106
|
+
prior.add(line);
|
|
107
|
+
history.set(origin, prior);
|
|
108
|
+
result.push(got);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
async function inventory(root) {
|
|
113
|
+
const p = policy(root), regions = new Map(local(root).regions.map(region => [region.scope, region]));
|
|
114
|
+
for (const got of (await verifiedIngress(root)).sort((a, b) => a.senderEpoch - b.senderEpoch))
|
|
115
|
+
for (const region of got.regions)
|
|
116
|
+
regions.set(region.scope, region);
|
|
117
|
+
return walkInventory([[...regions.values()]], p.digest, context(root, one(p.standing, 'walk/context', 'digest'), true).contract.partition);
|
|
118
|
+
}
|
|
119
|
+
/** Verified foreign histories stay separate from the local authority and local epoch inputs. */
|
|
120
|
+
export async function foreignWalkLines(root) {
|
|
121
|
+
const verified = await verifiedIngress(root);
|
|
122
|
+
return [...new Set(verified.flatMap(got => got.records))];
|
|
123
|
+
}
|
|
124
|
+
/** A declared origin exports its own signed history. Foreign packets are retained with their contexts, never re-signed as local history. */
|
|
125
|
+
export async function renderWalk(root, resolution, peer, key, keyFile, signer) {
|
|
126
|
+
const p = policy(root), pin = one(p.standing, 'walk/context', 'digest'), selected = context(root, pin, true), own = local(root);
|
|
127
|
+
const known = peer.length ? (() => { const info = headerInfo(peer), remote = context(root, info.pin); return readWalkHeaders(peer, info.pin, info.base, remote.authority.authenticate); })() : walkSnapshot([], p.contract.fields, p.digest);
|
|
128
|
+
const projection = walkProjection(selected.contract.projections, resolution);
|
|
129
|
+
const ownInventory = projection === 'history' ? own : await inventory(root);
|
|
130
|
+
const selectedPacket = walkAt(ownInventory, known, resolution, selected.contract.projections), by = key ?? ownerOf(p.store), publicKey = publicKeyOf(root, by);
|
|
131
|
+
if (!publicKey)
|
|
132
|
+
refuse('unknown local signer ' + by);
|
|
133
|
+
const drafted = walkHeaders(selectedPacket, pin, known.root, projection !== 'inventory'), epoch = epochOf(localActLines(root));
|
|
134
|
+
const got = await signConsentLot(drafted, { by, epoch, algorithm: signaturesOf(p.store).era }, publicKey, signaturesOf(p.store).admitted, signerFor(p.standing, by, signer, keyFile));
|
|
135
|
+
if (got.kind !== 'fact')
|
|
136
|
+
return refuse(got.why);
|
|
137
|
+
if (!selected.authority.authenticate(got.lines) || !selected.authority.authenticate(selectedPacket.records))
|
|
138
|
+
refuse('local context does not authenticate exported history');
|
|
139
|
+
return [...got.lines, ...selectedPacket.records, `EXCHANGE ${selectedPacket.touched} touched regions · ${selectedPacket.open} open regions · ${Buffer.byteLength(selectedPacket.records.join('\n') + (selectedPacket.records.length ? '\n' : ''))} payload bytes`];
|
|
140
|
+
}
|
|
141
|
+
/** The SDK proves the foreign prefix; the host admits the pair at one publication point. */
|
|
142
|
+
export async function landWalk(root, lines, key, keyFile, signer) {
|
|
143
|
+
const p = policy(root), info = headerInfo(lines), selected = context(root, info.pin);
|
|
144
|
+
const retained = await verifiedIngress(root);
|
|
145
|
+
if (retained.some(prior => prior.origin === selected.origin && prior.context !== info.pin))
|
|
146
|
+
refuse('origin context changed without declared lineage');
|
|
147
|
+
const previous = (verified) => { const prefixes = new Map(); for (const prior of verified.filter(got => got.origin === selected.origin).sort((a, b) => a.senderEpoch - b.senderEpoch))
|
|
148
|
+
for (const region of prior.regions)
|
|
149
|
+
prefixes.set(region.scope, { ...region, senderEpoch: prior.senderEpoch }); return [...prefixes.values()]; };
|
|
150
|
+
const payload = lines.filter(line => !isWalkHeader(line));
|
|
151
|
+
const proof = (prefixes) => readForeignWalk(lines, { origin: selected.origin, regionOrigin: selected.origin, context: info.pin, base: info.base, fields: selected.contract.fields, partition: selected.contract.partition, digest: p.digest, previousPrefixes: prefixes, authenticatePrefix: selected.authority.authenticate, authenticateEvidence: selected.authority.authenticate, validateOriginHistory: () => true });
|
|
152
|
+
const raw = proof([]), known = retained.find(prior => prior.identity === raw.identity), got = known ? raw : proof(previous(retained));
|
|
153
|
+
const history = (prior) => [...new Set([...prior.filter(item => item.origin === selected.origin).flatMap(item => item.records), ...payload])];
|
|
154
|
+
await validateHistory(root, selected, history(retained));
|
|
155
|
+
if (!known && info.base !== (await inventory(root)).root)
|
|
156
|
+
refuse('stale receiver base');
|
|
157
|
+
if (!payload.length && !readWalkHeaders(lines, info.pin, info.base, selected.authority.authenticate).regions.length)
|
|
158
|
+
return ['ONCE 0 new evidence lines · 0 import receipts · no regions offered', `EXCHANGE 0 payload bytes · ${Buffer.byteLength(lines.join('\n') + '\n')} protocol bytes · 0 new evidence bytes · 0 new receipt bytes`];
|
|
159
|
+
const by = key ?? ownerOf(p.store), pub = publicKeyOf(root, by);
|
|
160
|
+
if (!pub)
|
|
161
|
+
refuse('unknown receiver signer ' + by);
|
|
162
|
+
const result = await commitIngress(p.store, got.identity, async () => {
|
|
163
|
+
if (info.base !== (await inventory(root)).root)
|
|
164
|
+
refuse('receiver changed before ingress');
|
|
165
|
+
const current = await verifiedIngress(root);
|
|
166
|
+
proof(previous(current));
|
|
167
|
+
await validateHistory(root, selected, history(current));
|
|
168
|
+
const next = epochOf(localActLines(root)) + 1;
|
|
169
|
+
const signed = await signConsentLot([got.importReceiptProposal], { by, epoch: next, algorithm: signaturesOf(p.store).era }, pub, signaturesOf(p.store).admitted, signerFor(p.standing, by, signer, keyFile));
|
|
170
|
+
if (signed.kind !== 'fact')
|
|
171
|
+
return refuse(signed.why);
|
|
172
|
+
const authority = authorityFor(lockLines(p.store), rootSigner(root), signaturesOf(p.store).admitted);
|
|
173
|
+
if (authority.of(signed.lines[0]).kind !== 'admitted')
|
|
174
|
+
refuse('receiver signer does not cover the import receipt');
|
|
175
|
+
const grammar = fromLine(signed.lines[0], wireAt(p.standing.map(fields), next));
|
|
176
|
+
if (grammar.kind !== 'fact')
|
|
177
|
+
refuse('receiver receipt wire ' + grammar.why);
|
|
178
|
+
return { evidence: lines.join('\n') + '\n', receipt: signed.lines.join('\n') + '\n' };
|
|
179
|
+
}, bundle => {
|
|
180
|
+
try {
|
|
181
|
+
const retainedLines = bundle.evidence.split('\n').filter(Boolean), retainedInfo = headerInfo(retainedLines);
|
|
182
|
+
if (retainedInfo.pin !== info.pin)
|
|
183
|
+
return false;
|
|
184
|
+
const retainedProof = readForeignWalk(retainedLines, { origin: selected.origin, regionOrigin: selected.origin, context: info.pin, base: retainedInfo.base, fields: selected.contract.fields, partition: selected.contract.partition, digest: p.digest, previousPrefixes: [], authenticatePrefix: selected.authority.authenticate, authenticateEvidence: selected.authority.authenticate, validateOriginHistory: () => true });
|
|
185
|
+
const receipts = bundle.receipt.split('\n').filter(Boolean), receipt = receipts[0] ?? '';
|
|
186
|
+
return receipts.length === 1 && retainedProof.identity === got.identity && fieldOf(receipt, 'scope') === 'receipts' && fieldOf(receipt, 'value') === got.identity && fieldOf(receipt, 'at') === 'receipt:' + info.pin && authorityFor(lockLines(p.store), rootSigner(root), signaturesOf(p.store).admitted).of(receipt).kind === 'admitted';
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return [`${result.kind === 'once' ? 'ONCE' : 'INGRESS'} ${result.kind === 'once' ? 0 : got.records.length} new evidence lines · 1 ${result.kind === 'once' ? 'retained' : 'signed local'} import receipt`, result.receipt.trim(), ingressCost(result, Buffer.byteLength(payload.join('\n') + (payload.length ? '\n' : '')), Buffer.byteLength(lines.filter(isWalkHeader).join('\n') + '\n'))];
|
|
193
|
+
}
|
package/dist/land/ledger.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare function writerFile(store: string, writer: string): string;
|
|
|
10
10
|
export declare function ledgerLines(store: string, writer: string): readonly string[];
|
|
11
11
|
export declare function shardFile(store: string, writer: string, region: string): string;
|
|
12
12
|
export declare const shardLines: (store: string, writer: string, region: string) => readonly string[];
|
|
13
|
+
export declare function ledgerWriters(store: string): readonly string[];
|
|
13
14
|
export declare function landShard(store: string, writer: string, region: string, lines: readonly string[]): number;
|
|
14
15
|
export declare function signedLines(store: string, writer: string): string[];
|
|
15
16
|
export declare function blobAt(store: string, digest: string): string;
|
package/dist/land/ledger.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { actLines, committedLocalActs } from "../host/ports/local-act.js";
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
1
3
|
import { requireEffects } from "../host/read-only.js";
|
|
2
4
|
import { appendFileSync, closeSync, dirname, existsSync, join, linkSync, mkdirSync, openSync, readFileSync, readSync, renameSync, rmSync, statSync, writeFileSync } from "../host/io.js";
|
|
3
5
|
import { storeRoot } from "../host/ports/store.js";
|
|
@@ -21,12 +23,29 @@ export function writerFile(store, writer) {
|
|
|
21
23
|
}
|
|
22
24
|
const held = new Map();
|
|
23
25
|
export function ledgerLines(store, writer) {
|
|
24
|
-
return linesAt(writerFile(store, writer));
|
|
26
|
+
return journalLines(store, writer, '.', linesAt(writerFile(store, writer)));
|
|
25
27
|
}
|
|
26
28
|
export function shardFile(store, writer, region) {
|
|
27
29
|
return storeAt(store, 'ledger', writer.replace(/[^a-zA-Z0-9_-]/g, ''), `${region.replace(/\/+$/, '') || '@'}${EXTENSION}`);
|
|
28
30
|
}
|
|
29
|
-
export const shardLines = (store, writer, region) => linesAt(shardFile(store, writer, region));
|
|
31
|
+
export const shardLines = (store, writer, region) => journalLines(store, writer, region, linesAt(shardFile(store, writer, region)));
|
|
32
|
+
const journalMemo = new Map();
|
|
33
|
+
function journalLines(store, writer, place, legacy) {
|
|
34
|
+
const fresh = committedLocalActs(store).flatMap(act => act.bundle.placements?.filter(p => p.place === place).flatMap(p => actLines(p.records).filter(line => fieldOf(line, 'by') === writer)) ?? []);
|
|
35
|
+
if (!fresh.length)
|
|
36
|
+
return legacy;
|
|
37
|
+
const lines = [...new Set([...legacy, ...fresh])], key = store + '\0' + writer + '\0' + place, last = journalMemo.get(key);
|
|
38
|
+
if (last?.length === lines.length && last.every((line, i) => line === lines[i]))
|
|
39
|
+
return last;
|
|
40
|
+
journalMemo.set(key, lines);
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
43
|
+
export function ledgerWriters(store) {
|
|
44
|
+
const at = join(store, 'ledger');
|
|
45
|
+
const legacy = existsSync(at) ? readdirSync(at).filter(name => name.endsWith(EXTENSION)).map(name => name.slice(0, -EXTENSION.length)) : [];
|
|
46
|
+
const committed = committedLocalActs(store).flatMap(act => act.bundle.placements?.filter(p => p.place === '.').flatMap(p => actLines(p.records).map(line => fieldOf(line, 'by'))) ?? []);
|
|
47
|
+
return [...new Set([...legacy, ...committed])];
|
|
48
|
+
}
|
|
30
49
|
export function landShard(store, writer, region, lines) {
|
|
31
50
|
return appendNew(shardFile(store, writer, region), lines);
|
|
32
51
|
}
|
package/dist/land/owned.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export type Owned = {
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function ownedOf(root: string, signed: readonly string[], admits: (line: string) => boolean, coverage: (by: string) => readonly string[], named?: readonly string[], placedAt?: (line: string) => readonly string[] | undefined): Owned;
|
|
22
22
|
/** A place's own lock gains the lines a batch landed there, appended whole: a reader never meets half of it, and every line already there keeps its bytes as they were. */
|
|
23
|
-
export declare function landOwned(root: string, place: string, lines: readonly string[]): void;
|
|
23
|
+
export declare function landOwned(root: string, place: string, lines: readonly string[], committed?: boolean): void;
|
package/dist/land/owned.js
CHANGED
|
@@ -51,14 +51,15 @@ export function ownedOf(root, signed, admits, coverage, named = [], placedAt = (
|
|
|
51
51
|
return { ledger, own, nowhere: [...placed, ...rest.filter((line) => !lands(line))], forked };
|
|
52
52
|
}
|
|
53
53
|
/** A place's own lock gains the lines a batch landed there, appended whole: a reader never meets half of it, and every line already there keeps its bytes as they were. */
|
|
54
|
-
export function landOwned(root, place, lines) {
|
|
54
|
+
export function landOwned(root, place, lines, committed = false) {
|
|
55
55
|
if (!lines.length)
|
|
56
56
|
return tellOwn(place, []);
|
|
57
57
|
const at = join(root, place, LOCK);
|
|
58
58
|
const text = observeText(at) ?? '';
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
if (!committed)
|
|
60
|
+
for (const by of new Set(lines.map(line => fieldOf(line, 'by')))) {
|
|
61
|
+
landShard(storeOf(root), by, `${place}/${LOCK}`, lines.filter(line => fieldOf(line, 'by') === by));
|
|
62
|
+
}
|
|
62
63
|
writeFileSync(`${at}.${process.pid}.landing`, `${text.replace(/\n*$/, '\n')}${lines.join('\n')}\n`);
|
|
63
64
|
renameSync(`${at}.${process.pid}.landing`, at);
|
|
64
65
|
tellOwn(place, []);
|
package/dist/observe/files.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, lstatSync, readFileSync, readdirSync, readlinkSync } from 'node:fs';
|
|
1
|
+
import { existsSync, lstatSync, statSync, readFileSync, readdirSync, readlinkSync } from 'node:fs';
|
|
2
2
|
import { join, relative, sep } from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* The one place the instrument reads the world: a coordinate's bytes, the entries of a directory, the coordinates under a place,
|
|
@@ -7,7 +7,17 @@ import { join, relative, sep } from 'node:path';
|
|
|
7
7
|
const seen = new Map();
|
|
8
8
|
/** A coordinate read once is kept by its stamp — its inode, size and time — and read again only when the stamp moves. */
|
|
9
9
|
function bytesAt(at) {
|
|
10
|
-
|
|
10
|
+
// Reading follows a link, so its cache stamp must describe the same target.
|
|
11
|
+
// Directory links have no file bytes; traversal keeps its separate link policy.
|
|
12
|
+
let st = stampable(at);
|
|
13
|
+
if (st?.isSymbolicLink()) {
|
|
14
|
+
try {
|
|
15
|
+
st = statSync(at);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
11
21
|
if (st === undefined || st.isDirectory() || !existsSync(at))
|
|
12
22
|
return undefined;
|
|
13
23
|
const stamp = `${st.ino}-${st.size}-${st.mtimeMs}`;
|
package/dist/observe/regions.js
CHANGED
|
@@ -5,13 +5,11 @@ import { createHash } from 'node:crypto';
|
|
|
5
5
|
import { basename, dirname, join, relative } from 'node:path';
|
|
6
6
|
import { CAPSULE, LOCK, canonical, matches, PROTOCOL } from '@lapxo/topos/wire';
|
|
7
7
|
import { handed, readsOf } from '@lapxo/topos/capsule';
|
|
8
|
-
import { STATES } from '@lapxo/obligations';
|
|
9
8
|
import { foldCeilings } from "../fold/ceilings.js";
|
|
10
9
|
import { fieldOf, wireLinesOf as linesOf } from "../fold/claims.js";
|
|
11
10
|
import { isCeiling } from "../fold/configures.js";
|
|
12
11
|
import { keyFor } from "../fold/keys.js";
|
|
13
12
|
import { casesHeld, placedReadings } from "../fold/observed.js";
|
|
14
|
-
import { verdictsFor } from "../fold/paid.js";
|
|
15
13
|
import { capsuleAt as isWorld, placesIn, spoken } from "../fold/places.js";
|
|
16
14
|
import { rolesOf } from "../fold/roles.js";
|
|
17
15
|
import { ownLockOf } from "../fold/signed.js";
|
|
@@ -71,9 +69,8 @@ function askedOf(fold) {
|
|
|
71
69
|
return fold.under ? [...new Set(capsuleOf(fold).filter((line) => fieldOf(line, 'scope').startsWith('region/') && fieldOf(line, 'role') === 'render').map((line) => fieldOf(line, 'scope').slice('region/'.length)))]
|
|
72
70
|
.map((name) => ((n) => fact(`asked/${name}`, `${n}..${n}`, 'interval', 'count'))(pinnersOf(fold).map(at).filter((names) => names.has(name)).length)) : [];
|
|
73
71
|
}
|
|
74
|
-
/**
|
|
72
|
+
/** File-fold readings and their ceilings; this path computes no object encounters. */
|
|
75
73
|
function verdictOf(fold) {
|
|
76
|
-
const states = Object.values(verdictsFor(fold));
|
|
77
74
|
const named = (line) => ((cut) => [line.slice(0, cut < 0 ? line.length : cut).trim(), line.slice(cut < 0 ? line.length : cut + 3).trim() || line])(line.indexOf(' · '));
|
|
78
75
|
const here = (fold.under ?? '').replace(/\/$/, '');
|
|
79
76
|
const others = new Set(here ? placesIn(fold.root).filter((place) => place !== here) : []);
|
|
@@ -86,7 +83,8 @@ function verdictOf(fold) {
|
|
|
86
83
|
return !others.has(head);
|
|
87
84
|
};
|
|
88
85
|
return [['claims', fold.claims], ['forks', fold.forks.length + (fold.second.verdict === 'forks' ? 1 : 0)], ['refused', fold.refused.length], ['vacuous', fold.vacuous], ['history', fold.history.length],
|
|
89
|
-
['met', fold.ceilings.read], ['short', fold.ceilings.short.length], ['over', fold.ceilings.over.length]
|
|
86
|
+
['met', fold.ceilings.read], ['short', fold.ceilings.short.length], ['over', fold.ceilings.over.length]].map(([name, n]) => fact(`verdict/${name}`, `${n}..${n}`, 'interval', 'count'))
|
|
87
|
+
.concat([fact('verdict/computation', 'readings'), fact('verdict/encounters', 'uncomputed')])
|
|
90
88
|
.concat(fold.ceilings.short.filter((one) => mine(named(one)[0])).map((one) => (([scope, reading]) => fact(`unpaid/${scope}`, reading))(named(one))))
|
|
91
89
|
.concat(fold.ceilings.over.filter((one) => mine(named(one)[0])).map((one) => (([scope, reading]) => fact(`outside/${scope}`, reading))(named(one))))
|
|
92
90
|
.concat(fold.ceilings.unread.filter(mine).map((scope) => fact(`unpaid/${scope}`, 'unread')))
|
package/dist/render/field.d.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
1
|
import type { PlaceFold } from '../cli/place.ts';
|
|
2
|
-
/**
|
|
3
|
-
* The field of a place at resolution N: every bound placed at the place or at none, in the state its poles give, and
|
|
4
|
-
* every coordinate cut to its first N steps in the worst state it holds. Interval poles with no value in common
|
|
5
|
-
* conflict, as two signers on one key do; an alphabet that permits none is a ceiling like any other. A value read
|
|
6
|
-
* outside the poles is forbidden; a bound nothing read, a reader that refused and a demand unpaid are required; the
|
|
7
|
-
* rest is free. It is read off the region summaries and the landed readings, and no reader runs.
|
|
8
|
-
*/
|
|
2
|
+
/** Host file-region readings, grouped by coordinate prefix. These statuses do not compute object encounters. */
|
|
9
3
|
export declare function field(fold: PlaceFold, at: number): readonly string[];
|
package/dist/render/field.js
CHANGED
|
@@ -3,15 +3,9 @@ import { placesIn } from "../fold/places.js";
|
|
|
3
3
|
import { placedAt } from "../fold/reach.js";
|
|
4
4
|
import { polesOf, rankOf, unpackRegions } from "../fold/region.js";
|
|
5
5
|
import { INTERVALS } from '@lapxo/topos/forms';
|
|
6
|
-
const WORST = ['
|
|
6
|
+
const WORST = ['disagreement', 'outside', 'unanswered', 'met'];
|
|
7
7
|
const empty = (ceiling) => ceiling !== undefined && 'lo' in ceiling && !INTERVALS.inhabited(ceiling);
|
|
8
|
-
/**
|
|
9
|
-
* The field of a place at resolution N: every bound placed at the place or at none, in the state its poles give, and
|
|
10
|
-
* every coordinate cut to its first N steps in the worst state it holds. Interval poles with no value in common
|
|
11
|
-
* conflict, as two signers on one key do; an alphabet that permits none is a ceiling like any other. A value read
|
|
12
|
-
* outside the poles is forbidden; a bound nothing read, a reader that refused and a demand unpaid are required; the
|
|
13
|
-
* rest is free. It is read off the region summaries and the landed readings, and no reader runs.
|
|
14
|
-
*/
|
|
8
|
+
/** Host file-region readings, grouped by coordinate prefix. These statuses do not compute object encounters. */
|
|
15
9
|
export function field(fold, at) {
|
|
16
10
|
if (fold.regions === undefined)
|
|
17
11
|
return ['GREY field · this fold carries no region summaries; it is read again at the next fold'];
|
|
@@ -25,10 +19,10 @@ export function field(fold, at) {
|
|
|
25
19
|
...regions.bounds.filter((bound) => here(bound.line)).map((bound) => {
|
|
26
20
|
const ceiling = (poles.get(bound.region) ?? poles.set(bound.region, polesOf(regions, bound.region)).get(bound.region)).get(`${bound.question} ${bound.measure}`)?.ceiling;
|
|
27
21
|
const got = rankOf(regions, bound, own);
|
|
28
|
-
return [fieldOf(bound.line, 'scope'), empty(ceiling) ? '
|
|
22
|
+
return [fieldOf(bound.line, 'scope'), empty(ceiling) ? 'disagreement' : got.bars || got.sums ? 'outside' : got.met ? 'met' : 'unanswered'];
|
|
29
23
|
}),
|
|
30
|
-
...fold.missing.filter(here).map((line) => [fieldOf(line, 'scope'), '
|
|
31
|
-
...fold.forks.flatMap((fork) => fork.lines.slice(0, 1)).filter(here).map((line) => [fieldOf(line, 'scope'), '
|
|
24
|
+
...fold.missing.filter(here).map((line) => [fieldOf(line, 'scope'), 'unanswered']),
|
|
25
|
+
...fold.forks.flatMap((fork) => fork.lines.slice(0, 1)).filter(here).map((line) => [fieldOf(line, 'scope'), 'disagreement']),
|
|
32
26
|
];
|
|
33
27
|
const cells = new Map();
|
|
34
28
|
for (const [scope, state] of states) {
|
|
@@ -39,7 +33,7 @@ export function field(fold, at) {
|
|
|
39
33
|
const ordered = [...cells].sort((a, b) => WORST.indexOf(a[1].state) - WORST.indexOf(b[1].state) || a[0].localeCompare(b[0]));
|
|
40
34
|
return [
|
|
41
35
|
'## field', '', '```',
|
|
42
|
-
`FIELD @${at} · ${cells.size}
|
|
36
|
+
`FIELD @${at} · ${cells.size} groups · ${states.length} bounds · readings · no encounters computed · ${WORST.map((state) => `${state} ${ordered.filter(([, one]) => one.state === state).length}`).join(' · ')}`,
|
|
43
37
|
...ordered.map(([cell, one]) => `FIELD ${cell} · ${one.state} · ${one.bounds} bounds`),
|
|
44
38
|
'```',
|
|
45
39
|
];
|
package/dist/render/names.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** The regions bound renders itself, by name: a view that asks any other is answered by a capsule that declares it, or refused whole. */
|
|
2
|
-
export declare const REGION_NAMES: readonly ["receipts", "help", "lines", "red", "forks", "programs", "why", "sign", "take", "history", "field"];
|
|
2
|
+
export declare const REGION_NAMES: readonly ["receipts", "help", "lines", "red", "forks", "programs", "why", "sign", "take", "history", "field", "evidence"];
|
|
3
3
|
export type RegionName = (typeof REGION_NAMES)[number];
|
package/dist/render/names.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** The regions bound renders itself, by name: a view that asks any other is answered by a capsule that declares it, or refused whole. */
|
|
2
2
|
export const REGION_NAMES = [
|
|
3
|
-
'receipts', 'help', 'lines', 'red', 'forks', 'programs', 'why', 'sign', 'take', 'history', 'field',
|
|
3
|
+
'receipts', 'help', 'lines', 'red', 'forks', 'programs', 'why', 'sign', 'take', 'history', 'field', 'evidence',
|
|
4
4
|
];
|
package/dist/render/view.js
CHANGED
|
@@ -7,26 +7,29 @@ import { capsuleAnswers, capsuleRegions, capsuleViews } from "./capsule.js";
|
|
|
7
7
|
import { declaredBy } from "../observe/regions.js";
|
|
8
8
|
import { readersBehind } from "../fold/observed.js";
|
|
9
9
|
import { LOCK, canonical, matches } from '@lapxo/topos/wire';
|
|
10
|
-
import { STATES } from '@lapxo/obligations';
|
|
11
10
|
import { fieldOf, foldClaims, selfName } from "../fold/claims.js";
|
|
12
11
|
import { keepRender, keptRender } from "../fold/kept.js";
|
|
13
|
-
import { bytesDigest } from "../fold/digests.js";
|
|
14
|
-
import { writerFor } from "../fold/signers.js";
|
|
12
|
+
import { bytesDigest, signaturesOf } from "../fold/digests.js";
|
|
13
|
+
import { authorityFor, rootSigner, writerFor } from "../fold/signers.js";
|
|
15
14
|
import { lockLines } from "../fold/keys.js";
|
|
15
|
+
import { appliedIn } from "../fold/signed.js";
|
|
16
|
+
import { ledgerLines } from "../land/ledger.js";
|
|
16
17
|
import { horizonOf, moved, shapeLines } from "./shape.js";
|
|
17
18
|
import { rankLines } from "../fold/rank.js";
|
|
18
19
|
import { differing, regionsOf } from "../fold/region.js";
|
|
19
|
-
import { verdictsFor } from "../fold/paid.js";
|
|
20
20
|
import { field } from "./field.js";
|
|
21
21
|
import { helpLines } from "../cli/names.js";
|
|
22
|
+
/** Authenticated delivery history is evidence, not the place's normative standing. */
|
|
23
|
+
const evidence = (fold) => {
|
|
24
|
+
const authority = authorityFor(lockLines(fold.store), rootSigner(fold.root), signaturesOf(fold.store).admitted);
|
|
25
|
+
const place = (fold.under ?? '').replace(/\/$/, '');
|
|
26
|
+
const deliveries = place ? appliedIn(fold.root, place)
|
|
27
|
+
: [...new Set(authority.admitted.flatMap(key => ledgerLines(fold.store, key.id)))];
|
|
28
|
+
return deliveries
|
|
29
|
+
.filter(line => authority.of(line).kind === 'admitted');
|
|
30
|
+
};
|
|
22
31
|
const block = (name, lines) => (lines.length ? [`## ${name}`, '', '```', ...lines, '```'] : []);
|
|
23
32
|
const seconds = (ms) => `${(ms / 1000).toFixed(1)} s`;
|
|
24
|
-
const statesOf = (fold) => {
|
|
25
|
-
const held = new Map();
|
|
26
|
-
for (const one of Object.values(verdictsFor(fold)))
|
|
27
|
-
held.set(one, (held.get(one) ?? 0) + 1);
|
|
28
|
-
return STATES.flatMap((one) => (held.get(one) ? [`${one} ${held.get(one)}`] : []));
|
|
29
|
-
};
|
|
30
33
|
const programs = (fold) => {
|
|
31
34
|
const median = fold.costs.length ? [...fold.costs].sort((a, b) => a - b)[Math.floor(fold.costs.length / 2)] ?? 0 : 0;
|
|
32
35
|
const turned = moved(fold);
|
|
@@ -34,12 +37,13 @@ const programs = (fold) => {
|
|
|
34
37
|
const refusedByReader = fold.observed.filter((line) => fieldOf(line, 'scope') === 'refuse/reader').length;
|
|
35
38
|
const c = fold.ceilings;
|
|
36
39
|
return block('programs', [
|
|
37
|
-
`PROGRAMS${fold.under ? ` ${fold.under}` : ''} ${[`${fold.claims} claims`,
|
|
40
|
+
`PROGRAMS${fold.under ? ` ${fold.under}` : ''} ${[`${fold.claims} claims`, `${fold.history.length} history`].join(' · ')}`
|
|
38
41
|
+ ` · ${fold.forks.length + (fold.second.verdict === 'forks' ? 1 : 0)} forks · ${fold.grey.length} grey · ${fold.refused.length} refused · ${fold.vacuous} vacuous`
|
|
39
42
|
+ ` · ${secondName()} ${fold.second.verdict}${fold.second.verdict === 'agrees' && !fold.second.external ? ', never disagreed' : ''}`
|
|
40
43
|
+ ` · store ${fold.observed.length} observed${refusedByReader ? ` · ${refusedByReader} refuse·reader` : ''}`
|
|
41
44
|
+ (fold.under ? ` · ceilings ${c.read} met here · ${c.short.length} short · ${c.over.length} over`
|
|
42
45
|
: ` · ceilings ${c.read}/${c.total} read · ${c.short.length} short · ${c.over.length} over · ${c.unread.length} unread${c.vacuous.length ? ` · ${c.vacuous.length} vacuous unread` : ''}`)
|
|
46
|
+
+ ' · readings · no encounters computed'
|
|
43
47
|
+ ` · ${fold.effects} · take ${fold.costs.length ? seconds(fold.costs[fold.costs.length - 1] ?? 0) : 'none'} last`
|
|
44
48
|
+ (fold.costs.length > 1 ? ` · ${seconds(median)} median of ${fold.costs.length}` : ''),
|
|
45
49
|
...shapeLines(fold),
|
|
@@ -134,6 +138,7 @@ const REGIONS = {
|
|
|
134
138
|
receipts: (fold) => receiptRegion(fold, 1),
|
|
135
139
|
help: () => helpLines(),
|
|
136
140
|
lines: (fold) => fold.standing,
|
|
141
|
+
evidence,
|
|
137
142
|
red, forks, programs, why, sign, take, history,
|
|
138
143
|
field: (fold) => field(fold, 3),
|
|
139
144
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Exact release artifact
|
|
2
|
+
|
|
3
|
+
Packing is a host operation. An archive digest names those archive bytes; it is not the identity of a Topos standing. The release pipeline makes one archive and transfers that same file to its destinations. Repeating a pack operation is not a proof of identical bytes.
|
|
4
|
+
|
|
5
|
+
The archive conformance vector declares the host package command, digest algorithm, execution deadline, response bound and its sample packages. It packs each sample once, hashes the resulting bytes, verifies both destination copies, installs offline without package scripts and compares every listed installed member with the input bytes. It executes the installed entry and checks its declared output. Changed archive bytes refuse by digest before they can be treated as the named artifact. The negative control fails when that verification is removed.
|
|
6
|
+
|
|
7
|
+
The samples use independent packages with different contents. Their outputs and member digests are captured by the executable vector; their file presence alone is not evidence. The selected publication reader measures this quartet through the existing process contract, and native receipts supply its member status to the plan.
|
|
8
|
+
|
|
9
|
+
This is conformance of the packaging recipe. Acceptance of a particular Bound candidate additionally requires the exact archive audit, clean installation and product CLI captures for that archive. A previous archive's captures cannot certify changed implementation bytes. The final candidate must be rebuilt after admitted metadata changes, with every changed member accounted for. The product witness uses the installed reader directly, without a checkout ledger or source conditions.
|
|
10
|
+
|
|
11
|
+
Rendering and installation are different boundaries. README, the manifest and the standard MIT text remain native renders. Publication to a registry or forge is a later owner action; this vector does not publish, authenticate to an account, create a tag or infer an external release from a local file.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# fold
|
|
2
|
+
|
|
3
|
+
`bound fold` reads the admitted place. Configuration output names readings and says no encounters computed; its counts are not independent-origin states. A declared cells view projects admitted typed history into Obligations. A selected world defines other views; its contract decides when empty output is valid. A refusal or nonzero status wins over any parseable diagnostic stdout. `--check` verifies receipts without inventing a missing reading. The published evidence vector folds fresh places, preserves admitted key attribution, reads a native receipt and checks exact withdrawals. The typed-object quartet covers object states separately.
|
|
4
|
+
|
|
5
|
+
# land
|
|
6
|
+
|
|
7
|
+
`bound land <lot>` admits authenticated lines through the same boundary used by signing and previews. Authority, coverage, historical wire and typed context remain distinct checks. Unknown wire and tampered signatures refuse. Exact redelivery returns ONCE; it does not append another origin or grant new authority. Withdrawals remain exact under their declared contract. A refusal does not append a partial act. The published evidence vector lands fresh places, refuses unauthorized writes and tampering, redelivers signed bytes and exercises exact withdrawals. The signing-port vector additionally checks interrupted or invalid command responses do not land a partial lot.
|
|
8
|
+
|
|
9
|
+
# sign
|
|
10
|
+
|
|
11
|
+
`bound sign <lot>` returns canonical signed lines after validating the whole lot. The admitted key selects a declared signer; `--signer` selects the same port explicitly. File and command mechanisms use one port. Topos signing-lot/1 frames canonical bytes in lot order, one invocation per lot, and returns signatures and public-key IDs. Verification checks the admitted public key, independently of the mechanism. A command is invoked without a shell and under declared bounds. No secret output is disclosed in refusals. Missing, invalid, partial, timed-out or failed responses refuse; `land` cannot commit their partial output. The sample compares file and command signatures and landed lines, checks invocation count and ledger hashes, and refuses an unknown wire version. The sample uses ephemeral keys outside the place; no owner key is included.
|
|
12
|
+
|
|
13
|
+
# Committed act results
|
|
14
|
+
|
|
15
|
+
The opt-in `local-act@1` contract is selected by `wire/act-result`, `wire/act-result-scope` and `wire/act-result-context`. An admitted `view/act measure=id value=act` makes `bound fold --as act <identity>` available. Missing or conflicting declarations refuse; an absent profile preserves the historical interface. Topos defines the canonical record and result contract.
|
|
16
|
+
|
|
17
|
+
Admission publishes the original signed records, their logical placements and one native result receipt atomically. A failure before commit writes no partial act. The result names the committed digest and local epoch; replay returns the original result without another epoch or origin. A post-commit error is not a rollback. Read the committed result before deciding whether to retry.
|
|
18
|
+
|
|
19
|
+
The query verifies the record signatures and coverage against the authority snapshot at admission, and the native receipt against its retained provenance. Later key retirement or profile replacement cannot authorize a new act but does not erase a verifiable historical result. Admission is not conformance: the output says `admitted · conformance not asserted`. Missing or corrupt results refuse by name. Host paths and delivery counts are not identity inputs.
|