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
@@ -78,20 +78,19 @@ export class Harbor {
78
78
  for (const say of this.announcers)
79
79
  say();
80
80
  }
81
- // What this device lends the beings of the wards it hosts, by name. One
82
- // for the harbor, and `lendFor` says which ward may ask it: every ward,
83
- // unless a terrain says a stranger's ward is lent nothing.
84
- // Set at birth or once the harbor's own ward is standing, which is the
85
- // usual order: a terrain puts that ward up first, then boots the rest on a
86
- // ground that can reach it.
87
- lend;
88
- constructor(store, loader = async () => ({}), lend) {
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
- lendFor(_name, _record) {
94
- return this.lend;
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
- wrote: (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
- keep: 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
- calling: () => {
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
- };
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 lend = this.lendFor(name, kept.record);
566
- if (lend)
567
- ground.lend = lend;
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.keep?.());
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)
@@ -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, Lend } from '../ward/ground.ts';
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';
@@ -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';
@@ -1,5 +1,5 @@
1
- import type { BeingClass, BeingLike } from '../being/types.ts';
2
- import { type Ground, type WardPointers, type Lend } from '../ward/ground.ts';
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>, lend?: Lend): Promise<Booted>;
24
- reboot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, lend?: Lend): Promise<Booted>;
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
  }
@@ -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 asking.
40
- // `lend` is what this device lends the ward's beings, and a test's stand-in
41
- // for a real terrain's: left out, every name answers null.
42
- async boot(seed, Ward, classes, lend) {
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, lend);
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, lend) {
48
- const memory = this.partitions.get(seed);
49
- if (!memory)
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
- ...(lend ? { lend } : {}),
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
  }
@@ -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) => {
@@ -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
- before: string;
15
- after: string;
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[]>;
@@ -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 { world, sealed, bound, secret, rnd, partitionDigest, Caller } from './world.js';
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
- return { w, before, arrival };
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
- 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 };
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, w.inv.heir, 'word', { signer: secret(w.inv), next: 'a'.repeat(64) });
142
+ return at(WB, b.inv.heir, 'word', { signer: b.own });
115
143
  },
116
144
  },
117
145
  {
@@ -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;
@@ -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.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. Sixteen lines a kit in any language writes from this comment
24
- // alone: the state starts at the seed, every draw adds the golden gamma
25
- // 0x9e3779b97f4a7c15, mixes with two xor-shift-multiplies, and the sixty-four
26
- // bit result is spent eight bytes at a time, least significant first. It is
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;
@@ -1,15 +1,21 @@
1
1
  import type { Stance, BeingLike, BeingClass, Invitation } from '../being/types.ts';
2
- export type Lend = (name: string, take: (invitation: Invitation) => Promise<boolean>) => Promise<boolean>;
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
- memory: Record<string, unknown>;
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
- random(n: number): Uint8Array;
12
- lend?: Lend;
18
+ box?: Invitation;
13
19
  };
14
20
  export type WardPointers = {
15
21
  door(bytes: Uint8Array): Promise<{
@@ -1,8 +1,30 @@
1
- // ---- what every harbor builds a ground out of. Three pieces, because every
2
- // harbor in this tree writes the same three and the protocol keeps the harbors
3
- // themselves apart: what a memory harbor and a real one differ on is the
4
- // route and the store, and nothing here. A second kit writes its own harbor
5
- // and may write these again; they are convenience, never contract.
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. Every key a
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. A ward that answers anything
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;
@@ -1,6 +1,8 @@
1
1
  import { GONE } from './partition.js';
2
- // How wide the span is, is the ward's own — wider is more forgiving of a
3
- // rough road, and no peer can tell the difference except by being refused.
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;
@@ -1,5 +1,6 @@
1
1
  export { Ward } from './ward.ts';
2
- export type { Ground, WardPointers, Lend } from './ground.ts';
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';
@@ -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';
@@ -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((p.bind ?? {}), row);
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((p.beings ?? {}), (value.cells ?? emptyCells()));
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 `protocol/vectors/framing.json` pins.
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