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.
Files changed (53) hide show
  1. package/README.md +7 -7
  2. package/dist/being/index.d.ts +1 -0
  3. package/dist/being/index.js +1 -0
  4. package/dist/being/lent.d.ts +32 -0
  5. package/dist/being/lent.js +63 -0
  6. package/dist/being/types.js +1 -1
  7. package/dist/conformance/beings.d.ts +2 -1
  8. package/dist/conformance/beings.js +7 -2
  9. package/dist/conformance/index.d.ts +0 -1
  10. package/dist/conformance/index.js +8 -5
  11. package/dist/harbor/box.d.ts +121 -0
  12. package/dist/harbor/box.js +121 -0
  13. package/dist/harbor/core.d.ts +5 -5
  14. package/dist/harbor/core.js +73 -72
  15. package/dist/harbor/index.d.ts +3 -1
  16. package/dist/harbor/index.js +3 -0
  17. package/dist/harbor/memory.d.ts +8 -4
  18. package/dist/harbor/memory.js +43 -11
  19. package/dist/harbor/store.js +1 -1
  20. package/dist/vector/cases.d.ts +4 -3
  21. package/dist/vector/cases.js +33 -5
  22. package/dist/vector/world.d.ts +1 -0
  23. package/dist/vector/world.js +17 -6
  24. package/dist/ward/ground.d.ts +13 -7
  25. package/dist/ward/ground.js +29 -11
  26. package/dist/ward/heirs.js +4 -2
  27. package/dist/ward/index.d.ts +2 -1
  28. package/dist/ward/index.js +1 -0
  29. package/dist/ward/partition.js +2 -2
  30. package/dist/ward/seal.js +1 -1
  31. package/dist/ward/stance.d.ts +0 -5
  32. package/dist/ward/ward.d.ts +1 -1
  33. package/dist/ward/ward.js +62 -27
  34. package/package.json +7 -5
  35. package/src/being/index.ts +1 -0
  36. package/src/being/lent.ts +66 -0
  37. package/src/being/types.ts +1 -1
  38. package/src/conformance/beings.ts +8 -3
  39. package/src/conformance/index.ts +18 -15
  40. package/src/harbor/box.ts +131 -0
  41. package/src/harbor/core.ts +23 -22
  42. package/src/harbor/index.ts +4 -1
  43. package/src/harbor/memory.ts +52 -12
  44. package/src/harbor/store.ts +1 -1
  45. package/src/vector/cases.ts +37 -9
  46. package/src/vector/world.ts +18 -6
  47. package/src/ward/ground.ts +85 -103
  48. package/src/ward/heirs.ts +4 -2
  49. package/src/ward/index.ts +2 -1
  50. package/src/ward/partition.ts +2 -2
  51. package/src/ward/seal.ts +1 -1
  52. package/src/ward/stance.ts +1 -1
  53. package/src/ward/ward.ts +59 -27
@@ -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, type Lend } from '../ward/ground.ts';
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
- // What this device lends the beings of the wards it hosts, by name. One
153
- // for the harbor, and `lendFor` says which ward may ask it: every ward,
154
- // unless a terrain says a stranger's ward is lent nothing.
155
- // Set at birth or once the harbor's own ward is standing, which is the
156
- // usual order: a terrain puts that ward up first, then boots the rest on a
157
- // ground that can reach it.
158
- lend: Lend | undefined;
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 () => ({}), lend?: Lend) {
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 lendFor(_name: string, _record: WardRecord): Lend | undefined {
167
- return this.lend;
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]![1];
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]![1];
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
- wrote: (row) => this.#wrote(name, row),
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
- keep: async () => {
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
- calling: () => {
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 lend = this.lendFor(name, kept.record);
608
- if (lend) ground.lend = lend;
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.keep?.());
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;
@@ -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, Lend } from '../ward/ground.ts';
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';
@@ -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
- import type { BeingClass, BeingLike } from '../being/types.ts';
8
- import { maker, entropy, learnPk, type Ground, type WardPointers, type Lend } from '../ward/ground.ts';
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 asking.
51
- // `lend` is what this device lends the ward's beings, and a test's stand-in
52
- // for a real terrain's: left out, every name answers null.
53
- async boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, lend?: Lend): Promise<Booted> {
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, lend);
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>, lend?: Lend): Promise<Booted> {
60
- const memory = this.partitions.get(seed);
61
- if (!memory) throw new Error(`no partition for ${seed}`);
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
- ...(lend ? { lend } : {}),
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
  }
