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/dist/harbor/core.js
CHANGED
|
@@ -78,20 +78,19 @@ export class Harbor {
|
|
|
78
78
|
for (const say of this.announcers)
|
|
79
79
|
say();
|
|
80
80
|
}
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
constructor(store, loader = async () => ({})
|
|
81
|
+
// The box, as one invitation per ward: what this device hands a ward it
|
|
82
|
+
// boots so that the ward takes the box's being as a standing. Set once
|
|
83
|
+
// the harbor's own ward is standing, which is the usual order: a terrain
|
|
84
|
+
// puts that ward up first, then boots the rest on a ground that can reach
|
|
85
|
+
// it. A terrain says which ward is handed one by overriding `boxFor`, and
|
|
86
|
+
// a stranger's ward is handed none.
|
|
87
|
+
offering;
|
|
88
|
+
constructor(store, loader = async () => ({})) {
|
|
89
89
|
this.store = store;
|
|
90
90
|
this.loader = loader;
|
|
91
|
-
this.lend = lend;
|
|
92
91
|
}
|
|
93
|
-
|
|
94
|
-
return this.
|
|
92
|
+
boxFor(name, _record) {
|
|
93
|
+
return this.offering?.(name) ?? Promise.resolve(undefined);
|
|
95
94
|
}
|
|
96
95
|
// Boot every ward the store keeps, and learn the hints. One ward that will
|
|
97
96
|
// not host, a partition this kit cannot read or a loader that throws, is
|
|
@@ -498,73 +497,75 @@ export class Harbor {
|
|
|
498
497
|
const objects = new WeakMap(); // cells -> the object instantiate made. a side's hand, never the ward's; it follows the cells out when she is unbooted
|
|
499
498
|
const ground = {
|
|
500
499
|
seed,
|
|
501
|
-
memory,
|
|
502
500
|
// This ward's own classes first, then the harbor's: a ward that names a
|
|
503
501
|
// class of its own is answered with hers.
|
|
504
502
|
instantiate: maker(objects, classes, this.classes),
|
|
505
503
|
carry: (pk, bytes) => this.carry(pk, new Uint8Array(bytes)),
|
|
506
504
|
random,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
505
|
+
memory: {
|
|
506
|
+
rows: memory,
|
|
507
|
+
told: (row) => this.#wrote(name, row),
|
|
508
|
+
// What the ward wrote is in the store when this resolves, and it says
|
|
509
|
+
// whether the store took it. The ward calls it at the end of every
|
|
510
|
+
// arrival, before it seals, so a call through a pointer has already
|
|
511
|
+
// waited for the store when it comes back: a restart right after one
|
|
512
|
+
// finds everything, and a refusal is that arrival's own answer rather
|
|
513
|
+
// than a number counted behind it. A ward that wrote nothing has
|
|
514
|
+
// nothing to save, so asking it what it holds is free, which is what
|
|
515
|
+
// makes the ask pointer the way to find out rather than an expensive
|
|
516
|
+
// one.
|
|
517
|
+
//
|
|
518
|
+
// A ward on its way out keeps nothing and refuses nothing: an arrival
|
|
519
|
+
// racing an unhost is not a store saying no, and saying so would be a
|
|
520
|
+
// lie in the one direction that matters.
|
|
521
|
+
kept: async () => {
|
|
522
|
+
const s = this.#saving.get(name);
|
|
523
|
+
if (!s || s.gone)
|
|
524
|
+
return true;
|
|
525
|
+
const ok = await this.#flush(name);
|
|
526
|
+
// A refusal is heard once, by the first arrival to ask after it: the
|
|
527
|
+
// save that met it may have been a timer's, which had nobody to tell.
|
|
528
|
+
// Under a call it is left standing, so the call that carried the
|
|
529
|
+
// refused write is refused too, and the outermost arrival hears it.
|
|
530
|
+
const no = !ok || s.fault;
|
|
531
|
+
s.fault = false; // heard. the next arrival is answered by the store and not by what happened before it
|
|
532
|
+
return !no;
|
|
533
|
+
},
|
|
534
|
+
// The ward says a call is in flight, and says when it ends. The harbor
|
|
535
|
+
// goes on writing under it; what it does not do is take a save made
|
|
536
|
+
// there as the point a refused ward is put back to, and a refusal that
|
|
537
|
+
// lands under a call waits here for the call to end before it puts the
|
|
538
|
+
// ward back. The ward is the one that knows, because a being reaching
|
|
539
|
+
// another being of her own ward goes to that ward's door directly and
|
|
540
|
+
// the harbor never sees that call at all.
|
|
541
|
+
during: () => {
|
|
542
|
+
const s = this.#saving.get(name);
|
|
543
|
+
if (!s)
|
|
544
|
+
return () => { };
|
|
545
|
+
s.calls += 1;
|
|
546
|
+
let lowered = false;
|
|
547
|
+
return () => {
|
|
548
|
+
if (lowered)
|
|
549
|
+
return;
|
|
550
|
+
lowered = true;
|
|
551
|
+
s.calls -= 1;
|
|
552
|
+
if (s.calls > 0 || s.gone)
|
|
553
|
+
return;
|
|
554
|
+
// The moment the ward is whole again: the point catches up with the
|
|
555
|
+
// call that just ended, or a put back that was owed is done now, so
|
|
556
|
+
// a being stands at what is kept before her next word rather than
|
|
557
|
+
// at the next save.
|
|
558
|
+
if (s.back)
|
|
559
|
+
void this.#flush(name);
|
|
560
|
+
else
|
|
561
|
+
this.#settle(name, s);
|
|
562
|
+
};
|
|
563
|
+
},
|
|
563
564
|
},
|
|
564
565
|
};
|
|
565
|
-
const
|
|
566
|
-
if (
|
|
567
|
-
ground.
|
|
566
|
+
const box = await this.boxFor(name, kept.record);
|
|
567
|
+
if (box)
|
|
568
|
+
ground.box = box;
|
|
568
569
|
// What the store holds for this ward at birth is what it was just handed
|
|
569
570
|
// or just read, row by row. It is copied once here so that a being whose
|
|
570
571
|
// very first write of this run is refused is put back too, rather than
|
|
@@ -593,7 +594,7 @@ export class Harbor {
|
|
|
593
594
|
// here. It answers nothing, because a caller inside the process reads
|
|
594
595
|
// the fault off the harbor and an arrival is the only thing that needs a
|
|
595
596
|
// verdict of its own.
|
|
596
|
-
const save = async () => void (await ground.
|
|
597
|
+
const save = async () => void (await ground.memory.kept());
|
|
597
598
|
const door = (bytes) => w.door(bytes);
|
|
598
599
|
const ask = async (method, args) => {
|
|
599
600
|
if (saving.gone)
|
package/dist/harbor/index.d.ts
CHANGED
|
@@ -4,5 +4,7 @@ export { dial, type Dialer } from './dial.ts';
|
|
|
4
4
|
export { MemoryStore, values, rowsOf, type Store, type Kept, type WardRecord } from './store.ts';
|
|
5
5
|
export { HEAD, fromRows, rowsIn } from '../ward/partition.ts';
|
|
6
6
|
export { request, Socket, SUITE, REFUSED, type Reach, type Carry, type Line, type Announce } from './reach.ts';
|
|
7
|
-
export type { Ground, WardPointers,
|
|
7
|
+
export type { Ground, WardPointers, Memory } from '../ward/ground.ts';
|
|
8
|
+
export { BOX, OFFER, RETRACT, volatile } from '../ward/ground.ts';
|
|
9
|
+
export { Box, BOX_SEED, BOX_ASKS, stand, hold, offerTo, roll } from './box.ts';
|
|
8
10
|
export { maker, entropy, learnPk } from '../ward/ground.ts';
|
package/dist/harbor/index.js
CHANGED
|
@@ -11,6 +11,9 @@ export { MemoryStore, values, rowsOf } from './store.js';
|
|
|
11
11
|
// a being, and the partition back from the rows it was cut into.
|
|
12
12
|
export { HEAD, fromRows, rowsIn } from '../ward/partition.js';
|
|
13
13
|
export { request, Socket, SUITE, REFUSED } from './reach.js';
|
|
14
|
+
export { BOX, OFFER, RETRACT, volatile } from '../ward/ground.js';
|
|
15
|
+
// The box as one being, and the acts a harbor runs to stand it.
|
|
16
|
+
export { Box, BOX_SEED, BOX_ASKS, stand, hold, offerTo, roll } from './box.js';
|
|
14
17
|
// What a harbor builds a ground out of. Convenience, never contract: a kit
|
|
15
18
|
// writing its own harbor may write these three again.
|
|
16
19
|
export { maker, entropy, learnPk } from '../ward/ground.js';
|
package/dist/harbor/memory.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { BeingClass, BeingLike } from '../being/types.ts';
|
|
2
|
-
import { type Ground, type WardPointers
|
|
1
|
+
import type { BeingClass, BeingLike, Invitation } from '../being/types.ts';
|
|
2
|
+
import { type Ground, type WardPointers } from '../ward/ground.ts';
|
|
3
3
|
export type Booted = WardPointers & {
|
|
4
4
|
pk: string;
|
|
5
5
|
};
|
|
@@ -20,6 +20,10 @@ export declare class MemoryHarbor {
|
|
|
20
20
|
random: Ground['random'];
|
|
21
21
|
route(farPk: string, bytes: Uint8Array): Promise<Uint8Array | undefined>;
|
|
22
22
|
link(harbor: MemoryHarbor): void;
|
|
23
|
-
boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>,
|
|
24
|
-
reboot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>,
|
|
23
|
+
boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, box?: Invitation): Promise<Booted>;
|
|
24
|
+
reboot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, box?: Invitation): Promise<Booted>;
|
|
25
|
+
up(Ward: WardFactory, lent?: Record<string, BeingClass>): Promise<Booted>;
|
|
26
|
+
get box(): Booted | undefined;
|
|
27
|
+
host(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>): Promise<Booted>;
|
|
28
|
+
hosted(Ward: WardFactory, classes: Record<string, BeingClass>): Promise<string[]>;
|
|
25
29
|
}
|
package/dist/harbor/memory.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { maker, entropy, learnPk } from '../ward/ground.js';
|
|
1
|
+
import { maker, entropy, learnPk, volatile } from '../ward/ground.js';
|
|
2
2
|
import { hex } from '../ward/arithmetic.js';
|
|
3
|
+
import { Box, BOX_SEED, stand, offerTo, roll } from './box.js';
|
|
3
4
|
export class MemoryHarbor {
|
|
4
5
|
doors = new Map(); // ward pk -> door
|
|
5
6
|
down = new Set(); // ward pks this harbor cannot reach right now
|
|
@@ -36,22 +37,22 @@ export class MemoryHarbor {
|
|
|
36
37
|
this.peers.add(harbor);
|
|
37
38
|
harbor.peers.add(this);
|
|
38
39
|
}
|
|
39
|
-
// boot by seed. the harbor gets a door and an ask, and learns the pk by
|
|
40
|
-
// `
|
|
41
|
-
//
|
|
42
|
-
async boot(seed, Ward, classes,
|
|
40
|
+
// boot by seed. the harbor gets a door and an ask, and learns the pk by
|
|
41
|
+
// asking. `box` is the invitation on this device's box being, for this
|
|
42
|
+
// ward: left out, the ward holds no box and every lend is null.
|
|
43
|
+
async boot(seed, Ward, classes, box) {
|
|
43
44
|
this.partitions.set(seed, {});
|
|
44
|
-
return this.reboot(seed, Ward, classes,
|
|
45
|
+
return this.reboot(seed, Ward, classes, box);
|
|
45
46
|
}
|
|
46
47
|
// a restart: same seed, same partition, fresh ward. the ward's files are the harbor's to keep.
|
|
47
|
-
async reboot(seed, Ward, classes,
|
|
48
|
-
const
|
|
49
|
-
if (!
|
|
48
|
+
async reboot(seed, Ward, classes, box) {
|
|
49
|
+
const rows = this.partitions.get(seed);
|
|
50
|
+
if (!rows)
|
|
50
51
|
throw new Error(`no partition for ${seed}`);
|
|
51
52
|
const ground = {
|
|
52
53
|
seed,
|
|
53
|
-
memory,
|
|
54
|
-
...(
|
|
54
|
+
memory: volatile(rows),
|
|
55
|
+
...(box ? { box } : {}),
|
|
55
56
|
// One objects map for the whole harbor, not one per ward: every ward
|
|
56
57
|
// here is in this process and a test reaches any being of any of them.
|
|
57
58
|
instantiate: maker(this.objects, classes),
|
|
@@ -69,4 +70,35 @@ export class MemoryHarbor {
|
|
|
69
70
|
this.wards.set(seed, booted);
|
|
70
71
|
return booted;
|
|
71
72
|
}
|
|
73
|
+
// Act one. The box's own ward, the box's being and the lent beings named,
|
|
74
|
+
// each booted under its name and held by her under it. Run again on a
|
|
75
|
+
// harbor whose box ward stands, it changes nothing.
|
|
76
|
+
async up(Ward, lent = {}) {
|
|
77
|
+
const classes = { Box };
|
|
78
|
+
for (const C of Object.values(lent))
|
|
79
|
+
classes[C.name] = C;
|
|
80
|
+
const box = this.wards.get(BOX_SEED) ?? (await this.boot(BOX_SEED, Ward, classes));
|
|
81
|
+
await stand(box, lent);
|
|
82
|
+
return box;
|
|
83
|
+
}
|
|
84
|
+
get box() {
|
|
85
|
+
return this.wards.get(BOX_SEED);
|
|
86
|
+
}
|
|
87
|
+
// Act two, one ward: on the roll and handed one invitation on the box's being.
|
|
88
|
+
// A harbor with no box ward up hosts as `boot` does, with nothing.
|
|
89
|
+
async host(seed, Ward, classes) {
|
|
90
|
+
const box = this.box;
|
|
91
|
+
const inv = box ? await offerTo(box, seed) : undefined;
|
|
92
|
+
return this.partitions.has(seed) ? this.reboot(seed, Ward, classes, inv) : this.boot(seed, Ward, classes, inv);
|
|
93
|
+
}
|
|
94
|
+
// Act two, whole: the box's being says who is hosted, and each comes up.
|
|
95
|
+
async hosted(Ward, classes) {
|
|
96
|
+
const box = this.box;
|
|
97
|
+
if (!box)
|
|
98
|
+
return [];
|
|
99
|
+
const seeds = await roll(box);
|
|
100
|
+
for (const seed of seeds)
|
|
101
|
+
await this.host(seed, Ward, classes);
|
|
102
|
+
return seeds;
|
|
103
|
+
}
|
|
72
104
|
}
|
package/dist/harbor/store.js
CHANGED
|
@@ -18,7 +18,7 @@ export const values = (p) => JSON.parse(JSON.stringify(p));
|
|
|
18
18
|
// is the store dropping what it holds for her.
|
|
19
19
|
export const rowsOf = (partition, rows) => rows.map((row) => {
|
|
20
20
|
const value = rowOf(partition, row);
|
|
21
|
-
return [row, value === undefined ? undefined : values(value)];
|
|
21
|
+
return [row, value === undefined ? undefined : (values(value))];
|
|
22
22
|
});
|
|
23
23
|
// One store's copy, brought up to date with the rows it was told about.
|
|
24
24
|
const keep = (row, partition, rows) => {
|
package/dist/vector/cases.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Json } from '../being/types.ts';
|
|
1
|
+
import type { Json, JsonObject } from '../being/types.ts';
|
|
2
2
|
import type { Booted } from '../harbor/memory.ts';
|
|
3
3
|
import type { World, Sealed } from './world.ts';
|
|
4
4
|
export type DoorRecord = {
|
|
@@ -11,8 +11,8 @@ export type DoorRecord = {
|
|
|
11
11
|
ask: string;
|
|
12
12
|
reply: string;
|
|
13
13
|
opens: Json | null;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
draws: number;
|
|
15
|
+
blueprint?: JsonObject;
|
|
16
16
|
wrote: boolean;
|
|
17
17
|
};
|
|
18
18
|
export type Pin = () => Promise<void>;
|
|
@@ -34,6 +34,7 @@ export type Case = {
|
|
|
34
34
|
export declare function stand(c: Case): Promise<{
|
|
35
35
|
w: World;
|
|
36
36
|
before: string;
|
|
37
|
+
draws: number;
|
|
37
38
|
arrival: Arrival;
|
|
38
39
|
}>;
|
|
39
40
|
export declare function doorCases(): Promise<DoorRecord[]>;
|
package/dist/vector/cases.js
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
import { Ward } from '../ward/ward.js';
|
|
19
19
|
import * as seal from '../ward/seal.js';
|
|
20
20
|
import { hex } from '../ward/arithmetic.js';
|
|
21
|
-
import {
|
|
21
|
+
import { digest } from '../being/digest.js';
|
|
22
|
+
import { world, sealed, bound, secret, rnd, drawn, partitionDigest, Caller } from './world.js';
|
|
22
23
|
// Stand one case: its world, its setup, its ask. What comes back is enough
|
|
23
24
|
// to knock, and enough for a verifier to check the hand before the door.
|
|
24
25
|
export async function stand(c) {
|
|
@@ -27,14 +28,36 @@ export async function stand(c) {
|
|
|
27
28
|
const arrival = await c.arrive(w, async () => {
|
|
28
29
|
before = await partitionDigest(w.b, c.ward);
|
|
29
30
|
});
|
|
30
|
-
|
|
31
|
+
// Where the stream stands now, which is where the door is about to draw
|
|
32
|
+
// from. A kit that stood this case its own way sets its stream here.
|
|
33
|
+
return { w, before, draws: drawn(), arrival };
|
|
34
|
+
}
|
|
35
|
+
// The blueprint a reply's `seen` was taken over. The host is the only being
|
|
36
|
+
// in this world whose shape reaches a stranger, and the asker is the id her
|
|
37
|
+
// heir was opened for, so there is one blueprint to find and it is checked
|
|
38
|
+
// against the digest before it is written down.
|
|
39
|
+
async function shown(w, seen) {
|
|
40
|
+
for (const id of Object.keys(w.host.cells.occupants)) {
|
|
41
|
+
const bp = w.host.describe({ id });
|
|
42
|
+
if ((await digest(bp)) === seen)
|
|
43
|
+
return bp;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
31
46
|
}
|
|
32
47
|
async function record(c) {
|
|
33
|
-
const { w, before, arrival } = await stand(c);
|
|
48
|
+
const { w, before, draws, arrival } = await stand(c);
|
|
34
49
|
const judged = await arrival.door.door(arrival.ask.bytes);
|
|
35
50
|
const after = await partitionDigest(w.b, c.ward);
|
|
36
51
|
const opens = arrival.ask.open ? (await arrival.ask.open(judged.bytes)) : null;
|
|
37
|
-
|
|
52
|
+
// A reply that carries `seen` carries a being's shape on the wire, which is
|
|
53
|
+
// the one place in this corpus where what a being answers reaches a
|
|
54
|
+
// stranger. So the blueprint that digest is taken over is written down
|
|
55
|
+
// beside it: a kit stands a being that answers exactly this to the empty
|
|
56
|
+
// ask, and its door's bytes are these bytes. Every other record is the
|
|
57
|
+
// door alone and needs no being at all.
|
|
58
|
+
const seen = opens !== null && typeof opens === 'object' && !Array.isArray(opens) ? (opens).seen : undefined;
|
|
59
|
+
const blueprint = typeof seen === 'string' ? await shown(w, seen) : undefined;
|
|
60
|
+
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 };
|
|
38
61
|
}
|
|
39
62
|
// One after another and never at once: the cases share one stream, and two
|
|
40
63
|
// of them in flight together would draw from it in whatever order the
|
|
@@ -108,10 +131,15 @@ export const cases = [
|
|
|
108
131
|
name: 'she is not there',
|
|
109
132
|
state: 'the ward restarted without her class: her heirs stand and she does not',
|
|
110
133
|
ward: 'B',
|
|
134
|
+
// Under the key she bound: the heir was spent by her knock and admits
|
|
135
|
+
// nothing now, so an arrival signed with its secret would be D6 and not
|
|
136
|
+
// this case. Her own key is what the door holds for her, and it is the
|
|
137
|
+
// key that hears the word.
|
|
111
138
|
arrive: async (w, pin) => {
|
|
139
|
+
const b = await bound(w);
|
|
112
140
|
const WB = await w.b.reboot('B', Ward, { Caller });
|
|
113
141
|
await pin();
|
|
114
|
-
return at(WB,
|
|
142
|
+
return at(WB, b.inv.heir, 'word', { signer: b.own });
|
|
115
143
|
},
|
|
116
144
|
},
|
|
117
145
|
{
|
package/dist/vector/world.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Being } from '../being/being.ts';
|
|
|
5
5
|
import type { Answer, Asker, BeingClass, Blueprint, Invitation, JsonObject, Reply, Wanted } from '../being/types.ts';
|
|
6
6
|
export declare function splitmix64(seed: bigint): () => Uint8Array;
|
|
7
7
|
export declare const STREAM_SEED = 20250901n;
|
|
8
|
+
export declare const drawn: () => number;
|
|
8
9
|
export declare const rnd: (n?: number) => Uint8Array;
|
|
9
10
|
export declare const must: (ok: boolean, what: string) => void;
|
|
10
11
|
export declare const P: (h: MemoryHarbor, seed: string) => Partition;
|
package/dist/vector/world.js
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.js';
|
|
@@ -20,10 +20,10 @@ import { silence, unreached, isSilence, isUnreached, told } from '../being/silen
|
|
|
20
20
|
import { digest } from '../being/digest.js';
|
|
21
21
|
// ---- the stream
|
|
22
22
|
//
|
|
23
|
-
// SplitMix64
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
23
|
+
// SplitMix64, written out in the spec's vector-mode chapter with both
|
|
24
|
+
// multipliers and all three shifts, because a kit that reproduces it by
|
|
25
|
+
// recognising it is a kit that guessed. This is that, and it moves only
|
|
26
|
+
// when that does. It is
|
|
27
27
|
// not a cipher and is not meant to be one. Its whole job is that the bytes
|
|
28
28
|
// this file produces are the same bytes twice.
|
|
29
29
|
const GAMMA = 0x9e3779b97f4a7c15n;
|
|
@@ -46,13 +46,23 @@ export function splitmix64(seed) {
|
|
|
46
46
|
// stream to when it reproduces `door.json`.
|
|
47
47
|
export const STREAM_SEED = 20250901n;
|
|
48
48
|
let draw = splitmix64(STREAM_SEED);
|
|
49
|
+
// How many sixty-four bit words have been spent since the stream was reset.
|
|
50
|
+
// A record carries this number at the moment of its arrival, so a kit that
|
|
51
|
+
// built the door's state its own way sets its stream here and draws what
|
|
52
|
+
// this one would have drawn. It is the whole of what the setup's history is
|
|
53
|
+
// worth to a stranger: not how the world was made, but where the stream
|
|
54
|
+
// stands when the door is about to answer.
|
|
55
|
+
let spent = 0;
|
|
56
|
+
export const drawn = () => spent;
|
|
49
57
|
// Entropy, from the stream and never from the device. Every signer secret,
|
|
50
58
|
// every ephemeral seed, every byte of noise and every key a ward in this
|
|
51
59
|
// world mints comes through here, in the order the code asks for it.
|
|
52
60
|
export const rnd = (n = 32) => {
|
|
53
61
|
const out = new Uint8Array(n);
|
|
54
|
-
for (let i = 0; i < n; i += 8)
|
|
62
|
+
for (let i = 0; i < n; i += 8) {
|
|
55
63
|
out.set(draw().subarray(0, Math.min(8, n - i)), i);
|
|
64
|
+
spent += 1;
|
|
65
|
+
}
|
|
56
66
|
return out;
|
|
57
67
|
};
|
|
58
68
|
// A claim the world stands on. It throws, because a world that is not the
|
|
@@ -149,6 +159,7 @@ export const classes = { Host, Caller };
|
|
|
149
159
|
let counted = 1000;
|
|
150
160
|
export async function world() {
|
|
151
161
|
draw = splitmix64(STREAM_SEED); // a fresh world is a fresh stream: two runs, one corpus
|
|
162
|
+
spent = 0;
|
|
152
163
|
counted = 1000;
|
|
153
164
|
const a = new MemoryHarbor(), b = new MemoryHarbor();
|
|
154
165
|
a.random = rnd;
|
package/dist/ward/ground.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import type { Stance, BeingLike, BeingClass, Invitation } from '../being/types.ts';
|
|
2
|
-
export type
|
|
2
|
+
export type Memory = {
|
|
3
|
+
readonly rows: Record<string, unknown>;
|
|
4
|
+
told(row: string): void;
|
|
5
|
+
kept(): Promise<boolean>;
|
|
6
|
+
during(): () => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const volatile: (rows?: Record<string, unknown>) => Memory;
|
|
9
|
+
export { BOX } from '../being/lent.ts';
|
|
10
|
+
export declare const OFFER = "offer";
|
|
11
|
+
export declare const RETRACT = "retract";
|
|
3
12
|
export type Ground = {
|
|
4
13
|
seed: string | Uint8Array;
|
|
5
|
-
|
|
14
|
+
random(n: number): Uint8Array;
|
|
15
|
+
memory: Memory;
|
|
6
16
|
instantiate(className: string, stance: Stance): BeingLike | null;
|
|
7
|
-
wrote?: (row: string) => void;
|
|
8
|
-
keep?: () => Promise<boolean>;
|
|
9
|
-
calling?: () => () => void;
|
|
10
17
|
carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | undefined>;
|
|
11
|
-
|
|
12
|
-
lend?: Lend;
|
|
18
|
+
box?: Invitation;
|
|
13
19
|
};
|
|
14
20
|
export type WardPointers = {
|
|
15
21
|
door(bytes: Uint8Array): Promise<{
|
package/dist/ward/ground.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// The body that keeps nothing: the process is the memory.
|
|
2
|
+
export const volatile = (rows = {}) => ({ rows, told: () => { }, kept: () => Promise.resolve(true), during: () => () => { } });
|
|
3
|
+
// The device is one standing. What a box can do is beings, in a ward its
|
|
4
|
+
// harbor booted and roots, and one being there, the box's own, holds a
|
|
5
|
+
// standing at each of them under the name it is lent as. The ground carries
|
|
6
|
+
// one invitation on her, minted for this ward alone; the ward takes it at
|
|
7
|
+
// boot under `BOX`, as its own first being, and from then on the device is
|
|
8
|
+
// reached the way anything is reached: what it offers is her describe, and a
|
|
9
|
+
// lend is the ask `OFFER` on that standing, naming what a being wants.
|
|
10
|
+
//
|
|
11
|
+
// She answers the lent being's own invitation, minted by that being on herself,
|
|
12
|
+
// and the ward knocks and takes it in the being's name. No root mints for a
|
|
13
|
+
// lend, and the invitation never reaches the being: it is the device's, a
|
|
14
|
+
// value she could copy is one she could hand to anyone, so the ward holds
|
|
15
|
+
// it and hands her the id. Which ward may have which name is the box's own
|
|
16
|
+
// gate, reading who asks, and a stranger's ward holds no standing at her.
|
|
17
|
+
//
|
|
18
|
+
// A harbor with no box leaves it out, and every lend is null. A ward that
|
|
19
|
+
// could not take it at boot, the box down or the invitation spent, boots all
|
|
20
|
+
// the same, lends nothing this run, and tries again at the next.
|
|
21
|
+
export { BOX } from '../being/lent.js';
|
|
22
|
+
export const OFFER = 'offer';
|
|
23
|
+
// A lend that was offered and not taken leaves nothing: the ward says so to
|
|
24
|
+
// the box, naming the heir, and the lent being removes what she minted.
|
|
25
|
+
export const RETRACT = 'retract';
|
|
26
|
+
// ---- what every harbor builds a ground out of. Convenience, never contract:
|
|
27
|
+
// a second kit writes its own harbor and may write these again.
|
|
6
28
|
// The code half of a ground, and the map back to what it made. A class is
|
|
7
29
|
// found by own key only, since `constructor` is a name Object lends every
|
|
8
30
|
// registry, and the first registry holding the name wins, so a ward's own
|
|
@@ -18,17 +40,13 @@ export const maker = (objects, ...registries) => (className, stance) => {
|
|
|
18
40
|
objects.set(stance.cells, obj);
|
|
19
41
|
return obj;
|
|
20
42
|
};
|
|
21
|
-
// Entropy, from the one place every terrain that runs Quo has it.
|
|
22
|
-
// ward mints is drawn from this, so a harbor that wants another source hands
|
|
23
|
-
// its own and nothing here has to know.
|
|
43
|
+
// Entropy, from the one place every terrain that runs Quo has it.
|
|
24
44
|
export const entropy = (n) => globalThis.crypto.getRandomValues(new Uint8Array(n));
|
|
25
45
|
// A ward's pk, learned the way anyone learns anything: by asking. The empty
|
|
26
46
|
// ask on the ask pointer is the ward's own describe and its notes carry the
|
|
27
47
|
// pk. A harbor has no other way to it and wants none: the ward mints it from
|
|
28
48
|
// the seed, and a harbor that read it off the seed itself would be a second
|
|
29
|
-
// derivation to keep in step with the first.
|
|
30
|
-
// else is not one this harbor can route to, and says so here rather than
|
|
31
|
-
// leaving an undefined pk in a directory.
|
|
49
|
+
// derivation to keep in step with the first.
|
|
32
50
|
export async function learnPk(w) {
|
|
33
51
|
const notes = (await w.ask());
|
|
34
52
|
const pk = notes?.notes?.pk;
|
package/dist/ward/heirs.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { GONE } from './partition.js';
|
|
2
|
-
// How wide the span is, is the
|
|
3
|
-
// rough road, and
|
|
2
|
+
// How wide the span is, is the spec's and never a ward's. Wider is more
|
|
3
|
+
// forgiving of a rough road, and that is exactly why it is not a ward's to
|
|
4
|
+
// choose: a peer tells two doors apart by being refused at one and answered
|
|
5
|
+
// at the other, which is one relation dying over one number.
|
|
4
6
|
const SPAN = 64;
|
|
5
7
|
export class Heirs {
|
|
6
8
|
#p;
|
package/dist/ward/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Ward } from './ward.ts';
|
|
2
|
-
export type { Ground, WardPointers,
|
|
2
|
+
export type { Ground, WardPointers, Memory } from './ground.ts';
|
|
3
|
+
export { BOX, OFFER, RETRACT, volatile } from './ground.ts';
|
|
3
4
|
export type { Partition, Heir, Bind, StandingKeys } from './partition.ts';
|
|
4
5
|
export { GONE, MINTED, KNOCKS } from './partition.ts';
|
|
5
6
|
export type { AskPayload, ReplyPayload } from './seal.ts';
|
package/dist/ward/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// nervur/ward — one function. Every ward is the same ward.
|
|
3
3
|
export { Ward } from './ward.js';
|
|
4
|
+
export { BOX, OFFER, RETRACT, volatile } from './ground.js';
|
|
4
5
|
// The three bounds on what a partition may hold, for a kit that must keep
|
|
5
6
|
// the same ones and for a test that pins them.
|
|
6
7
|
export { GONE, MINTED, KNOCKS } from './partition.js';
|
package/dist/ward/partition.js
CHANGED
|
@@ -250,7 +250,7 @@ export const restoreRow = (p, row, value) => {
|
|
|
250
250
|
drop(p.beings ?? {}, row);
|
|
251
251
|
// Her bind goes with her: a being the store never kept has no relation
|
|
252
252
|
// anybody is standing on, so there is nothing to leave forward.
|
|
253
|
-
drop(
|
|
253
|
+
drop(p.bind ?? {}, row);
|
|
254
254
|
return;
|
|
255
255
|
}
|
|
256
256
|
// Behind the guard and never through it: a being's cells in a partition
|
|
@@ -267,7 +267,7 @@ export const restoreRow = (p, row, value) => {
|
|
|
267
267
|
for (const [k, v] of Object.entries(kept))
|
|
268
268
|
put(held, k, v);
|
|
269
269
|
};
|
|
270
|
-
into(
|
|
270
|
+
into(p.beings ?? {}, (value.cells ?? emptyCells()));
|
|
271
271
|
};
|
|
272
272
|
// The partition back from its rows. A head that is absent is a partition
|
|
273
273
|
// nobody has written, and `open` makes an empty one of it.
|
package/dist/ward/seal.js
CHANGED
|
@@ -26,7 +26,7 @@ export const SIZE = 1024 * 1024;
|
|
|
26
26
|
// designs and no separation at all: one secret would be doing two jobs with
|
|
27
27
|
// nothing said about it, and a second kit would have to reproduce a
|
|
28
28
|
// construction nobody named. HKDF-SHA-256 under a label is the separation
|
|
29
|
-
// said out loud, and it is what `
|
|
29
|
+
// said out loud, and it is what `quo/vectors/framing.json` pins.
|
|
30
30
|
//
|
|
31
31
|
// Bytes are key material and text is not. A seed handed in as bytes of the
|
|
32
32
|
// key length is taken as it stands, which is what a harbor mints; anything
|