@lapxo/bound 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/TARGET.bound +19 -18
- package/dist/cli/accept.d.ts +5 -0
- package/dist/cli/accept.js +57 -8
- package/dist/cli/act-result.d.ts +3 -0
- package/dist/cli/act-result.js +48 -0
- package/dist/cli/owner.js +3 -2
- package/dist/cli/pass.js +6 -4
- package/dist/cli/place.d.ts +1 -1
- package/dist/cli/place.js +1 -1
- package/dist/cli/release-result.d.ts +5 -0
- package/dist/cli/release-result.js +80 -0
- package/dist/cli/standing-result.d.ts +5 -0
- package/dist/cli/standing-result.js +39 -0
- package/dist/cli/verb.js +33 -2
- package/dist/fold/admission-history.d.ts +6 -0
- package/dist/fold/admission-history.js +22 -0
- package/dist/fold/closed.js +9 -3
- package/dist/fold/digests.js +13 -9
- package/dist/fold/kept.d.ts +1 -1
- package/dist/fold/kept.js +21 -6
- package/dist/fold/keys.js +16 -9
- package/dist/fold/observed.js +8 -7
- package/dist/fold/places.d.ts +5 -1
- package/dist/fold/places.js +19 -3
- package/dist/fold/readers.js +2 -1
- package/dist/fold/release-policy.js +0 -2
- package/dist/fold/signed.d.ts +5 -0
- package/dist/fold/signed.js +19 -15
- package/dist/fold/zoom.js +5 -4
- package/dist/host/capsule.d.ts +2 -1
- package/dist/host/capsule.js +8 -6
- package/dist/host/cell-receipts.js +7 -3
- package/dist/host/io.d.ts +1 -1
- package/dist/host/io.js +1 -1
- package/dist/host/ports/ingress.js +4 -28
- 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 +3 -0
- package/dist/host/reading-closures.js +5 -4
- package/dist/host/selected-capsules.js +4 -4
- package/dist/host/selected-topos.d.ts +4 -1
- package/dist/host/selected-topos.js +16 -1
- 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/land/standing-pin.d.ts +3 -0
- package/dist/land/standing-pin.js +49 -0
- package/dist/observe/files.js +12 -2
- package/docs/contracts/cli.md +16 -0
- package/docs/contracts/evidence-view.md +4 -0
- package/package.json +2 -2
- package/receipts.bound +593 -314
package/dist/fold/kept.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { actLines, committedLocalActs } from "../host/ports/local-act.js";
|
|
1
2
|
import { readOnly } from "../host/read-only.js";
|
|
2
3
|
import { createHash, dirname, existsSync, join, mkdirSync, readdirSync, renameSync, writeFileSync } from "../host/io.js";
|
|
3
4
|
import { bytesDigest, coordinateDigest } from "./digests.js";
|
|
4
5
|
import { viewsOf } from "./views.js";
|
|
5
|
-
import { landMemo, ledgerLines, memoAt, shardFile, writerFile } from "../land/ledger.js";
|
|
6
|
+
import { landMemo, ledgerLines, ledgerWriters, memoAt, shardFile, writerFile } from "../land/ledger.js";
|
|
6
7
|
import { coordinatesUnder, observeFile, observeText } from "../observe/files.js";
|
|
7
8
|
import { EXTENSION, fieldOf } from "./claims.js";
|
|
8
9
|
import { keyFor, keyOf, lockSigners } from "./keys.js";
|
|
@@ -36,7 +37,7 @@ export function foldPoints(store, standing, under, keeper, instrument = '', obse
|
|
|
36
37
|
`take ${digestOf(ledgerLines(store, 'take').filter((line) => reads.scopes.has(fieldOf(line, 'scope').replace(/^take\//, ''))))}`,
|
|
37
38
|
`tree ${digestOf(ledgerLines(store, tree ?? '').filter((line) => (sourceOf(line) ?? '').startsWith(reads.place)))}`,
|
|
38
39
|
];
|
|
39
|
-
const heads = (
|
|
40
|
+
const heads = ledgerWriters(store)
|
|
40
41
|
.filter((writer) => writer !== keeper && writer !== 'gate' && !writer.startsWith('reader') && !(reads && (signers.has(writer) || writer === 'judge' || writer === 'take' || writer === tree)))
|
|
41
42
|
.map((writer) => `${writer} ${writer === liveWriter(store, standing) ? lastWord(store, writer) : bytesDigest(store, text(ledgerLines(store, writer).join('\n')))}`);
|
|
42
43
|
const written = [...new Set([...viewsOf(standing).values()].flatMap((view) => ('regions' in view && view.shape ? [view.shape] : [])))].sort()
|
|
@@ -100,10 +101,20 @@ function readingContext(store) {
|
|
|
100
101
|
const speaker = keyOf(store, 'read');
|
|
101
102
|
const files = speaker === undefined ? [] : [writerFile(store, speaker), ...coordinatesUnder(dirname(shardFile(store, speaker, '')), store)
|
|
102
103
|
.filter(file => file.endsWith(EXTENSION)).map(file => join(store, file))];
|
|
103
|
-
|
|
104
|
-
.
|
|
104
|
+
const segments = speaker === undefined ? [] : committedLocalActs(store).flatMap(act => act.bundle.placements?.flatMap(placement => {
|
|
105
|
+
const records = actLines(placement.records).filter(line => fieldOf(line, 'by') === speaker);
|
|
106
|
+
return records.length ? [placement.place + '\n' + records.join('\n')] : [];
|
|
107
|
+
}) ?? []);
|
|
108
|
+
return bytesDigest(store, text([...files.filter(file => existsSync(file)).sort()
|
|
109
|
+
.map(file => bytesDigest(store, observeFile(file) ?? new Uint8Array())), ...segments].join('\n')));
|
|
105
110
|
}
|
|
106
|
-
|
|
111
|
+
/** A new judgment changes the result even when the files and reader history do not. */
|
|
112
|
+
function judgmentContext(store, demands) {
|
|
113
|
+
const scopes = new Set(demands.map(line => fieldOf(line, 'scope')));
|
|
114
|
+
return bytesDigest(store, text(ledgerLines(store, 'judge')
|
|
115
|
+
.filter(line => scopes.has(fieldOf(line, 'scope'))).join('\n')));
|
|
116
|
+
}
|
|
117
|
+
export function keptPlace(store, under, currentInstrument) {
|
|
107
118
|
const key = observeText(memoAt(store, lastOf([`place ${under ?? '.'}`])))?.trim();
|
|
108
119
|
if (key === undefined || key === '')
|
|
109
120
|
return undefined;
|
|
@@ -114,6 +125,10 @@ export function keptPlace(store, under) {
|
|
|
114
125
|
if (blob.readingContext !== readingContext(store))
|
|
115
126
|
return undefined;
|
|
116
127
|
const fold = blob.fold;
|
|
128
|
+
if (currentInstrument !== undefined && fold.instrument !== currentInstrument)
|
|
129
|
+
return undefined;
|
|
130
|
+
if (blob.judgmentContext !== judgmentContext(store, linesOf(fold.demands)))
|
|
131
|
+
return undefined;
|
|
117
132
|
return {
|
|
118
133
|
...fold,
|
|
119
134
|
standing: linesOf(fold.standing),
|
|
@@ -149,7 +164,7 @@ export function keepFold(store, key, fold, points, instrument = '') {
|
|
|
149
164
|
signed: [...fold.signed],
|
|
150
165
|
kept: false,
|
|
151
166
|
};
|
|
152
|
-
landMemo(store, `fold/${key}`, text(JSON.stringify({ fold: packed, points, instrument, readingContext: readingContext(store) })));
|
|
167
|
+
landMemo(store, `fold/${key}`, text(JSON.stringify({ fold: packed, points, instrument, readingContext: readingContext(store), judgmentContext: judgmentContext(store, fold.demands) })));
|
|
153
168
|
const at = memoAt(store, lastOf(points));
|
|
154
169
|
mkdirSync(dirname(at), { recursive: true });
|
|
155
170
|
writeFileSync(`${at}.${process.pid}.part`, `${key}\n`);
|
package/dist/fold/keys.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { dirname, existsSync, join } from "../host/io.js";
|
|
2
|
-
import { ledgerLines, signedLines } from "../land/ledger.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { ledgerLines, ledgerWriters, signedLines } from "../land/ledger.js";
|
|
3
|
+
import { fieldOf, foldClaims, isWire, LOCK, selfName } from "./claims.js";
|
|
4
|
+
import { observeText } from "../observe/files.js";
|
|
5
|
+
import { publicLock } from '@lapxo/topos/wire';
|
|
5
6
|
const held = new Map();
|
|
7
|
+
const keyInputs = new Map();
|
|
6
8
|
const merged = new Map();
|
|
7
9
|
/**
|
|
8
10
|
* Whose lines carry a class of writing, as the lock says it. Every ledger the store keeps is read, the key claims
|
|
@@ -47,13 +49,15 @@ export function signedLockLines(store, told = []) {
|
|
|
47
49
|
return lockSigners(store, told).flatMap((id) => signedLines(store, id));
|
|
48
50
|
}
|
|
49
51
|
function keysOf(store, told) {
|
|
50
|
-
const
|
|
52
|
+
const input = ledgerKeyLines(store).join('\n');
|
|
53
|
+
const mine = told.length ? read(store, told) : keyInputs.get(store) === input ? held.get(store) ?? read(store) : read(store);
|
|
54
|
+
if (!told.length)
|
|
55
|
+
keyInputs.set(store, input);
|
|
51
56
|
if (!told.length)
|
|
52
57
|
held.set(store, mine);
|
|
53
58
|
return mine;
|
|
54
59
|
}
|
|
55
|
-
const ledgerKeyLines = (store) => (
|
|
56
|
-
.filter((name) => name.endsWith(EXTENSION)).flatMap((name) => ledgerLines(store, name.slice(0, -EXTENSION.length)))
|
|
60
|
+
const ledgerKeyLines = (store) => ledgerWriters(store).flatMap(writer => ledgerLines(store, writer))
|
|
57
61
|
.filter((line) => /\bsig=/.test(line) && /(?:^|\s)scope=keys\/[^/\s]+\s/.test(line));
|
|
58
62
|
const releases = new Map();
|
|
59
63
|
/**
|
|
@@ -65,13 +69,16 @@ export function releaseOf(store) {
|
|
|
65
69
|
return releases.get(store);
|
|
66
70
|
const at = join(dirname(store), LOCK);
|
|
67
71
|
const lines = ledgerKeyLines(store).length || !existsSync(at) ? [] : (observeText(at) ?? '').split('\n').filter(isWire);
|
|
68
|
-
const
|
|
72
|
+
const published = lines.some(line => fieldOf(line, 'scope').startsWith('keys/') && fieldOf(line, 'measure') === 'public-key' && fieldOf(line, 'sig'))
|
|
73
|
+
&& publicLock(lines) !== undefined;
|
|
74
|
+
const got = published || lines.some((line) => fieldOf(line, 'scope') === 'publish/bootstrap' && fieldOf(line, 'value') !== 'withdraw') ? lines : undefined;
|
|
69
75
|
releases.set(store, got);
|
|
70
76
|
return got;
|
|
71
77
|
}
|
|
72
78
|
function read(store, told = []) {
|
|
73
|
-
const
|
|
74
|
-
|
|
79
|
+
const release = releaseOf(store) ?? [], published = publicLock(release) === undefined ? [] : release;
|
|
80
|
+
const said = [...ledgerKeyLines(store), ...release, ...told]
|
|
81
|
+
.filter((line) => (/\bsig=/.test(line) || published.includes(line)) && /(?:^|\s)scope=keys\/[^/\s]+\s/.test(line));
|
|
75
82
|
const standing = foldClaims(said).standing.filter((line) => fieldOf(line, 'value') !== 'withdraw')
|
|
76
83
|
.sort((a, b) => (Number(fieldOf(a, 'epoch')) || 0) - (Number(fieldOf(b, 'epoch')) || 0));
|
|
77
84
|
const idOf = (line) => fieldOf(line, 'scope').slice('keys/'.length);
|
package/dist/fold/observed.js
CHANGED
|
@@ -267,13 +267,14 @@ export function observedClaims(root, store, options = {}) {
|
|
|
267
267
|
}
|
|
268
268
|
function observeOne(reader, started) {
|
|
269
269
|
const module = resolve(root, reader.module);
|
|
270
|
+
const lifetime = readerLifetime(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), { id: reader.id, module: reader.module });
|
|
270
271
|
const asked = reader.capsule === undefined ? undefined : reader.capsule.selected ?? capsuleAt(reader.capsule.digest, reader.capsule.entry);
|
|
271
272
|
const projection = reader.capsule === undefined ? [] : (asked?.lines ?? []).filter(line => fieldOf(line, 'scope') === `region/${reader.capsule.region}` && fieldOf(line, 'measure') === 'input-projection' && fieldOf(line, 'value') !== 'withdraw');
|
|
272
273
|
if (projection.length) {
|
|
273
274
|
if (projection.length !== 1 || !asked)
|
|
274
275
|
throw Error('REFUSE·input projection is ambiguous ' + reader.module);
|
|
275
276
|
const files = [...new Set(reader.where.flatMap(where => handedAt(root, reader, roleOf, look, where)))].sort().map(place => ({ place, text: observeText(resolve(root, place)) ?? '' }));
|
|
276
|
-
const result = readingClosures({ root, store, reader: reader.module, speaker, digest: text => bytesDigest(store, Buffer.from(text)), capsule: asked, region: reader.capsule.region, projection: fieldOf(projection[0], 'value'), allowsEmpty: readerAllowsEmpty(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader.module), files, emit: line => process.stderr.write(line + '\n') });
|
|
277
|
+
const result = readingClosures({ root, store, reader: reader.module, speaker, digest: text => bytesDigest(store, Buffer.from(text)), capsule: asked, region: reader.capsule.region, projection: fieldOf(projection[0], 'value'), readerReference: { id: reader.id, module: reader.module }, lifetime, allowsEmpty: readerAllowsEmpty(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader.module), files, emit: line => process.stderr.write(line + '\n') });
|
|
277
278
|
out.push(...result.lines);
|
|
278
279
|
ran += result.executed;
|
|
279
280
|
read += result.read - result.opened;
|
|
@@ -287,7 +288,7 @@ export function observedClaims(root, store, options = {}) {
|
|
|
287
288
|
assertReaderInputs(standing.flatMap(line => { const p = parseLifetime(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader.module, offered.length);
|
|
288
289
|
const vectors = reader.capsule === undefined ? observeText(resolve(root, vectorsOf(reader.module, standing))) : undefined;
|
|
289
290
|
const kept = whole && legacy === undefined && answered === undefined && source ? sha([
|
|
290
|
-
speaker, reader.module, reader.kind, reader.shape.join('|'), reader.where.join('|'), bundle, vectors === undefined ? 'no vectors' : sha(vectors),
|
|
291
|
+
speaker, reader.module, JSON.stringify(lifetime), reader.kind, reader.shape.join('|'), reader.where.join('|'), bundle, vectors === undefined ? 'no vectors' : sha(vectors),
|
|
291
292
|
reader.capsule === undefined ? '' : `${reader.capsule.selected?.selection ?? reader.capsule.digest} ${reader.capsule.region} ${reader.capsule.reads.join('|')} ${[...reader.capsule.globs].map(([place, globs]) => `${place}${globs.join('|')}`).join(' ')}`,
|
|
292
293
|
...offered.map((offer) => `${offer.place} ${offer.digest?.() ?? bytesDigest(store, offer.bytes())}`),
|
|
293
294
|
].join('\n')) : undefined;
|
|
@@ -302,9 +303,9 @@ export function observedClaims(root, store, options = {}) {
|
|
|
302
303
|
const home = regionIn(regions, reader.module);
|
|
303
304
|
const own = shardLines(store, speaker, home).filter((line) => fieldOf(line, 'scope') === `reader/${reader.module}`);
|
|
304
305
|
const lineage = own.filter((line) => fieldOf(line, 'measure') === 'identity').map((line) => fieldOf(line, 'value'));
|
|
305
|
-
const sum = source && reader.capsule === undefined ? bytesDigest(store, new TextEncoder().encode(`${reader.module} ${bundle}`)).replace(/^sha256:/, '').slice(0, 12) : 'none';
|
|
306
|
-
const measured = reader.capsule !== undefined ? { by: source ? sha(`${reader.capsule.selected?.selection ?? reader.capsule.digest}\n${reader.capsule.region}`).slice(0, 12) : 'none', lines: [] }
|
|
307
|
-
: source ? identityOf({ store, root, speaker, module: reader.module, kind: reader.kind, code: sum, lineage, lifetime
|
|
306
|
+
const sum = source && reader.capsule === undefined ? bytesDigest(store, new TextEncoder().encode(`${reader.module} ${bundle}${lifetime === undefined ? "" : "\n" + JSON.stringify(lifetime)}`)).replace(/^sha256:/, '').slice(0, 12) : 'none';
|
|
307
|
+
const measured = reader.capsule !== undefined ? { by: source ? sha(`${reader.capsule.selected?.selection ?? reader.capsule.digest}\n${reader.capsule.region}${lifetime === undefined ? "" : "\n" + JSON.stringify(lifetime)}`).slice(0, 12) : 'none', lines: [] }
|
|
308
|
+
: source ? identityOf({ store, root, speaker, module: reader.module, kind: reader.kind, code: sum, lineage, lifetime, kept: own.filter((line) => /(?:^|\s)measure=(?:extension|emits)\s/.test(line)) })
|
|
308
309
|
: { by: 'none', lines: [] };
|
|
309
310
|
const by = measured.by;
|
|
310
311
|
const level = [...measured.lines];
|
|
@@ -419,12 +420,12 @@ export function observedClaims(root, store, options = {}) {
|
|
|
419
420
|
? asked.ask(due.map((d) => ({
|
|
420
421
|
protocol: PROTOCOL, verb: 'read', rootScope: d.place, region: reader.capsule.region, held: heldOf(d.place, d.region),
|
|
421
422
|
files: (reader.capsule.reads.some((read) => read.includes('@')) ? handedAt(root, reader, roleOf, look, d.place) : [d.place]).map((coordinate) => ({ place: coordinate, text: observeText(resolve(root, coordinate)) ?? '' })),
|
|
422
|
-
}))).map((answer) => {
|
|
423
|
+
})), { id: reader.id, module: reader.module }).map((answer) => {
|
|
423
424
|
if (answer?.kind !== 'fact')
|
|
424
425
|
throw new Error(`REFUSE·reader ${reader.module} · ${answer?.why ?? 'no capsule answer'}`);
|
|
425
426
|
return (answer.claims ?? []);
|
|
426
427
|
})
|
|
427
|
-
: runReader(module, root, due.map((d) => ({ place: d.place, ...(reader.kind === 'js' ? { text: new TextDecoder().decode(d.bytes) } : {}), held: heldOf(d.place, d.region) })),
|
|
428
|
+
: runReader(module, root, due.map((d) => ({ place: d.place, ...(reader.kind === 'js' ? { text: new TextDecoder().decode(d.bytes) } : {}), held: heldOf(d.place, d.region) })), lifetime);
|
|
428
429
|
}
|
|
429
430
|
catch (error) {
|
|
430
431
|
const why = `REFUSE·reader ${reader.module} · ${error instanceof Error ? error.message : String(error)}`;
|
package/dist/fold/places.d.ts
CHANGED
|
@@ -2,7 +2,11 @@ type Spoken = {
|
|
|
2
2
|
readonly place: string;
|
|
3
3
|
readonly line: string;
|
|
4
4
|
};
|
|
5
|
-
|
|
5
|
+
/** A dependency's lock describes the dependency, never a child of its host.
|
|
6
|
+
* Retiring a mount does not turn its retained bytes into local declarations. */
|
|
7
|
+
export declare function mountedCoordinatesIn(root: string, history?: readonly string[]): readonly string[];
|
|
8
|
+
export declare function localDirectoriesIn(root: string, history?: readonly string[]): readonly string[];
|
|
9
|
+
export declare function placesIn(root: string, history?: readonly string[]): readonly string[];
|
|
6
10
|
/** A directory with no own lock is founded by the root: a line of its region family names the directory's coordinates, and a covered batch that names the place lands its first own lock. */
|
|
7
11
|
export declare const foundableIn: (root: string, standing: readonly string[], family: string, name: string) => boolean;
|
|
8
12
|
export declare const capsuleAt: (root: string, name: string) => boolean;
|
package/dist/fold/places.js
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { join } from "../host/io.js";
|
|
2
|
-
import { CAPSULE, canonical, LOCK, matches, parse } from '@lapxo/topos/wire';
|
|
2
|
+
import { CAPSULE, canonical, EXTENSION, LOCK, matches, parse } from '@lapxo/topos/wire';
|
|
3
|
+
import { lockLines } from "./keys.js";
|
|
4
|
+
import { releasesOf } from "../host/release.js";
|
|
5
|
+
import { archiveCoordinate } from "../host/archive.js";
|
|
3
6
|
import { fieldOf } from "./claims.js";
|
|
4
7
|
import { isCeiling } from "./configures.js";
|
|
5
8
|
import { entriesIn, observeText } from "../observe/files.js";
|
|
6
9
|
import { ownLock } from "../observe/runner.js";
|
|
7
10
|
import { said } from "./wire.js";
|
|
8
11
|
import { ownLockOf } from "./signed.js";
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
/** A dependency's lock describes the dependency, never a child of its host.
|
|
13
|
+
* Retiring a mount does not turn its retained bytes into local declarations. */
|
|
14
|
+
export function mountedCoordinatesIn(root, history = lockLines(join(root, EXTENSION))) {
|
|
15
|
+
return releasesOf(history).flatMap(line => {
|
|
16
|
+
const needs = fieldOf(line, 'needs');
|
|
17
|
+
return needs ? [archiveCoordinate(needs)] : [];
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function localDirectoriesIn(root, history = lockLines(join(root, EXTENSION))) {
|
|
21
|
+
const mounts = new Set(mountedCoordinatesIn(root, history));
|
|
22
|
+
return entriesIn(root).filter(entry => entry.dir && !mounts.has(entry.name)).map(entry => entry.name).sort();
|
|
23
|
+
}
|
|
24
|
+
export function placesIn(root, history = lockLines(join(root, EXTENSION))) {
|
|
25
|
+
return localDirectoriesIn(root, history).filter(name => observeText(join(root, name, LOCK)) !== undefined);
|
|
26
|
+
}
|
|
11
27
|
/** A directory with no own lock is founded by the root: a line of its region family names the directory's coordinates, and a covered batch that names the place lands its first own lock. */
|
|
12
28
|
export const foundableIn = (root, standing, family, name) => !placesIn(root).includes(name)
|
|
13
29
|
&& (entriesIn(root).find((entry) => entry.name === name)?.dir ?? false)
|
package/dist/fold/readers.js
CHANGED
|
@@ -43,6 +43,7 @@ const reachOfLine = (module, where) => {
|
|
|
43
43
|
*/
|
|
44
44
|
export function capsuleReadersOf(root, store, standing, lines, resolve = capsulesFor) {
|
|
45
45
|
const reaching = new Set(lines.flatMap((reader) => reachOfLine(reader.module, reader.where)));
|
|
46
|
+
const coordinateMeasure = wordOn(standing, 'region-coordinate', ownLock());
|
|
46
47
|
const localMeasures = wordsOf(standing, 'region-measures');
|
|
47
48
|
const pins = standing.filter(line => fieldOf(line, 'scope').startsWith('uses/') && fieldOf(line, 'value') !== 'withdraw');
|
|
48
49
|
const measures = localMeasures.length || !pins.length ? localMeasures
|
|
@@ -51,7 +52,7 @@ export function capsuleReadersOf(root, store, standing, lines, resolve = capsule
|
|
|
51
52
|
const groups = new Map();
|
|
52
53
|
for (const place of ['', ...placesIn(root)]) {
|
|
53
54
|
const where = place ? `${place}/` : '';
|
|
54
|
-
const own = ownLockOf(root, place).filter((line) => fieldOf(line, 'scope').startsWith('region/') && measures.includes(fieldOf(line, 'measure')) && fieldOf(line, 'value') !== 'withdraw');
|
|
55
|
+
const own = ownLockOf(root, place).filter((line) => fieldOf(line, 'scope').startsWith('region/') && measures.includes(fieldOf(line, 'measure')) && fieldOf(line, 'measure') === coordinateMeasure && fieldOf(line, 'value') !== 'withdraw');
|
|
55
56
|
const offered = [...own, ...(capsuleAt(root, place) ? world.filter((line) => !own.some((one) => fieldOf(one, 'scope') === fieldOf(line, 'scope'))) : [])];
|
|
56
57
|
if (!offered.length)
|
|
57
58
|
continue;
|
|
@@ -92,8 +92,6 @@ export function releasePolicyLines(result) {
|
|
|
92
92
|
if (result === undefined)
|
|
93
93
|
return [];
|
|
94
94
|
const gates = (result.gates ?? []).map(g => `GATE ${g.scope} ${g.status} · requires=${g.required.join('|')} · ${g.evidence.map(e => `${e.coordinate}=${e.value}`).join(' | ') || g.reason || 'no evidence'}`);
|
|
95
|
-
if (result.status === 'absent')
|
|
96
|
-
return gates;
|
|
97
95
|
return [
|
|
98
96
|
...gates,
|
|
99
97
|
`RELEASE policy ${result.status} · fold ${result.snapshot.fold} · instrument ${result.snapshot.instrument}`,
|
package/dist/fold/signed.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { authorityFor } from './signers.ts';
|
|
1
2
|
/** Lines told to a place's own lock for a preview: folded as if the file held them, and standing only as its signed lines do. */
|
|
2
3
|
export declare const tellOwn: (place: string, lines: readonly string[]) => void;
|
|
3
4
|
/**
|
|
@@ -16,3 +17,7 @@ export declare const admittedIn: (root: string, place: string, line: string) =>
|
|
|
16
17
|
export declare const appliedIn: (root: string, place: string) => readonly string[];
|
|
17
18
|
export declare const forksIn: (root: string, place: string) => readonly string[];
|
|
18
19
|
export declare const writtenIn: (root: string, place: string) => number;
|
|
20
|
+
/** Authenticated history excludes prospective lines told only for admission. */
|
|
21
|
+
export declare const appliedHistoryIn: (root: string, place: string) => readonly string[];
|
|
22
|
+
/** Shared signature/window/coordinate coverage boundary for own history. */
|
|
23
|
+
export declare function ownRecordAdmitted(line: string, place: string, family: string, signers: ReturnType<typeof authorityFor>['admitted'], algorithms: readonly string[]): boolean;
|
package/dist/fold/signed.js
CHANGED
|
@@ -13,7 +13,7 @@ const held = new Map();
|
|
|
13
13
|
const told = new Map();
|
|
14
14
|
/** Lines told to a place's own lock for a preview: folded as if the file held them, and standing only as its signed lines do. */
|
|
15
15
|
export const tellOwn = (place, lines) => void told.set(place, lines);
|
|
16
|
-
function verified(root, place) {
|
|
16
|
+
function verified(root, place, preview = true) {
|
|
17
17
|
const store = storeOf(root);
|
|
18
18
|
const lock = lockLines(store);
|
|
19
19
|
const publicText = observeText(join(root, place, LOCK)) ?? '';
|
|
@@ -21,7 +21,7 @@ function verified(root, place) {
|
|
|
21
21
|
const algorithms = hasAuthority ? signaturesOf(store).admitted : [];
|
|
22
22
|
const signers = algorithms.length ? authorityFor(lock, rootSigner(root), algorithms).admitted : [];
|
|
23
23
|
const history = signers.flatMap(signer => place ? shardLines(store, signer.id, `${place}/${LOCK}`) : ledgerLines(store, signer.id)).filter(line => fieldOf(line, 'sig') !== '');
|
|
24
|
-
const text = [publicText, ...history, ...(told.get(place) ?? [])].join('\n');
|
|
24
|
+
const text = [publicText, ...history, ...(preview ? told.get(place) ?? [] : [])].join('\n');
|
|
25
25
|
const key = `${root} ${place}`;
|
|
26
26
|
const last = held.get(key);
|
|
27
27
|
if (last !== undefined && last.text === text && last.lock === lock)
|
|
@@ -30,19 +30,7 @@ function verified(root, place) {
|
|
|
30
30
|
// Unrestricted coverage does not construct a region coordinate. Restricted
|
|
31
31
|
// coverage still requires the lock's declared namespace before comparison.
|
|
32
32
|
const family = signers.some(signer => !signer.coverage.includes('*')) ? wordOf(lockStanding(store), 'families', 'region') : '';
|
|
33
|
-
const stands = (line) =>
|
|
34
|
-
const got = parse(line);
|
|
35
|
-
if (got.kind !== 'fact' || got.value.fields['sig'] === undefined)
|
|
36
|
-
return false;
|
|
37
|
-
const fields = got.value.fields;
|
|
38
|
-
const signer = signers.find((one) => one.id === fields['by']);
|
|
39
|
-
const epoch = Number(fields['epoch']) || 0;
|
|
40
|
-
if (signer === undefined || !verifiesFields(fields, signer.publicKey, algorithms))
|
|
41
|
-
return false;
|
|
42
|
-
const window = windowAt(signer, epoch);
|
|
43
|
-
return epoch >= window.start && (window.close === null || epoch <= window.close)
|
|
44
|
-
&& signer.coverage.some((prefix) => prefix === '*' || within(region(`${family}/${place}/${fields['scope'] ?? ''}`, './'), region(prefix, './')));
|
|
45
|
-
};
|
|
33
|
+
const stands = (line) => ownRecordAdmitted(line, place, family, signers, algorithms);
|
|
46
34
|
const applied = new Set(lines.filter(stands));
|
|
47
35
|
// A published copy of an authenticated delivery is not another inscription.
|
|
48
36
|
const projected = new Set([...applied].map(sayingOf));
|
|
@@ -75,3 +63,19 @@ export const admittedIn = (root, place, line) => verified(root, place).applied.h
|
|
|
75
63
|
export const appliedIn = (root, place) => [...verified(root, place).applied];
|
|
76
64
|
export const forksIn = (root, place) => ((got) => foldClaims(got.lines.filter((line) => got.applied.has(line) || (!/ sig=/.test(line) && !got.projected.has(sayingOf(line))))).forks.map((fork) => fork.key))(verified(root, place));
|
|
77
65
|
export const writtenIn = (root, place) => ((got) => got.lines.filter((line) => !got.applied.has(line)).length)(verified(root, place));
|
|
66
|
+
/** Authenticated history excludes prospective lines told only for admission. */
|
|
67
|
+
export const appliedHistoryIn = (root, place) => [...verified(root, place, false).applied];
|
|
68
|
+
/** Shared signature/window/coordinate coverage boundary for own history. */
|
|
69
|
+
export function ownRecordAdmitted(line, place, family, signers, algorithms) {
|
|
70
|
+
const got = parse(line);
|
|
71
|
+
if (got.kind !== 'fact' || got.value.fields['sig'] === undefined)
|
|
72
|
+
return false;
|
|
73
|
+
const fields = got.value.fields;
|
|
74
|
+
const signer = signers.find((one) => one.id === fields['by']);
|
|
75
|
+
const epoch = Number(fields['epoch']) || 0;
|
|
76
|
+
if (signer === undefined || !verifiesFields(fields, signer.publicKey, algorithms))
|
|
77
|
+
return false;
|
|
78
|
+
const window = windowAt(signer, epoch);
|
|
79
|
+
return epoch >= window.start && (window.close === null || epoch <= window.close)
|
|
80
|
+
&& signer.coverage.some((prefix) => prefix === '*' || within(region(`${family}/${place}/${fields['scope'] ?? ''}`, './'), region(prefix, './')));
|
|
81
|
+
}
|
package/dist/fold/zoom.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { join } from "../host/io.js";
|
|
2
2
|
import { canonical, LOCK, parse, RECEIPTS } from '@lapxo/topos/wire';
|
|
3
3
|
import { fieldOf, foldClaims, isWire } from "./claims.js";
|
|
4
|
-
import {
|
|
4
|
+
import { observeText } from "../observe/files.js";
|
|
5
|
+
import { placesIn } from "./places.js";
|
|
5
6
|
/**
|
|
6
7
|
* A line of the tree seen from inside a place: every path it names under the place is read from the place's root, and
|
|
7
8
|
* a path outside it is dropped, since the place alone cannot read it. The place's name drops out of the scope, the condition
|
|
@@ -46,10 +47,10 @@ export function zoomed(line, place) {
|
|
|
46
47
|
}
|
|
47
48
|
/** The lines of each place's own lock that still name the place: seen from inside a place, no scope carries its name. */
|
|
48
49
|
export function namingTheirPlace(root) {
|
|
49
|
-
return
|
|
50
|
-
const own = (observeText(join(root,
|
|
50
|
+
return placesIn(root).filter(name => !name.startsWith('.')).flatMap((name) => {
|
|
51
|
+
const own = (observeText(join(root, name, LOCK)) ?? '').split('\n').filter(isWire);
|
|
51
52
|
return foldClaims(own).standing.filter((line) => fieldOf(line, 'value') !== 'withdraw')
|
|
52
|
-
.map((line) => fieldOf(line, 'scope')).filter((scope) => scope.startsWith(`${
|
|
53
|
+
.map((line) => fieldOf(line, 'scope')).filter((scope) => scope.startsWith(`${name}/`) || scope.includes(`/${name}/`));
|
|
53
54
|
}).length;
|
|
54
55
|
}
|
|
55
56
|
/**
|
package/dist/host/capsule.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ReaderReference } from '@lapxo/topos/wire';
|
|
1
2
|
import type { Declaration } from '@lapxo/topos/capsule';
|
|
2
3
|
import type { Request, Response } from '@lapxo/topos/contract';
|
|
3
4
|
export interface Capsule {
|
|
@@ -5,7 +6,7 @@ export interface Capsule {
|
|
|
5
6
|
readonly digest: string;
|
|
6
7
|
readonly declaration: Declaration;
|
|
7
8
|
readonly lines: readonly string[];
|
|
8
|
-
readonly ask: (requests: readonly Request[]) => readonly (Response | undefined)[];
|
|
9
|
+
readonly ask: (requests: readonly Request[], reader?: ReaderReference) => readonly (Response | undefined)[];
|
|
9
10
|
}
|
|
10
11
|
/** A blob is loaded once in an act. A later ask for the same digest reads that capsule, and does not hash or unpack it again. */
|
|
11
12
|
export declare function capsuleAt(digest: string, entry: string, store?: string): Capsule | undefined;
|
package/dist/host/capsule.js
CHANGED
|
@@ -60,10 +60,10 @@ export function capsuleAt(digest, entry, store) {
|
|
|
60
60
|
if (!entry || isAbsolute(entry) || (coordinate === '..' || coordinate.startsWith(`..${sep}`)) || entry.split(/[\\/]/).includes('..')) {
|
|
61
61
|
throw new Error(`REFUSE·capsule ${digest} declared entry ${entry || '(absent)'} unavailable`);
|
|
62
62
|
}
|
|
63
|
-
const capsule = { digest, ...known, ask: (requests) => {
|
|
63
|
+
const capsule = { digest, ...known, ask: (requests, reader) => {
|
|
64
64
|
if (!statSync(module, { throwIfNoEntry: false })?.isFile())
|
|
65
65
|
throw new Error(`REFUSE·capsule ${digest} declared entry ${entry} unavailable`);
|
|
66
|
-
return answered(digest, bytes, requests, names, root, cacheStore, module);
|
|
66
|
+
return answered(digest, bytes, requests, names, root, cacheStore, module, reader);
|
|
67
67
|
} };
|
|
68
68
|
capsules.set(identity, capsule);
|
|
69
69
|
return capsule;
|
|
@@ -86,14 +86,16 @@ const spawnOf = (at, names) => {
|
|
|
86
86
|
return child;
|
|
87
87
|
};
|
|
88
88
|
export const childFlags = (argv = process.execArgv) => argv.filter((flag, i) => !/^(-e|-p|--eval|--print)$/.test(argv[i - 1] ?? '') && !/^(-e|-p|--eval|--print|--input-type)(=|$)/.test(flag));
|
|
89
|
-
function answered(digest, bytes, requests, names, tree, cacheStore = ownStore(), entry) {
|
|
89
|
+
function answered(digest, bytes, requests, names, tree, cacheStore = ownStore(), entry, reader) {
|
|
90
90
|
const child = entry === undefined ? spawnOf(tree ?? unpacked(digest, bytes), names) : [entry];
|
|
91
91
|
if (!child.length) {
|
|
92
92
|
process.stderr.write(`LOAD regions · none of ${names.length} under the directories release/layout names\n`);
|
|
93
93
|
return requests.map(() => undefined);
|
|
94
94
|
}
|
|
95
95
|
const loader = entry !== undefined ? `${createHash('sha256').update(`${relative(tree, entry)}\n`).digest('hex')}\n` : child.length > 1 ? `${createHash('sha256').update(observeFile(child[0]) ?? new Uint8Array()).digest('hex')}\n` : '';
|
|
96
|
-
const
|
|
96
|
+
const lifetime = readerLifetime(contentPolicy().flatMap(line => { const p = parse(line); return p.kind === 'fact' ? [p.value.fields] : []; }), reader ?? digest);
|
|
97
|
+
const policy = lifetime === undefined ? '' : JSON.stringify(lifetime) + '\n';
|
|
98
|
+
const hitOf = (request) => sha(`${digest}\n${loader}${policy}${JSON.stringify(request)}`);
|
|
97
99
|
const hits = requests.map(hitOf);
|
|
98
100
|
const kept = hits.map(hit => storeAt(cacheStore, 'cas', 'answers', hit));
|
|
99
101
|
const held = kept.map((at, i) => {
|
|
@@ -114,7 +116,7 @@ function answered(digest, bytes, requests, names, tree, cacheStore = ownStore(),
|
|
|
114
116
|
if (missing.length)
|
|
115
117
|
process.stderr.write(`CAPSULE ${digest.slice(7, 19)} · ${requests.length} asked · ${missing.length} run · ${missing.map((one) => `${one.rootScope}${one.region}`).join(' ')}\n`);
|
|
116
118
|
// Execute and validate the entire batch before committing any answer to the cache.
|
|
117
|
-
const ran = !missing.length ? [] : executeCapsule(process.execPath, [...childFlags(), hostOf(), ...child], missing, {},
|
|
119
|
+
const ran = !missing.length ? [] : executeCapsule(process.execPath, [...childFlags(), hostOf(), ...child], missing, {}, lifetime);
|
|
118
120
|
return requests.map((request, i) => {
|
|
119
121
|
if (held[i] !== undefined)
|
|
120
122
|
return held[i];
|
|
@@ -148,5 +150,5 @@ export function locatedAt(tree, place, cacheStore = ownStore()) {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
const digest = `sha256:${hash.digest('hex')}`;
|
|
151
|
-
return { digest, ...known, ask: (requests) => answered(digest, new Uint8Array(), requests, names, at, cacheStore) };
|
|
153
|
+
return { digest, ...known, ask: (requests, reader) => answered(digest, new Uint8Array(), requests, names, at, cacheStore, undefined, reader) };
|
|
152
154
|
}
|
|
@@ -3,7 +3,7 @@ import { join, dirname } from 'node:path';
|
|
|
3
3
|
import { performance } from 'node:perf_hooks';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { canonical, objectHistory, parse, byBytes, wireAt, RECEIPTS } from '@lapxo/topos/wire';
|
|
6
|
-
import { cellInputs, cellReceiptContract } from '@lapxo/topos/cell-inputs';
|
|
6
|
+
import { cellInputs, cellReceiptContract, cellInputProfile } from '@lapxo/topos/cell-inputs';
|
|
7
7
|
import { renderCells } from '@lapxo/topos/cells-view';
|
|
8
8
|
import { admittedObjects } from "./objects.js";
|
|
9
9
|
import { objectFold } from "../fold/object.js";
|
|
@@ -30,6 +30,7 @@ export async function cellsWithReceipts(root, history, emit) {
|
|
|
30
30
|
throw Error(`REFUSE·authority receipt input ${record.fields.id}`);
|
|
31
31
|
const config = parsed.value.configuration.filter(f => authority.of(canonical(f)).kind === 'admitted');
|
|
32
32
|
cellReceiptContract(config);
|
|
33
|
+
const profile = cellInputProfile(config);
|
|
33
34
|
const instrument = ownLock().map(fields);
|
|
34
35
|
const wire = wireAt(instrument, Number.MAX_SAFE_INTEGER);
|
|
35
36
|
if (!wire)
|
|
@@ -38,7 +39,7 @@ export async function cellsWithReceipts(root, history, emit) {
|
|
|
38
39
|
if (!algorithm)
|
|
39
40
|
throw Error('REFUSE·receipt no digest algorithm');
|
|
40
41
|
const code = implementation(algorithm);
|
|
41
|
-
const inputs = cellInputs(parsed.value.objects), semantic = new Map(), inputBytes = new Map();
|
|
42
|
+
const inputs = cellInputs(parsed.value.objects, profile), semantic = new Map(), inputBytes = new Map();
|
|
42
43
|
const pins = new Map();
|
|
43
44
|
for (const record of parsed.value.objects.filter(r => r.record === 'cell')) {
|
|
44
45
|
const pin = record.fields.topos;
|
|
@@ -47,7 +48,10 @@ export async function cellsWithReceipts(root, history, emit) {
|
|
|
47
48
|
pins.set(pin, [pin, ...top.dependencies]);
|
|
48
49
|
}
|
|
49
50
|
const scope = `receipts/cells/${record.fields.scope}`;
|
|
50
|
-
const
|
|
51
|
+
const ownInputs = inputs.get(record.fields.scope);
|
|
52
|
+
const metadata = [...pins.get(pin), ...code, 'resolution=1'];
|
|
53
|
+
const bytes = profile === 'semantic-live@1' ? [...ownInputs, ...metadata].sort(byBytes).join('\n') :
|
|
54
|
+
[...ownInputs, ...metadata.sort(byBytes), `receipt-inputs=${profile}`].join('\n');
|
|
51
55
|
inputBytes.set(scope, bytes);
|
|
52
56
|
semantic.set(scope, fullDigest(bytes, algorithm));
|
|
53
57
|
}
|
package/dist/host/io.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The host's node surface: kernel folders import here, never `node:`. World reads still go through observeFile. */
|
|
2
2
|
export { spawn, spawnSync } from 'node:child_process';
|
|
3
3
|
export { createHash } from 'node:crypto';
|
|
4
|
-
export { appendFileSync, closeSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
4
|
+
export { appendFileSync, closeSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
5
5
|
export { tmpdir } from 'node:os';
|
|
6
6
|
export { basename, dirname, join, relative, resolve } from 'node:path';
|
|
7
7
|
export { fileURLToPath } from 'node:url';
|
package/dist/host/io.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The host's node surface: kernel folders import here, never `node:`. World reads still go through observeFile. */
|
|
2
2
|
export { spawn, spawnSync } from 'node:child_process';
|
|
3
3
|
export { createHash } from 'node:crypto';
|
|
4
|
-
export { appendFileSync, closeSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
4
|
+
export { appendFileSync, closeSync, existsSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
5
5
|
export { tmpdir } from 'node:os';
|
|
6
6
|
export { basename, dirname, join, relative, resolve } from 'node:path';
|
|
7
7
|
export { fileURLToPath } from 'node:url';
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import { requireEffects } from "../read-only.js";
|
|
5
4
|
import { underTheLock } from "../../land/act.js";
|
|
6
|
-
|
|
7
|
-
fsyncSync(fd);
|
|
8
|
-
}
|
|
9
|
-
finally {
|
|
10
|
-
closeSync(fd);
|
|
11
|
-
} };
|
|
12
|
-
const durableFile = (path, text) => { const fd = openSync(path, 'wx', 0o600); try {
|
|
13
|
-
writeFileSync(fd, text);
|
|
14
|
-
fsyncSync(fd);
|
|
15
|
-
}
|
|
16
|
-
finally {
|
|
17
|
-
closeSync(fd);
|
|
18
|
-
} };
|
|
5
|
+
import { nativePublicationStorage, publishBundle } from "./publication.js";
|
|
19
6
|
/** Directory publication makes the pair visible together; there is no later ledger append that can expose half. */
|
|
20
|
-
export const nativeIngressStorage =
|
|
7
|
+
export const nativeIngressStorage = nativePublicationStorage;
|
|
21
8
|
const directory = (store) => join(store, 'ingress');
|
|
22
9
|
const nameOf = (identity) => {
|
|
23
10
|
if (!identity || identity.includes('\0'))
|
|
@@ -58,18 +45,7 @@ export async function commitIngress(store, identity, prepare, verify, storage =
|
|
|
58
45
|
const bundle = await prepare(ingressReceipts(store));
|
|
59
46
|
if (!bundle.evidence || !bundle.receipt || verify(bundle) !== true)
|
|
60
47
|
throw Error('REFUSE·ingress evidence and receipt do not verify');
|
|
61
|
-
|
|
62
|
-
const staged = join(root, '.stage-' + randomUUID());
|
|
63
|
-
mkdirSync(staged, { mode: 0o700 });
|
|
64
|
-
try {
|
|
65
|
-
durableFile(join(staged, 'evidence.bound'), bundle.evidence);
|
|
66
|
-
durableFile(join(staged, 'receipt.bound'), bundle.receipt);
|
|
67
|
-
syncDirectory(staged);
|
|
68
|
-
storage.publish(staged, at);
|
|
69
|
-
}
|
|
70
|
-
finally {
|
|
71
|
-
rmSync(staged, { recursive: true, force: true });
|
|
72
|
-
}
|
|
48
|
+
publishBundle(at, { 'evidence.bound': bundle.evidence, 'receipt.bound': bundle.receipt }, storage);
|
|
73
49
|
return measured('landed', bundle);
|
|
74
50
|
});
|
|
75
51
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PublicationStorage } from './publication.ts';
|
|
2
|
+
/** Prepared and verified by the selected Topos admission/result contract. */
|
|
3
|
+
export interface LocalActBundle {
|
|
4
|
+
readonly records: string;
|
|
5
|
+
readonly receipt: string;
|
|
6
|
+
readonly placements?: readonly {
|
|
7
|
+
place: string;
|
|
8
|
+
records: string;
|
|
9
|
+
}[];
|
|
10
|
+
}
|
|
11
|
+
export type LocalActVerification = (identity: string, bundle: LocalActBundle) => boolean;
|
|
12
|
+
export declare const actLines: (bytes: string) => readonly string[];
|
|
13
|
+
/** Journal integrity is not authority. Signature/coverage checks remain the
|
|
14
|
+
* same admission boundary; the hash algorithm here is the stored seal's named
|
|
15
|
+
* algorithm, not a new selection of the place's wire.
|
|
16
|
+
*/
|
|
17
|
+
export declare function intactLocalAct(identity: string, bundle: LocalActBundle): boolean;
|
|
18
|
+
/** Committed segments are part of this ledger, not a second authority source. */
|
|
19
|
+
export declare function committedLocalActs(store: string): readonly {
|
|
20
|
+
identity: string;
|
|
21
|
+
bundle: LocalActBundle;
|
|
22
|
+
}[];
|
|
23
|
+
/** A public result is available only as a verified committed pair.
|
|
24
|
+
* No writer-tail guess, newest-result selection or implicit authority.
|
|
25
|
+
*/
|
|
26
|
+
export declare function localActResult(store: string, identity: string, verify: LocalActVerification): LocalActBundle | undefined;
|
|
27
|
+
/** One publication point for records and their native result. Re-delivery
|
|
28
|
+
* reads the original pair without preparation, signing or a new local epoch.
|
|
29
|
+
* The caller must also integrate this boundary into admission-history readers;
|
|
30
|
+
* this storage port alone does not activate a grammar or land authority.
|
|
31
|
+
*/
|
|
32
|
+
export declare function commitLocalAct(store: string, identity: string, regions: readonly string[], prepare: () => Promise<LocalActBundle>, verify: LocalActVerification, storage?: PublicationStorage): Promise<LocalActBundle & {
|
|
33
|
+
readonly kind: 'landed' | 'once';
|
|
34
|
+
}>;
|