@@ -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) as Record<string, unknown>)];
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.
@@ -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
- before: string; // the partition's digest before the door judged
37
- after: string; // and after. equal is `nothing written`
38
- wrote: boolean; // the two digests differ. a refusal never sets it
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
- return { w, before, arrival };
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
- return { case: c.case, name: c.name, state: c.state, ward: c.ward, kind: arrival.kind, heard: judged.heard, ask: hex(arrival.ask.bytes), reply: hex(judged.bytes), opens, before, after, wrote: before !== after };
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, w.inv.heir!, 'word', { signer: secret(w.inv), next: 'a'.repeat(64) });
176
+ return at(WB, b.inv.heir!, 'word', { signer: b.own });
149
177
  },
150
178
  },
151
179
  {
@@ -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 `protocol/vectors/door.json` be a corpus and not a
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. Sixteen lines a kit in any language writes from this comment
27
- // alone: the state starts at the seed, every draw adds the golden gamma
28
- // 0x9e3779b97f4a7c15, mixes with two xor-shift-multiplies, and the sixty-four
29
- // bit result is spent eight bytes at a time, least significant first. It is
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) out.set(draw().subarray(0, Math.min(8, n - i)), i);
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();
@@ -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: nine things
3
- // the ward uses. Everything a runtime differs on arrives here, which is why
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
- // What the device lends this ward's beings, by name. What a box can do is
8
- // beings, in a ward its harbor booted and roots, and this is how a being of
9
- // another ward comes to hold a standing at one: the harbor asks its own root
10
- // to invite on that being, and hands the invitation to the taker.
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
- // The taker is the ward's half, and it is inside this call rather than after
13
- // it so that nothing half-lives. The harbor minted, so the harbor is the only
14
- // one who can unmint: a ward that could not knock, or could not take, says so
15
- // by answering false, and the root that minted removes the occupant it made.
16
- // A lend that failed leaves no heir open at that being and no relation bound
17
- // to a ward that does not hold it, which is the promise `boot` keeps with
18
- // `unmake` and the same promise here.
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
- // False is every kind of no, and they are one answer because a being would do
21
- // nothing different for any of them: this harbor lends nothing, or nothing of
22
- // that name, or nothing of that name to this ward, or the ward did not take
23
- // what was minted.
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
- // The invitation never reaches the being. It is the device's capability, and
26
- // a value she could copy is one she could hand to anyone, so it lives in this
27
- // call and nowhere else.
28
- export type Lend = (name: string, take: (invitation: Invitation) => Promise<boolean>) => Promise<boolean>;
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; // the ward derives its pk from it and nothing else
32
- memory: Record<string, unknown>; // the partition. the ward's files. opaque to the harbor
33
- instantiate(className: string, stance: Stance): BeingLike | null; // the code half
34
- // The ward wrote its partition, and which row of it: a being by her key,
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
- // A harbor that sent the bytes and then gave up waiting knows no such
94
- // thing: the far door may have heard and be working still. It must not
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
- random(n: number): Uint8Array; // entropy. every key a ward mints is drawn from it
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. Three pieces, because every
121
- // harbor in this tree writes the same three and the protocol keeps the harbors
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. Every key a
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. A ward that answers anything
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 ward's own — wider is more forgiving of a
11
- // rough road, and no peer can tell the difference except by being refused.
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 {