nervur 0.18.0 → 0.19.0
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 +7 -7
- package/dist/being/index.d.ts +1 -0
- package/dist/being/index.js +1 -0
- package/dist/being/lent.d.ts +32 -0
- package/dist/being/lent.js +63 -0
- package/dist/being/types.js +1 -1
- package/dist/conformance/beings.d.ts +2 -1
- package/dist/conformance/beings.js +7 -2
- package/dist/conformance/index.d.ts +0 -1
- package/dist/conformance/index.js +8 -5
- package/dist/harbor/box.d.ts +121 -0
- package/dist/harbor/box.js +121 -0
- package/dist/harbor/core.d.ts +5 -5
- package/dist/harbor/core.js +73 -72
- package/dist/harbor/index.d.ts +3 -1
- package/dist/harbor/index.js +3 -0
- package/dist/harbor/memory.d.ts +8 -4
- package/dist/harbor/memory.js +43 -11
- package/dist/harbor/store.js +1 -1
- package/dist/vector/cases.d.ts +4 -3
- package/dist/vector/cases.js +33 -5
- package/dist/vector/world.d.ts +1 -0
- package/dist/vector/world.js +17 -6
- package/dist/ward/ground.d.ts +13 -7
- package/dist/ward/ground.js +29 -11
- package/dist/ward/heirs.js +4 -2
- package/dist/ward/index.d.ts +2 -1
- package/dist/ward/index.js +1 -0
- package/dist/ward/partition.js +2 -2
- package/dist/ward/seal.js +1 -1
- package/dist/ward/stance.d.ts +0 -5
- package/dist/ward/ward.d.ts +1 -1
- package/dist/ward/ward.js +62 -27
- package/package.json +7 -5
- package/src/being/index.ts +1 -0
- package/src/being/lent.ts +66 -0
- package/src/being/types.ts +1 -1
- package/src/conformance/beings.ts +8 -3
- package/src/conformance/index.ts +18 -15
- package/src/harbor/box.ts +131 -0
- package/src/harbor/core.ts +23 -22
- package/src/harbor/index.ts +4 -1
- package/src/harbor/memory.ts +52 -12
- package/src/harbor/store.ts +1 -1
- package/src/vector/cases.ts +37 -9
- package/src/vector/world.ts +18 -6
- package/src/ward/ground.ts +85 -103
- package/src/ward/heirs.ts +4 -2
- package/src/ward/index.ts +2 -1
- package/src/ward/partition.ts +2 -2
- package/src/ward/seal.ts +1 -1
- package/src/ward/stance.ts +1 -1
- package/src/ward/ward.ts +59 -27
package/src/harbor/core.ts
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
// that arrive from the wire go to an own door or a held socket and never
|
|
20
20
|
// onward by request or by fallback; that one rule is the rendezvous.
|
|
21
21
|
import { Ward } from '../ward/ward.ts';
|
|
22
|
-
import { maker, entropy as random, learnPk, type Ground, type WardPointers
|
|
23
|
-
import type { BeingClass, BeingLike } from '../being/types.ts';
|
|
22
|
+
import { maker, entropy as random, learnPk, type Ground, type WardPointers } from '../ward/ground.ts';
|
|
23
|
+
import type { BeingClass, BeingLike, Invitation } from '../being/types.ts';
|
|
24
24
|
import { silence } from '../being/silence.ts';
|
|
25
25
|
import { request, type Reach } from './reach.ts';
|
|
26
26
|
import { isWardPk, openReply, wardSignPk } from '../ward/seal.ts';
|
|
@@ -149,22 +149,21 @@ export class Harbor {
|
|
|
149
149
|
for (const say of this.announcers) say();
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
152
|
+
// The box, as one invitation per ward: what this device hands a ward it
|
|
153
|
+
// boots so that the ward takes the box's being as a standing. Set once
|
|
154
|
+
// the harbor's own ward is standing, which is the usual order: a terrain
|
|
155
|
+
// puts that ward up first, then boots the rest on a ground that can reach
|
|
156
|
+
// it. A terrain says which ward is handed one by overriding `boxFor`, and
|
|
157
|
+
// a stranger's ward is handed none.
|
|
158
|
+
offering: ((name: string) => Promise<Invitation | undefined>) | undefined;
|
|
159
159
|
|
|
160
|
-
constructor(store: Store, loader: Loader = async () => ({})
|
|
160
|
+
constructor(store: Store, loader: Loader = async () => ({})) {
|
|
161
161
|
this.store = store;
|
|
162
162
|
this.loader = loader;
|
|
163
|
-
this.lend = lend;
|
|
164
163
|
}
|
|
165
164
|
|
|
166
|
-
protected
|
|
167
|
-
return this.
|
|
165
|
+
protected boxFor(name: string, _record: WardRecord): Promise<Invitation | undefined> {
|
|
166
|
+
return this.offering?.(name) ?? Promise.resolve(undefined);
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
// Boot every ward the store keeps, and learn the hints. One ward that will
|
|
@@ -237,7 +236,7 @@ export class Harbor {
|
|
|
237
236
|
const moved: string[] = [];
|
|
238
237
|
for (const row of s.kept.keys()) {
|
|
239
238
|
const was = s.kept.get(row);
|
|
240
|
-
const now = rowsOf(s.memory, [row])[0]
|
|
239
|
+
const now = rowsOf(s.memory, [row])[0][1];
|
|
241
240
|
if (JSON.stringify(was) === JSON.stringify(now)) continue;
|
|
242
241
|
restoreRow(s.memory as unknown as Partition, row, was);
|
|
243
242
|
moved.push(row);
|
|
@@ -283,7 +282,7 @@ export class Harbor {
|
|
|
283
282
|
// rows where the store already has them.
|
|
284
283
|
#settle(name: string, s: Saving): void {
|
|
285
284
|
for (const [row, value] of s.under) {
|
|
286
|
-
const now = rowsOf(s.memory, [row])[0]
|
|
285
|
+
const now = rowsOf(s.memory, [row])[0][1];
|
|
287
286
|
if (JSON.stringify(value) === JSON.stringify(now)) s.kept.set(row, value);
|
|
288
287
|
else s.dirty.add(row);
|
|
289
288
|
}
|
|
@@ -546,13 +545,14 @@ export class Harbor {
|
|
|
546
545
|
const objects = new WeakMap<object, BeingLike>(); // cells -> the object instantiate made. a side's hand, never the ward's; it follows the cells out when she is unbooted
|
|
547
546
|
const ground: Ground = {
|
|
548
547
|
seed,
|
|
549
|
-
memory,
|
|
550
548
|
// This ward's own classes first, then the harbor's: a ward that names a
|
|
551
549
|
// class of its own is answered with hers.
|
|
552
550
|
instantiate: maker(objects, classes, this.classes),
|
|
553
551
|
carry: (pk, bytes) => this.carry(pk, new Uint8Array(bytes)),
|
|
554
552
|
random,
|
|
555
|
-
|
|
553
|
+
memory: {
|
|
554
|
+
rows: memory,
|
|
555
|
+
told: (row) => this.#wrote(name, row),
|
|
556
556
|
// What the ward wrote is in the store when this resolves, and it says
|
|
557
557
|
// whether the store took it. The ward calls it at the end of every
|
|
558
558
|
// arrival, before it seals, so a call through a pointer has already
|
|
@@ -566,7 +566,7 @@ export class Harbor {
|
|
|
566
566
|
// A ward on its way out keeps nothing and refuses nothing: an arrival
|
|
567
567
|
// racing an unhost is not a store saying no, and saying so would be a
|
|
568
568
|
// lie in the one direction that matters.
|
|
569
|
-
|
|
569
|
+
kept: async () => {
|
|
570
570
|
const s = this.#saving.get(name);
|
|
571
571
|
if (!s || s.gone) return true;
|
|
572
572
|
const ok = await this.#flush(name);
|
|
@@ -585,7 +585,7 @@ export class Harbor {
|
|
|
585
585
|
// ward back. The ward is the one that knows, because a being reaching
|
|
586
586
|
// another being of her own ward goes to that ward's door directly and
|
|
587
587
|
// the harbor never sees that call at all.
|
|
588
|
-
|
|
588
|
+
during: () => {
|
|
589
589
|
const s = this.#saving.get(name);
|
|
590
590
|
if (!s) return () => {};
|
|
591
591
|
s.calls += 1;
|
|
@@ -603,9 +603,10 @@ export class Harbor {
|
|
|
603
603
|
else this.#settle(name, s);
|
|
604
604
|
};
|
|
605
605
|
},
|
|
606
|
+
},
|
|
606
607
|
};
|
|
607
|
-
const
|
|
608
|
-
if (
|
|
608
|
+
const box = await this.boxFor(name, kept.record);
|
|
609
|
+
if (box) ground.box = box;
|
|
609
610
|
// What the store holds for this ward at birth is what it was just handed
|
|
610
611
|
// or just read, row by row. It is copied once here so that a being whose
|
|
611
612
|
// very first write of this run is refused is put back too, rather than
|
|
@@ -633,7 +634,7 @@ export class Harbor {
|
|
|
633
634
|
// here. It answers nothing, because a caller inside the process reads
|
|
634
635
|
// the fault off the harbor and an arrival is the only thing that needs a
|
|
635
636
|
// verdict of its own.
|
|
636
|
-
const save = async (): Promise<void> => void (await ground.
|
|
637
|
+
const save = async (): Promise<void> => void (await ground.memory.kept());
|
|
637
638
|
const door = (bytes: Uint8Array) => w.door(bytes);
|
|
638
639
|
const ask = async (method?: string, args?: Record<string, unknown>) => {
|
|
639
640
|
if (saving.gone) return silence;
|
package/src/harbor/index.ts
CHANGED
|
@@ -11,7 +11,10 @@ export { MemoryStore, values, rowsOf, type Store, type Kept, type WardRecord } f
|
|
|
11
11
|
// a being, and the partition back from the rows it was cut into.
|
|
12
12
|
export { HEAD, fromRows, rowsIn } from '../ward/partition.ts';
|
|
13
13
|
export { request, Socket, SUITE, REFUSED, type Reach, type Carry, type Line, type Announce } from './reach.ts';
|
|
14
|
-
export type { Ground, WardPointers,
|
|
14
|
+
export type { Ground, WardPointers, Memory } from '../ward/ground.ts';
|
|
15
|
+
export { BOX, OFFER, RETRACT, volatile } from '../ward/ground.ts';
|
|
16
|
+
// The box as one being, and the acts a harbor runs to stand it.
|
|
17
|
+
export { Box, BOX_SEED, BOX_ASKS, stand, hold, offerTo, roll } from './box.ts';
|
|
15
18
|
// What a harbor builds a ground out of. Convenience, never contract: a kit
|
|
16
19
|
// writing its own harbor may write these three again.
|
|
17
20
|
export { maker, entropy, learnPk } from '../ward/ground.ts';
|
package/src/harbor/memory.ts
CHANGED
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
// keeping TWO pointers. Routes one ward pk to one door. Knows no being,
|
|
5
5
|
// reads no partition, opens no byte. Several memory harbors may be linked,
|
|
6
6
|
// which stands in for a wire, and cut, which stands in for weather.
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
//
|
|
8
|
+
// It works in acts, as every harbor does. `up` is act one: the box's own
|
|
9
|
+
// ward on the plain ground, the box's being and the lent beings in it.
|
|
10
|
+
// `host` is act two for one ward: on the roll, and handed one invitation
|
|
11
|
+
// on the box's being. A
|
|
12
|
+
// ward booted with `boot` alone is handed no box and lends nothing, which is
|
|
13
|
+
// what a suite of the ward wants.
|
|
14
|
+
import type { BeingClass, BeingLike, Invitation } from '../being/types.ts';
|
|
15
|
+
import { maker, entropy, learnPk, volatile, type Ground, type WardPointers } from '../ward/ground.ts';
|
|
9
16
|
import { hex } from '../ward/arithmetic.ts';
|
|
17
|
+
import { Box, BOX_SEED, stand, offerTo, roll } from './box.ts';
|
|
10
18
|
|
|
11
19
|
export type Booted = WardPointers & { pk: string };
|
|
12
20
|
export type WardFactory = (ground: Ground) => Promise<WardPointers>;
|
|
@@ -47,22 +55,22 @@ export class MemoryHarbor {
|
|
|
47
55
|
harbor.peers.add(this);
|
|
48
56
|
}
|
|
49
57
|
|
|
50
|
-
// boot by seed. the harbor gets a door and an ask, and learns the pk by
|
|
51
|
-
// `
|
|
52
|
-
//
|
|
53
|
-
async boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>,
|
|
58
|
+
// boot by seed. the harbor gets a door and an ask, and learns the pk by
|
|
59
|
+
// asking. `box` is the invitation on this device's box being, for this
|
|
60
|
+
// ward: left out, the ward holds no box and every lend is null.
|
|
61
|
+
async boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, box?: Invitation): Promise<Booted> {
|
|
54
62
|
this.partitions.set(seed, {});
|
|
55
|
-
return this.reboot(seed, Ward, classes,
|
|
63
|
+
return this.reboot(seed, Ward, classes, box);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
// a restart: same seed, same partition, fresh ward. the ward's files are the harbor's to keep.
|
|
59
|
-
async reboot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>,
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
67
|
+
async reboot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, box?: Invitation): Promise<Booted> {
|
|
68
|
+
const rows = this.partitions.get(seed);
|
|
69
|
+
if (!rows) throw new Error(`no partition for ${seed}`);
|
|
62
70
|
const ground: Ground = {
|
|
63
71
|
seed,
|
|
64
|
-
memory,
|
|
65
|
-
...(
|
|
72
|
+
memory: volatile(rows),
|
|
73
|
+
...(box ? { box } : {}),
|
|
66
74
|
// One objects map for the whole harbor, not one per ward: every ward
|
|
67
75
|
// here is in this process and a test reaches any being of any of them.
|
|
68
76
|
instantiate: maker(this.objects, classes),
|
|
@@ -80,4 +88,36 @@ export class MemoryHarbor {
|
|
|
80
88
|
this.wards.set(seed, booted);
|
|
81
89
|
return booted;
|
|
82
90
|
}
|
|
91
|
+
|
|
92
|
+
// Act one. The box's own ward, the box's being and the lent beings named,
|
|
93
|
+
// each booted under its name and held by her under it. Run again on a
|
|
94
|
+
// harbor whose box ward stands, it changes nothing.
|
|
95
|
+
async up(Ward: WardFactory, lent: Record<string, BeingClass> = {}): Promise<Booted> {
|
|
96
|
+
const classes: Record<string, BeingClass> = { Box };
|
|
97
|
+
for (const C of Object.values(lent)) classes[C.name] = C;
|
|
98
|
+
const box = this.wards.get(BOX_SEED) ?? (await this.boot(BOX_SEED, Ward, classes));
|
|
99
|
+
await stand(box, lent);
|
|
100
|
+
return box;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get box(): Booted | undefined {
|
|
104
|
+
return this.wards.get(BOX_SEED);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Act two, one ward: on the roll and handed one invitation on the box's being.
|
|
108
|
+
// A harbor with no box ward up hosts as `boot` does, with nothing.
|
|
109
|
+
async host(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>): Promise<Booted> {
|
|
110
|
+
const box = this.box;
|
|
111
|
+
const inv = box ? await offerTo(box, seed) : undefined;
|
|
112
|
+
return this.partitions.has(seed) ? this.reboot(seed, Ward, classes, inv) : this.boot(seed, Ward, classes, inv);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Act two, whole: the box's being says who is hosted, and each comes up.
|
|
116
|
+
async hosted(Ward: WardFactory, classes: Record<string, BeingClass>): Promise<string[]> {
|
|
117
|
+
const box = this.box;
|
|
118
|
+
if (!box) return [];
|
|
119
|
+
const seeds = await roll(box);
|
|
120
|
+
for (const seed of seeds) await this.host(seed, Ward, classes);
|
|
121
|
+
return seeds;
|
|
122
|
+
}
|
|
83
123
|
}
|
package/src/harbor/store.ts
CHANGED
|
@@ -46,7 +46,7 @@ export const values = (p: Record<string, unknown>): Record<string, unknown> => J
|
|
|
46
46
|
export const rowsOf = (partition: Record<string, unknown>, rows: readonly string[]): [string, Record<string, unknown> | undefined][] =>
|
|
47
47
|
rows.map((row) => {
|
|
48
48
|
const value = rowOf(partition as unknown as Partition, row);
|
|
49
|
-
return [row, value === undefined ? undefined : (values(value)
|
|
49
|
+
return [row, value === undefined ? undefined : (values(value))];
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
// One store's copy, brought up to date with the rows it was told about.
|
package/src/vector/cases.ts
CHANGED
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
import { Ward } from '../ward/ward.ts';
|
|
19
19
|
import * as seal from '../ward/seal.ts';
|
|
20
20
|
import { hex } from '../ward/arithmetic.ts';
|
|
21
|
+
import { digest } from '../being/digest.ts';
|
|
21
22
|
import type { Json, JsonObject } from '../being/types.ts';
|
|
22
23
|
import type { Booted } from '../harbor/memory.ts';
|
|
23
|
-
import { world, sealed, bound, secret, rnd, partitionDigest, Caller } from './world.ts';
|
|
24
|
+
import { world, sealed, bound, secret, rnd, drawn, partitionDigest, Caller } from './world.ts';
|
|
24
25
|
import type { World, Hand, Sealed } from './world.ts';
|
|
25
26
|
|
|
26
27
|
export type DoorRecord = {
|
|
@@ -33,9 +34,9 @@ export type DoorRecord = {
|
|
|
33
34
|
ask: string; // hex, exactly as the bytes arrive
|
|
34
35
|
reply: string; // hex, exactly as they leave
|
|
35
36
|
opens: Json | null; // what the reply opens to where the hand holds the lid's secret
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
wrote: boolean; // the
|
|
37
|
+
draws: number; // how many words of the stream were spent before the arrival
|
|
38
|
+
blueprint?: JsonObject; // the shape `seen` is the digest of, where a reply carries one
|
|
39
|
+
wrote: boolean; // the partition changed while the door judged. a refusal never sets it
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
// `pin` is where the case's own setup ends and the arrival begins. A record
|
|
@@ -50,21 +51,43 @@ export type Case = { case: string; name: string; state: string; ward: string; ar
|
|
|
50
51
|
|
|
51
52
|
// Stand one case: its world, its setup, its ask. What comes back is enough
|
|
52
53
|
// to knock, and enough for a verifier to check the hand before the door.
|
|
53
|
-
export async function stand(c: Case): Promise<{ w: World; before: string; arrival: Arrival }> {
|
|
54
|
+
export async function stand(c: Case): Promise<{ w: World; before: string; draws: number; arrival: Arrival }> {
|
|
54
55
|
const w = await world();
|
|
55
56
|
let before = await partitionDigest(w.b, c.ward);
|
|
56
57
|
const arrival = await c.arrive(w, async () => {
|
|
57
58
|
before = await partitionDigest(w.b, c.ward);
|
|
58
59
|
});
|
|
59
|
-
|
|
60
|
+
// Where the stream stands now, which is where the door is about to draw
|
|
61
|
+
// from. A kit that stood this case its own way sets its stream here.
|
|
62
|
+
return { w, before, draws: drawn(), arrival };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The blueprint a reply's `seen` was taken over. The host is the only being
|
|
66
|
+
// in this world whose shape reaches a stranger, and the asker is the id her
|
|
67
|
+
// heir was opened for, so there is one blueprint to find and it is checked
|
|
68
|
+
// against the digest before it is written down.
|
|
69
|
+
async function shown(w: World, seen: string): Promise<JsonObject | undefined> {
|
|
70
|
+
for (const id of Object.keys(w.host.cells.occupants)) {
|
|
71
|
+
const bp = w.host.describe({ id }) as unknown as JsonObject;
|
|
72
|
+
if ((await digest(bp)) === seen) return bp;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
async function record(c: Case): Promise<DoorRecord> {
|
|
63
|
-
const { w, before, arrival } = await stand(c);
|
|
78
|
+
const { w, before, draws, arrival } = await stand(c);
|
|
64
79
|
const judged = await arrival.door.door(arrival.ask.bytes);
|
|
65
80
|
const after = await partitionDigest(w.b, c.ward);
|
|
66
81
|
const opens = arrival.ask.open ? ((await arrival.ask.open(judged.bytes)) as Json | null) : null;
|
|
67
|
-
|
|
82
|
+
// A reply that carries `seen` carries a being's shape on the wire, which is
|
|
83
|
+
// the one place in this corpus where what a being answers reaches a
|
|
84
|
+
// stranger. So the blueprint that digest is taken over is written down
|
|
85
|
+
// beside it: a kit stands a being that answers exactly this to the empty
|
|
86
|
+
// ask, and its door's bytes are these bytes. Every other record is the
|
|
87
|
+
// door alone and needs no being at all.
|
|
88
|
+
const seen = opens !== null && typeof opens === 'object' && !Array.isArray(opens) ? (opens).seen : undefined;
|
|
89
|
+
const blueprint = typeof seen === 'string' ? await shown(w, seen) : undefined;
|
|
90
|
+
return { case: c.case, name: c.name, state: c.state, ward: c.ward, kind: arrival.kind, heard: judged.heard, draws, ask: hex(arrival.ask.bytes), reply: hex(judged.bytes), opens, ...(blueprint ? { blueprint } : {}), wrote: before !== after };
|
|
68
91
|
}
|
|
69
92
|
|
|
70
93
|
// One after another and never at once: the cases share one stream, and two
|
|
@@ -142,10 +165,15 @@ export const cases: Case[] = [
|
|
|
142
165
|
name: 'she is not there',
|
|
143
166
|
state: 'the ward restarted without her class: her heirs stand and she does not',
|
|
144
167
|
ward: 'B',
|
|
168
|
+
// Under the key she bound: the heir was spent by her knock and admits
|
|
169
|
+
// nothing now, so an arrival signed with its secret would be D6 and not
|
|
170
|
+
// this case. Her own key is what the door holds for her, and it is the
|
|
171
|
+
// key that hears the word.
|
|
145
172
|
arrive: async (w, pin) => {
|
|
173
|
+
const b = await bound(w);
|
|
146
174
|
const WB = await w.b.reboot('B', Ward, { Caller });
|
|
147
175
|
await pin();
|
|
148
|
-
return at(WB,
|
|
176
|
+
return at(WB, b.inv.heir!, 'word', { signer: b.own });
|
|
149
177
|
},
|
|
150
178
|
},
|
|
151
179
|
{
|
package/src/vector/world.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Nothing here draws from the device. A door's reply is sealed under a fresh
|
|
9
9
|
// ephemeral key and a fresh nonce, so the bytes are fixed only where the
|
|
10
10
|
// entropy is, and every draw in this file comes from one written-down stream.
|
|
11
|
-
// That is what lets `
|
|
11
|
+
// That is what lets `quo/vectors/door.json` be a corpus and not a
|
|
12
12
|
// sample, and what lets a kit stand in vector mode for a verifier that holds
|
|
13
13
|
// no key: the spec's chapter of that name is the contract this file keeps.
|
|
14
14
|
import { MemoryHarbor } from '../harbor/memory.ts';
|
|
@@ -23,10 +23,10 @@ import type { Answer, Asker, BeingClass, Blueprint, Invitation, Json, JsonObject
|
|
|
23
23
|
|
|
24
24
|
// ---- the stream
|
|
25
25
|
//
|
|
26
|
-
// SplitMix64
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
26
|
+
// SplitMix64, written out in the spec's vector-mode chapter with both
|
|
27
|
+
// multipliers and all three shifts, because a kit that reproduces it by
|
|
28
|
+
// recognising it is a kit that guessed. This is that, and it moves only
|
|
29
|
+
// when that does. It is
|
|
30
30
|
// not a cipher and is not meant to be one. Its whole job is that the bytes
|
|
31
31
|
// this file produces are the same bytes twice.
|
|
32
32
|
const GAMMA = 0x9e3779b97f4a7c15n;
|
|
@@ -50,12 +50,23 @@ export function splitmix64(seed: bigint): () => Uint8Array {
|
|
|
50
50
|
export const STREAM_SEED = 20250901n;
|
|
51
51
|
|
|
52
52
|
let draw = splitmix64(STREAM_SEED);
|
|
53
|
+
// How many sixty-four bit words have been spent since the stream was reset.
|
|
54
|
+
// A record carries this number at the moment of its arrival, so a kit that
|
|
55
|
+
// built the door's state its own way sets its stream here and draws what
|
|
56
|
+
// this one would have drawn. It is the whole of what the setup's history is
|
|
57
|
+
// worth to a stranger: not how the world was made, but where the stream
|
|
58
|
+
// stands when the door is about to answer.
|
|
59
|
+
let spent = 0;
|
|
60
|
+
export const drawn = (): number => spent;
|
|
53
61
|
// Entropy, from the stream and never from the device. Every signer secret,
|
|
54
62
|
// every ephemeral seed, every byte of noise and every key a ward in this
|
|
55
63
|
// world mints comes through here, in the order the code asks for it.
|
|
56
64
|
export const rnd = (n = 32): Uint8Array => {
|
|
57
65
|
const out = new Uint8Array(n);
|
|
58
|
-
for (let i = 0; i < n; i += 8)
|
|
66
|
+
for (let i = 0; i < n; i += 8) {
|
|
67
|
+
out.set(draw().subarray(0, Math.min(8, n - i)), i);
|
|
68
|
+
spent += 1;
|
|
69
|
+
}
|
|
59
70
|
return out;
|
|
60
71
|
};
|
|
61
72
|
|
|
@@ -154,6 +165,7 @@ let counted = 1000;
|
|
|
154
165
|
|
|
155
166
|
export async function world() {
|
|
156
167
|
draw = splitmix64(STREAM_SEED); // a fresh world is a fresh stream: two runs, one corpus
|
|
168
|
+
spent = 0;
|
|
157
169
|
counted = 1000;
|
|
158
170
|
const a = new MemoryHarbor(),
|
|
159
171
|
b = new MemoryHarbor();
|
package/src/ward/ground.ts
CHANGED
|
@@ -1,110 +1,99 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The ground. The one object a harbor passes a ward at birth
|
|
3
|
-
// the
|
|
4
|
-
// the ward itself knows no runtime.
|
|
2
|
+
// The ground. The one object a harbor passes a ward at birth, and the whole
|
|
3
|
+
// of the device to it: six things, and everything a runtime differs on
|
|
4
|
+
// arrives in one of them, which is why the ward itself knows no runtime.
|
|
5
|
+
//
|
|
6
|
+
// seed who the ward is. the pk derives from it and from nothing else
|
|
7
|
+
// random entropy. every key the ward mints is drawn from it
|
|
8
|
+
// memory what the ward remembers: one interface, a body per terrain
|
|
9
|
+
// instantiate the dna: a class name and a stance in, a being or nothing out
|
|
10
|
+
// carry the wire: a ward pk and sealed bytes in, sealed bytes or nothing out
|
|
11
|
+
// box the device, as one invitation on the box's own being
|
|
12
|
+
//
|
|
13
|
+
// The spec lists nine things and says a kit gathers them however its
|
|
14
|
+
// language gathers things. This kit gathers the partition, wrote, keep and
|
|
15
|
+
// calling into memory, because they are one concern with four moments, and
|
|
16
|
+
// a ward is handed one body and told how to use it rather than four calls
|
|
17
|
+
// it must keep in step.
|
|
5
18
|
import type { Stance, BeingLike, BeingClass, Invitation } from '../being/types.ts';
|
|
6
19
|
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
20
|
+
// Memory. Four moments, and they are the promise a body keeps:
|
|
21
|
+
//
|
|
22
|
+
// rows the values the ward holds and writes into directly. synchronous,
|
|
23
|
+
// always, because the door judges an arrival in one pass and a
|
|
24
|
+
// disk in the middle of it would be a door that waits on a device
|
|
25
|
+
// told the ward says it after every write, naming the row: a being by
|
|
26
|
+
// her key, or `HEAD` for everything that is not one being's.
|
|
27
|
+
// nothing comes back. a body that keeps the rows saves in the
|
|
28
|
+
// order it was told, so a being driven in process is kept the way
|
|
29
|
+
// one reached through a door is
|
|
30
|
+
// kept keep what has been written, now, and say whether it was. the
|
|
31
|
+
// ward asks it once at the end of an arrival, after the being has
|
|
32
|
+
// answered and before the answer is sealed, the last moment
|
|
33
|
+
// anything can still be said about it. false is a store that
|
|
34
|
+
// refused, and the ward says so with the word a failed ask already
|
|
35
|
+
// has: `threw` to an asker the door holds, silence to the ask
|
|
36
|
+
// pointer. never a reason
|
|
37
|
+
// during a call of this ward's has begun, and what comes back lowers it.
|
|
38
|
+
// the ward raises it before it reaches out and lowers it in a
|
|
39
|
+
// finally, so a call that threw lowers too, and lowering twice is
|
|
40
|
+
// lowering once. calls nest. a body may go on writing while it is
|
|
41
|
+
// raised; what it may not do is take a save made there as the
|
|
42
|
+
// point it puts a refused ward back to
|
|
11
43
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
44
|
+
// A body that keeps nothing has rows, is told and does nothing, answers yes,
|
|
45
|
+
// and raises nothing: `volatile` below. The ward has one path either way.
|
|
46
|
+
export type Memory = {
|
|
47
|
+
readonly rows: Record<string, unknown>;
|
|
48
|
+
told(row: string): void;
|
|
49
|
+
kept(): Promise<boolean>;
|
|
50
|
+
during(): () => void;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// The body that keeps nothing: the process is the memory.
|
|
54
|
+
export const volatile = (rows: Record<string, unknown> = {}): Memory => ({ rows, told: () => {}, kept: () => Promise.resolve(true), during: () => () => {} });
|
|
55
|
+
|
|
56
|
+
// The device is one standing. What a box can do is beings, in a ward its
|
|
57
|
+
// harbor booted and roots, and one being there, the box's own, holds a
|
|
58
|
+
// standing at each of them under the name it is lent as. The ground carries
|
|
59
|
+
// one invitation on her, minted for this ward alone; the ward takes it at
|
|
60
|
+
// boot under `BOX`, as its own first being, and from then on the device is
|
|
61
|
+
// reached the way anything is reached: what it offers is her describe, and a
|
|
62
|
+
// lend is the ask `OFFER` on that standing, naming what a being wants.
|
|
19
63
|
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
64
|
+
// She answers the lent being's own invitation, minted by that being on herself,
|
|
65
|
+
// and the ward knocks and takes it in the being's name. No root mints for a
|
|
66
|
+
// lend, and the invitation never reaches the being: it is the device's, a
|
|
67
|
+
// value she could copy is one she could hand to anyone, so the ward holds
|
|
68
|
+
// it and hands her the id. Which ward may have which name is the box's own
|
|
69
|
+
// gate, reading who asks, and a stranger's ward holds no standing at her.
|
|
24
70
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
export
|
|
71
|
+
// A harbor with no box leaves it out, and every lend is null. A ward that
|
|
72
|
+
// could not take it at boot, the box down or the invitation spent, boots all
|
|
73
|
+
// the same, lends nothing this run, and tries again at the next.
|
|
74
|
+
export { BOX } from '../being/lent.ts';
|
|
75
|
+
export const OFFER = 'offer';
|
|
76
|
+
// A lend that was offered and not taken leaves nothing: the ward says so to
|
|
77
|
+
// the box, naming the heir, and the lent being removes what she minted.
|
|
78
|
+
export const RETRACT = 'retract';
|
|
29
79
|
|
|
30
80
|
export type Ground = {
|
|
31
|
-
seed: string | Uint8Array;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// or `HEAD` for everything that is not one being's. It says so after every
|
|
36
|
-
// write, a key rotated, a relation taken, a cell she set, and says nothing
|
|
37
|
-
// else: this word is told, never asked, and nothing comes back from it. A
|
|
38
|
-
// harbor that keeps the partition saves in the order it was told, so a
|
|
39
|
-
// being driven in process is kept the way one reached through a door is.
|
|
40
|
-
// Whether any of it was kept is one question asked once, at the end of an
|
|
41
|
-
// arrival, and `keep` is where. A harbor that keeps nothing leaves both
|
|
42
|
-
// out.
|
|
43
|
-
//
|
|
44
|
-
// The row is named because a being's cells are the one part of a partition
|
|
45
|
-
// that grows without limit, and a harbor told only that something moved has
|
|
46
|
-
// to write down every being in the ward to be sure of one. What a harbor
|
|
47
|
-
// does with the name is its own: writing the whole partition on every word
|
|
48
|
-
// is correct, and slower.
|
|
49
|
-
wrote?: (row: string) => void;
|
|
50
|
-
// Keep what has been written, now, and say whether it was kept. The ward
|
|
51
|
-
// calls it once at the end of an arrival, after the being has answered and
|
|
52
|
-
// before the answer is sealed, which is the last moment anything can still
|
|
53
|
-
// be said about it: a reply sealed to an asker's lid cannot be unsaid, and
|
|
54
|
-
// only the ward can seal.
|
|
55
|
-
//
|
|
56
|
-
// False is a store that refused, and the ward says so with the word a
|
|
57
|
-
// failed ask already has: `threw` to an asker the door holds, silence to
|
|
58
|
-
// the ask pointer and to a stranger. It is never a reason; a full disk is
|
|
59
|
-
// no more the far side's business than any other insides of this device.
|
|
60
|
-
//
|
|
61
|
-
// A harbor that keeps nothing leaves it out, and so does one that would
|
|
62
|
-
// rather count a refusal than answer it: absent means the ward asks
|
|
63
|
-
// nothing and seals what the being said, which is what every harbor did
|
|
64
|
-
// before this existed. A harbor that writes it saves here and nowhere
|
|
65
|
-
// else for this arrival, since `wrote` has already named every row.
|
|
66
|
-
keep?: () => Promise<boolean>;
|
|
67
|
-
// A call of this ward's has begun, and what comes back says it has ended.
|
|
68
|
-
// The ward alone knows where a call begins: a being reaching another being,
|
|
69
|
-
// in this process or over the wire, is one relation and two halves, and she
|
|
70
|
-
// writes her side of it before the bytes go out and again when they come
|
|
71
|
-
// back. Between those two writes her rows are half done, and a harbor that
|
|
72
|
-
// wrote them and then had to put them back would stand the ward at a point
|
|
73
|
-
// no call ever stood at.
|
|
74
|
-
//
|
|
75
|
-
// It is told, and the one thing that comes back is how to lower it. The
|
|
76
|
-
// ward raises it before it reaches out and lowers it in a finally, so a
|
|
77
|
-
// call that threw lowers too, and lowering twice is lowering once. Calls
|
|
78
|
-
// nest: a ward is in flight while any of its beings is.
|
|
79
|
-
//
|
|
80
|
-
// A harbor is free to keep writing while it is raised, because the store is
|
|
81
|
-
// what survives a restart. What it may not do is take a save made there as
|
|
82
|
-
// the point it puts a refused ward back to. A harbor that keeps nothing
|
|
83
|
-
// leaves it out.
|
|
84
|
-
calling?: () => () => void;
|
|
81
|
+
seed: string | Uint8Array;
|
|
82
|
+
random(n: number): Uint8Array;
|
|
83
|
+
memory: Memory;
|
|
84
|
+
instantiate(className: string, stance: Stance): BeingLike | null;
|
|
85
85
|
// Sealed bytes to a ward pk. What comes back, or undefined.
|
|
86
86
|
//
|
|
87
87
|
// undefined is a promise, not a shrug: no door was reached, and nothing was
|
|
88
88
|
// delivered. The ward hands it to a being as unreached, which is the one
|
|
89
89
|
// answer that says asking again is safe, so a harbor may only return it
|
|
90
90
|
// when it knows the bytes never arrived: no reach for that pk, a socket
|
|
91
|
-
// that would not open, a link that is down.
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// answer at all in that case. The ward bounds every ask itself, and an
|
|
96
|
-
// answer that never comes inside that bound is `late`, which promises
|
|
97
|
-
// nothing either way. So a harbor may hold a shorter patience than the
|
|
98
|
-
// ward's for its own reasons, a socket it wants back or a queue it will
|
|
99
|
-
// not grow, and the two bounds never need to read each other: whichever
|
|
100
|
-
// ends first ends the ask, and each says only what it can honestly say.
|
|
91
|
+
// that would not open, a link that is down. A harbor that sent the bytes
|
|
92
|
+
// and then gave up waiting knows no such thing and must not answer at all:
|
|
93
|
+
// the ward bounds every ask itself, and an answer that never comes inside
|
|
94
|
+
// that bound is `late`, which promises nothing either way.
|
|
101
95
|
carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | undefined>;
|
|
102
|
-
|
|
103
|
-
// A standing at one of the harbor's own beings, by the name that harbor
|
|
104
|
-
// knows it under. Built per ward, so which ward may ask for which name is
|
|
105
|
-
// the harbor's own decision and a stranger's ward is lent nothing. A harbor
|
|
106
|
-
// with nothing to lend leaves it out, and every name answers false.
|
|
107
|
-
lend?: Lend;
|
|
96
|
+
box?: Invitation;
|
|
108
97
|
};
|
|
109
98
|
|
|
110
99
|
// What a ward hands back. Two pointers. The door answers bytes, always, and
|
|
@@ -117,11 +106,8 @@ export type WardPointers = {
|
|
|
117
106
|
ask(method?: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
118
107
|
};
|
|
119
108
|
|
|
120
|
-
// ---- what every harbor builds a ground out of.
|
|
121
|
-
//
|
|
122
|
-
// themselves apart: what a memory harbor and a real one differ on is the
|
|
123
|
-
// route and the store, and nothing here. A second kit writes its own harbor
|
|
124
|
-
// and may write these again; they are convenience, never contract.
|
|
109
|
+
// ---- what every harbor builds a ground out of. Convenience, never contract:
|
|
110
|
+
// a second kit writes its own harbor and may write these again.
|
|
125
111
|
|
|
126
112
|
// The code half of a ground, and the map back to what it made. A class is
|
|
127
113
|
// found by own key only, since `constructor` is a name Object lends every
|
|
@@ -140,18 +126,14 @@ export const maker =
|
|
|
140
126
|
return obj;
|
|
141
127
|
};
|
|
142
128
|
|
|
143
|
-
// Entropy, from the one place every terrain that runs Quo has it.
|
|
144
|
-
// ward mints is drawn from this, so a harbor that wants another source hands
|
|
145
|
-
// its own and nothing here has to know.
|
|
129
|
+
// Entropy, from the one place every terrain that runs Quo has it.
|
|
146
130
|
export const entropy = (n: number): Uint8Array => globalThis.crypto.getRandomValues(new Uint8Array(n));
|
|
147
131
|
|
|
148
132
|
// A ward's pk, learned the way anyone learns anything: by asking. The empty
|
|
149
133
|
// ask on the ask pointer is the ward's own describe and its notes carry the
|
|
150
134
|
// pk. A harbor has no other way to it and wants none: the ward mints it from
|
|
151
135
|
// the seed, and a harbor that read it off the seed itself would be a second
|
|
152
|
-
// derivation to keep in step with the first.
|
|
153
|
-
// else is not one this harbor can route to, and says so here rather than
|
|
154
|
-
// leaving an undefined pk in a directory.
|
|
136
|
+
// derivation to keep in step with the first.
|
|
155
137
|
export async function learnPk(w: WardPointers): Promise<string> {
|
|
156
138
|
const notes = (await w.ask()) as { notes?: { pk?: unknown } } | null;
|
|
157
139
|
const pk = notes?.notes?.pk;
|
package/src/ward/heirs.ts
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
import type { DoorWord } from '../being/types.ts';
|
|
8
8
|
import { GONE, type Heir, type Partition } from './partition.ts';
|
|
9
9
|
|
|
10
|
-
// How wide the span is, is the
|
|
11
|
-
// rough road, and
|
|
10
|
+
// How wide the span is, is the spec's and never a ward's. Wider is more
|
|
11
|
+
// forgiving of a rough road, and that is exactly why it is not a ward's to
|
|
12
|
+
// choose: a peer tells two doors apart by being refused at one and answered
|
|
13
|
+
// at the other, which is one relation dying over one number.
|
|
12
14
|
const SPAN = 64;
|
|
13
15
|
|
|
14
16
|
export class Heirs {
|