nervur 0.19.2 → 0.20.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 (205) hide show
  1. package/README.md +66 -101
  2. package/dist/being/being.d.ts +7 -15
  3. package/dist/being/being.js +16 -67
  4. package/dist/being/digest.d.ts +1 -1
  5. package/dist/being/digest.js +7 -27
  6. package/dist/being/faculty.d.ts +9 -0
  7. package/dist/being/faculty.js +68 -0
  8. package/dist/being/index.d.ts +4 -5
  9. package/dist/being/index.js +5 -5
  10. package/dist/being/types.d.ts +35 -62
  11. package/dist/being/types.js +3 -57
  12. package/dist/being/words.d.ts +13 -0
  13. package/dist/being/words.js +14 -0
  14. package/dist/contract/index.d.ts +26 -0
  15. package/dist/contract/index.js +19 -0
  16. package/dist/crypto/aes.d.ts +3 -0
  17. package/dist/crypto/aes.js +24 -0
  18. package/dist/crypto/bytes.d.ts +6 -0
  19. package/dist/crypto/bytes.js +33 -0
  20. package/dist/crypto/ed25519.d.ts +3 -0
  21. package/dist/crypto/ed25519.js +85 -0
  22. package/dist/crypto/hash.d.ts +2 -0
  23. package/dist/crypto/hash.js +11 -0
  24. package/dist/crypto/index.d.ts +7 -0
  25. package/dist/crypto/index.js +10 -0
  26. package/dist/crypto/json.d.ts +8 -0
  27. package/dist/crypto/json.js +250 -0
  28. package/dist/crypto/mlkem.d.ts +12 -0
  29. package/dist/crypto/mlkem.js +36 -0
  30. package/dist/crypto/subtle.d.ts +10 -0
  31. package/dist/crypto/subtle.js +29 -0
  32. package/dist/crypto/x25519.d.ts +2 -0
  33. package/dist/crypto/x25519.js +21 -0
  34. package/dist/folder/index.d.ts +13 -0
  35. package/dist/folder/index.js +125 -0
  36. package/dist/harbor/dock.d.ts +19 -0
  37. package/dist/harbor/dock.js +92 -0
  38. package/dist/harbor/harbor.d.ts +22 -0
  39. package/dist/harbor/harbor.js +84 -0
  40. package/dist/harbor/index.d.ts +4 -10
  41. package/dist/harbor/index.js +5 -18
  42. package/dist/harbor/registry.d.ts +7 -0
  43. package/dist/harbor/registry.js +13 -0
  44. package/dist/harbor/terrain.d.ts +8 -0
  45. package/dist/harbor/terrain.js +2 -0
  46. package/dist/index.d.ts +7 -0
  47. package/dist/index.js +14 -0
  48. package/dist/pointer/bodies.d.ts +20 -0
  49. package/dist/pointer/bodies.js +45 -0
  50. package/dist/pointer/index.d.ts +2 -0
  51. package/dist/pointer/index.js +4 -0
  52. package/dist/pointer/world.d.ts +34 -0
  53. package/dist/pointer/world.js +76 -0
  54. package/dist/quo/door.d.ts +34 -0
  55. package/dist/quo/door.js +172 -0
  56. package/dist/quo/index.d.ts +8 -0
  57. package/dist/quo/index.js +11 -0
  58. package/dist/quo/invitation.d.ts +7 -0
  59. package/dist/quo/invitation.js +15 -0
  60. package/dist/quo/keys.d.ts +40 -0
  61. package/dist/quo/keys.js +79 -0
  62. package/dist/quo/payload.d.ts +15 -0
  63. package/dist/quo/payload.js +55 -0
  64. package/dist/quo/relations.d.ts +40 -0
  65. package/dist/quo/relations.js +33 -0
  66. package/dist/quo/reply.d.ts +13 -0
  67. package/dist/quo/reply.js +35 -0
  68. package/dist/quo/seal.d.ts +42 -0
  69. package/dist/quo/seal.js +78 -0
  70. package/dist/quo/standing.d.ts +39 -0
  71. package/dist/quo/standing.js +90 -0
  72. package/dist/ward/allowance.d.ts +13 -9
  73. package/dist/ward/allowance.js +28 -67
  74. package/dist/ward/cells.d.ts +6 -6
  75. package/dist/ward/cells.js +67 -179
  76. package/dist/ward/index.d.ts +5 -10
  77. package/dist/ward/index.js +6 -13
  78. package/dist/ward/partition.d.ts +44 -60
  79. package/dist/ward/partition.js +56 -285
  80. package/dist/ward/stance.d.ts +29 -20
  81. package/dist/ward/stance.js +173 -406
  82. package/dist/ward/ward-being.d.ts +33 -0
  83. package/dist/ward/ward-being.js +76 -0
  84. package/dist/ward/ward.d.ts +43 -11
  85. package/dist/ward/ward.js +215 -363
  86. package/package.json +19 -33
  87. package/src/being/being.ts +31 -78
  88. package/src/being/digest.ts +7 -35
  89. package/src/being/faculty.ts +66 -0
  90. package/src/being/index.ts +5 -6
  91. package/src/being/types.ts +47 -145
  92. package/src/being/words.ts +31 -0
  93. package/src/contract/index.ts +44 -0
  94. package/src/crypto/aes.ts +26 -0
  95. package/src/crypto/bytes.ts +37 -0
  96. package/src/crypto/ed25519.ts +84 -0
  97. package/src/crypto/hash.ts +14 -0
  98. package/src/crypto/index.ts +10 -0
  99. package/src/crypto/json.ts +241 -0
  100. package/src/crypto/mlkem.ts +38 -0
  101. package/src/crypto/subtle.ts +33 -0
  102. package/src/crypto/x25519.ts +21 -0
  103. package/src/folder/index.ts +134 -0
  104. package/src/harbor/dock.ts +101 -0
  105. package/src/harbor/harbor.ts +105 -0
  106. package/src/harbor/index.ts +5 -19
  107. package/src/harbor/registry.ts +20 -0
  108. package/src/harbor/terrain.ts +13 -0
  109. package/src/index.ts +20 -0
  110. package/src/pointer/bodies.ts +47 -0
  111. package/src/pointer/index.ts +4 -0
  112. package/src/pointer/world.ts +91 -0
  113. package/src/quo/door.ts +178 -0
  114. package/src/quo/index.ts +11 -0
  115. package/src/quo/invitation.ts +15 -0
  116. package/src/quo/keys.ts +91 -0
  117. package/src/quo/payload.ts +61 -0
  118. package/src/quo/relations.ts +62 -0
  119. package/src/quo/reply.ts +38 -0
  120. package/src/quo/seal.ts +101 -0
  121. package/src/quo/standing.ts +111 -0
  122. package/src/stand/main.ts +19 -0
  123. package/src/stand/stand.ts +178 -0
  124. package/src/ward/allowance.ts +37 -75
  125. package/src/ward/cells.ts +63 -176
  126. package/src/ward/index.ts +6 -17
  127. package/src/ward/partition.ts +86 -326
  128. package/src/ward/stance.ts +185 -420
  129. package/src/ward/ward-being.ts +97 -0
  130. package/src/ward/ward.ts +229 -363
  131. package/dist/being/lent.d.ts +0 -32
  132. package/dist/being/lent.js +0 -72
  133. package/dist/being/silence.d.ts +0 -12
  134. package/dist/being/silence.js +0 -41
  135. package/dist/conformance/assert.d.ts +0 -11
  136. package/dist/conformance/assert.js +0 -106
  137. package/dist/conformance/beings.d.ts +0 -199
  138. package/dist/conformance/beings.js +0 -188
  139. package/dist/conformance/estate.d.ts +0 -5
  140. package/dist/conformance/estate.js +0 -388
  141. package/dist/conformance/index.d.ts +0 -80
  142. package/dist/conformance/index.js +0 -819
  143. package/dist/conformance/reach.d.ts +0 -10
  144. package/dist/conformance/reach.js +0 -72
  145. package/dist/conformance/store.d.ts +0 -5
  146. package/dist/conformance/store.js +0 -113
  147. package/dist/harbor/box.d.ts +0 -121
  148. package/dist/harbor/box.js +0 -121
  149. package/dist/harbor/core.d.ts +0 -55
  150. package/dist/harbor/core.js +0 -662
  151. package/dist/harbor/dial.d.ts +0 -9
  152. package/dist/harbor/dial.js +0 -81
  153. package/dist/harbor/memory.d.ts +0 -29
  154. package/dist/harbor/memory.js +0 -104
  155. package/dist/harbor/reach.d.ts +0 -36
  156. package/dist/harbor/reach.js +0 -199
  157. package/dist/harbor/store.d.ts +0 -35
  158. package/dist/harbor/store.js +0 -62
  159. package/dist/vector/cases.d.ts +0 -42
  160. package/dist/vector/cases.js +0 -223
  161. package/dist/vector/index.d.ts +0 -6
  162. package/dist/vector/index.js +0 -8
  163. package/dist/vector/stand.d.ts +0 -9
  164. package/dist/vector/stand.js +0 -77
  165. package/dist/vector/world.d.ts +0 -144
  166. package/dist/vector/world.js +0 -209
  167. package/dist/ward/arithmetic.d.ts +0 -31
  168. package/dist/ward/arithmetic.js +0 -249
  169. package/dist/ward/door.d.ts +0 -18
  170. package/dist/ward/door.js +0 -186
  171. package/dist/ward/ground.d.ts +0 -29
  172. package/dist/ward/ground.js +0 -56
  173. package/dist/ward/heirs.d.ts +0 -13
  174. package/dist/ward/heirs.js +0 -117
  175. package/dist/ward/json.d.ts +0 -2
  176. package/dist/ward/json.js +0 -163
  177. package/dist/ward/owner.d.ts +0 -13
  178. package/dist/ward/owner.js +0 -220
  179. package/dist/ward/seal.d.ts +0 -52
  180. package/dist/ward/seal.js +0 -150
  181. package/src/being/lent.ts +0 -72
  182. package/src/being/silence.ts +0 -46
  183. package/src/conformance/assert.ts +0 -100
  184. package/src/conformance/beings.ts +0 -188
  185. package/src/conformance/estate.ts +0 -412
  186. package/src/conformance/index.ts +0 -965
  187. package/src/conformance/reach.ts +0 -83
  188. package/src/conformance/store.ts +0 -125
  189. package/src/harbor/box.ts +0 -131
  190. package/src/harbor/core.ts +0 -699
  191. package/src/harbor/dial.ts +0 -112
  192. package/src/harbor/memory.ts +0 -123
  193. package/src/harbor/reach.ts +0 -221
  194. package/src/harbor/store.ts +0 -91
  195. package/src/vector/cases.ts +0 -257
  196. package/src/vector/index.ts +0 -11
  197. package/src/vector/stand.ts +0 -76
  198. package/src/vector/world.ts +0 -232
  199. package/src/ward/arithmetic.ts +0 -251
  200. package/src/ward/door.ts +0 -186
  201. package/src/ward/ground.ts +0 -142
  202. package/src/ward/heirs.ts +0 -116
  203. package/src/ward/json.ts +0 -144
  204. package/src/ward/owner.ts +0 -214
  205. package/src/ward/seal.ts +0 -178
package/dist/ward/ward.js CHANGED
@@ -1,396 +1,248 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
- // A ward that is its own ward. Booted by a harbor with one object, the
3
- // ground, and hands back two pointers: the door and the ask. Boots itself as
4
- // the first being in its own map, and every being it later instantiates gets
5
- // a stance built by the same code. Mints every key, seals every ask that
6
- // leaves, opens every one that arrives.
7
- import { silence, isSilence, unreached, isWord, word } from '../being/silence.js';
8
- import { OWNER, isInvitation } from '../being/types.js';
9
- import { BOX, OFFER, RETRACT } from './ground.js';
10
- import { at, drop, open, put, emptyBind, emptyCells, HEAD, MINTED } from './partition.js';
11
- import { Heirs } from './heirs.js';
12
- import { makeDoor } from './door.js';
13
- import { buildStance } from './stance.js';
14
- import { ownerAnswer } from './owner.js';
15
- import { allow, within, LATE } from './allowance.js';
16
- import { beingKey, openReply, sealAsk, wardKey, wardPadlock, wardSignPk } from './seal.js';
17
- import { unhex } from './arithmetic.js';
18
- import { guardCells, unguarded, cellFault } from './cells.js';
19
- // The ward. One call in, two pointers out. Async because keys are derived.
20
- export async function Ward(ground) {
21
- const w = await Self.boot(ground);
22
- return { door: w.door, ask: w.ask };
2
+ // The ward: one door, one partition, and the beings behind the door. It
3
+ // unpacks from its seed, its memory and a catalogue of classes, the
4
+ // ward-being first and every other being from her own row, and it stands
5
+ // what is behind its door for `quo/`.
6
+ import { readValue, writeValue } from '../crypto/index.js';
7
+ import { Door, readInvitation, WardKey } from '../quo/index.js';
8
+ import { digest, isSilence, isWord, OWNER, silence, WARD, word } from '../being/index.js';
9
+ import { Partition, PartitionRelations } from './partition.js';
10
+ import { DEPTH, WardStance } from './stance.js';
11
+ import { WardBeing } from './ward-being.js';
12
+ // The standing every hosted ward's ward-being holds on the dock.
13
+ export const DOCK = 'dock';
14
+ const lends = (being) => typeof being.offer === 'function';
15
+ class RootStance extends WardStance {
16
+ steward;
17
+ constructor(inside, row, steward) {
18
+ super(inside, WARD, row);
19
+ this.steward = steward;
20
+ }
23
21
  }
24
- class Self {
22
+ export class Ward {
25
23
  key;
26
- pk;
27
- g;
28
- p;
29
- heirs;
30
- doors = new Map();
31
- #birthing = new Set(); // keys whose constructor is running: taken, though no door and no row say so yet
24
+ partition;
32
25
  door;
33
- ask;
34
- // Birth, in order: the ward as its own first being, the box taken, and
35
- // only then every being the partition names constructed again. A being
36
- // who takes what her box lends does it at her birth, so the box must be
37
- // hers to ask before she is born, or a restart would be the one birth at
38
- // which the device lends nothing.
39
- static async boot(ground) {
40
- const w = new Self(ground, await wardKey(ground.seed));
41
- await w.#takeBox();
42
- w.#reborn();
43
- return w;
26
+ clock;
27
+ #parts;
28
+ #residents = new Map();
29
+ #birthing = new Set();
30
+ constructor(parts, key) {
31
+ this.#parts = parts;
32
+ this.key = key;
33
+ this.clock = parts.clock;
34
+ this.partition = new Partition(parts.memory);
35
+ this.door = new Door(key, new PartitionRelations(this.partition), this, parts.entropy.drawer);
44
36
  }
45
- // The device, taken as one standing under `BOX` by the ward's own being,
46
- // once: a ward woken from its partition already holds it. A knock that is
47
- // not answered leaves the ward up and lending nothing this run.
48
- async #takeBox() {
49
- const inv = this.g.box;
50
- const mine = this.doors.get(this.pk)?.stance;
51
- if (!inv || !mine || mine.standings[BOX])
52
- return;
53
- const out = await mine.standings.knock(inv);
54
- if (isSilence(out) || isWord(out))
55
- return;
56
- await mine.standings.take(BOX, inv);
37
+ // The ward-being first, then every being her row names. A class the
38
+ // catalogue lacks, or one that throws, leaves that being absent and her
39
+ // row as it was.
40
+ static async unpack(parts) {
41
+ const ward = new Ward(parts, await WardKey.from(parts.seed));
42
+ if (!ward.partition.being(WARD)) {
43
+ ward.partition.beings[WARD] = { class: '', cells: {}, occupants: {}, standings: {} };
44
+ ward.partition.told(WARD);
45
+ ward.partition.toldHead();
46
+ }
47
+ const own = ward.partition.being(WARD);
48
+ const stance = new RootStance(ward, own, ward.#steward);
49
+ const being = parts.root ? parts.root(stance) : new WardBeing(stance);
50
+ own.class = being.constructor.name;
51
+ ward.#residents.set(WARD, { being, row: own, stance });
52
+ if (parts.dock && !Object.hasOwn(own.standings, DOCK))
53
+ await stance.standings.take(DOCK, parts.dock);
54
+ for (const [key, row] of Object.entries(ward.partition.beings)) {
55
+ const C = key === WARD ? undefined : parts.catalogue.classOf(row.class);
56
+ if (C)
57
+ ward.#bear(key, row, C);
58
+ }
59
+ return ward;
57
60
  }
58
- // What this device lends this ward's beings: the box's being, asked to
59
- // offer a name, answers the lent being's own invitation, and the taker knocks
60
- // and takes it. The ward reads nothing in the value but that it is one.
61
- async #lend(name, take) {
62
- const box = this.doors.get(this.pk)?.stance.standings[BOX];
63
- if (!box)
64
- return false;
65
- const inv = await box.ask(OFFER, { name });
66
- if (!isInvitation(inv))
67
- return false;
68
- const took = await take(inv);
69
- if (!took)
70
- await box.ask(RETRACT, { name, heir: inv.heir ?? null });
71
- return took;
61
+ get pk() {
62
+ return this.key.pk;
72
63
  }
73
- constructor(ground, key) {
74
- this.g = ground;
75
- this.key = key;
76
- this.pk = key.pk;
77
- this.p = open(ground.memory.rows); // the partition: the ward's own state, and every being's row
78
- // Opening is itself a write: a memory nobody has written comes back with
79
- // a version stamped on it and the ward's own tables under it. A ward that
80
- // said nothing here would have every being of it kept and the head that
81
- // says which version they are written under kept by nobody, so the next
82
- // boot would read a partition of no version and refuse it.
83
- this.#wrote(HEAD);
84
- this.heirs = new Heirs(this.p, () => this.#wrote(HEAD));
85
- this.door = makeDoor(key, this.heirs, this.doors, () => this.p.public, (n) => ground.random(n), () => this.#keep()); // pointer one
86
- this.#boot(this.pk, () => this); // the ward's ward is itself
87
- // pointer two. the owner. the same answer every named asker gets, asker filled in as owner.
88
- this.ask = async (method, args = {}) => {
89
- try {
90
- const out = await this.answer({ id: OWNER }, method, args);
91
- // The same question the door asks, in the same place: after she has
92
- // answered and before the owner holds the answer. Silence is what a
93
- // failed ask says here, so a store that refused is silence, and the
94
- // owner reads it as it reads every other ask that did not take.
95
- if (!(await this.#keep().catch(() => false)))
96
- return silence;
97
- return isSilence(out) || isWord(out) ? silence : out;
98
- }
99
- catch {
100
- return silence;
101
- }
102
- };
64
+ get draw() {
65
+ return this.#parts.entropy.drawer;
103
66
  }
104
- #reborn() {
105
- // a restart is silent: every being in the cells is constructed again, unasked.
106
- // A throw on a boot the owner asked for is that boot's error, and nothing
107
- // half-lives. This boot nobody asked for, and there is nobody to hand an
108
- // error to. Any constructor may throw, a default that is not a value, an
109
- // author's mistake, a shape she does not recognise after an upgrade, and
110
- // let through here it is one being taking down the ward and every other
111
- // being in it, on a restart. She is absent instead: no door, so silence
112
- // at the door, and her cells sit untouched, waiting for the run that can
113
- // read them. Her row stays hers: nobody boots another class over it, the
114
- // owner sees her as absent, and the owner may unboot her.
115
- for (const [k, c] of Object.entries(this.p.beings)) {
116
- const className = c.class;
117
- if (k === this.pk || !className)
118
- continue;
119
- try {
120
- this.#boot(k, (stance) => this.g.instantiate(className, stance));
121
- }
122
- catch {
123
- /* she is not here this run. the ward is, and so is everyone else. */
124
- }
125
- }
67
+ get wardBeing() {
68
+ return this.#residents.get(WARD).being;
126
69
  }
127
- // ---- the ward as a being. asked only by its owners: the root, through
128
- // the ask pointer, and its occupants, through the door, whom only the root
129
- // can invite. A stranger cannot be a named asker here, because the door
130
- // names nobody the ward did not bind.
131
- async answer(asker, method, args = {}) {
132
- // Nobody is not an owner. The door names nobody the ward did not bind, so
133
- // the only way here unnamed is a partition that says the ward is its own
134
- // public being, which no owner ask will write. Silence, not an error
135
- // object: an unnamed asker is a stranger, and a stranger hears one thing.
136
- if (asker.id === undefined)
137
- return silence;
138
- return ownerAnswer({
139
- pk: this.pk,
140
- doors: this.doors,
141
- absent: () => this.#absent(),
142
- publicKey: () => this.p.public,
143
- instantiate: (className, key) => this.#instantiate(className, key),
144
- unboot: (key) => this.#unboot(key),
145
- setPublic: (key) => {
146
- this.p.public = key;
147
- this.#wrote(HEAD);
148
- },
149
- }, asker, method, args);
70
+ // The unsealed ask: the ward-being, asked by whoever holds this ward.
71
+ async root(method, args = {}) {
72
+ return this.#kept(WARD, () => this.#answer(WARD, { id: OWNER }, method, args));
150
73
  }
151
- // ---- ward functions. on the object. no stance reaches them.
152
- // The partition was written, and which row of it: a being by her key, or
153
- // the head. The harbor is told, and nothing more: what it does with the
154
- // word is its own, and the ward never learns. Every write says its row,
155
- // because a harbor told only that something moved must keep all of it, and
156
- // a being's row is the one part of a partition that grows without limit.
157
- #wrote(row) {
158
- this.g.memory.told(row);
74
+ // A being's answer, kept. Where the memory refuses, what she wrote is
75
+ // put back and her answer is silence. What her relations stand on is not
76
+ // put back: a relation's keys move only as far as its standing follows.
77
+ async #kept(key, work) {
78
+ const row = this.partition.being(key);
79
+ const before = row && JSON.stringify(row.cells);
80
+ const out = await work();
81
+ if (await this.partition.kept())
82
+ return out;
83
+ if (row && before !== undefined) {
84
+ for (const k of Object.keys(row.cells))
85
+ delete row.cells[k];
86
+ for (const [k, v] of Object.entries(JSON.parse(before)))
87
+ Object.defineProperty(row.cells, k, { value: v, enumerable: true, writable: true, configurable: true });
88
+ this.partition.told(key);
89
+ }
90
+ return silence;
159
91
  }
160
- // Whether what was written is kept, asked once at the end of an arrival
161
- // and never during one. A harbor that keeps nothing was never going to
162
- // refuse anything, so its silence here is yes: a ward driven with no store
163
- // under it answers exactly as it did before a harbor could say no.
164
- async #keep() {
165
- return this.g.memory.kept();
92
+ // Behind: the zero head reaches the public being, a heir the being that
93
+ // invited it.
94
+ get zero() {
95
+ const key = this.partition.head.public;
96
+ return key !== null && this.#residents.has(key);
166
97
  }
167
- // The rows no door holds this run: beings whose class threw at birth or
168
- // is not the harbor's to give. Key to class.
169
- #absent() {
170
- const out = {};
171
- for (const [k, c] of Object.entries(this.p.beings))
172
- if (k !== this.pk && !this.doors.has(k))
173
- put(out, k, c.class ?? null);
174
- return out;
98
+ async answer(heir, p) {
99
+ const bound = heir === null ? undefined : this.partition.head.bind[heir];
100
+ const key = heir === null ? this.partition.head.public : bound?.being;
101
+ const asker = bound ? { id: bound.id } : {};
102
+ const args = p.args === undefined ? {} : readValue(p.args, DEPTH + 1);
103
+ if (key === null || key === undefined || typeof args !== 'object' || args === null || Array.isArray(args))
104
+ return { silence: true };
105
+ const out = await this.#kept(key, () => this.#answer(key, asker, p.method, args));
106
+ const object = isSilence(out) || isWord(out) ? undefined : writeValue(out, DEPTH);
107
+ if (object === undefined)
108
+ return { silence: true };
109
+ const being = this.#residents.get(key)?.being;
110
+ const seen = p.method === undefined || !being?.describe ? null : await digest(being.describe(asker));
111
+ return { object, seen };
175
112
  }
176
- // A new being under a key nobody holds. A key with a row in the partition
177
- // is booted, whether a door holds it this run or not: an absent being's
178
- // row is hers, with every relation in it, and no class is booted over it.
179
- // A throw at birth is that boot's refusal and nothing half-lives; the
180
- // ward says which, since the owner hears objects.
181
- #instantiate(className, key) {
182
- // The three refusals are read before the constructor runs, and the
183
- // constructor can reach the stance and boot: a class that boots its own
184
- // key from inside itself would pass all three, since neither its door nor
185
- // its row exists yet, and the two objects would then share one row. A key
186
- // being born is a key taken, for as long as the birth lasts.
187
- if (typeof key !== 'string' || typeof className !== 'string' || !key || this.doors.has(key) || at(this.p.beings, key) || this.#birthing.has(key))
188
- return null;
189
- let door;
190
- this.#birthing.add(key);
113
+ // Every path that reaches a being's answer. A throw, a word out of her,
114
+ // or nothing at all is silence.
115
+ async #answer(key, asker, method, args) {
116
+ const resident = this.#residents.get(key);
117
+ if (!resident)
118
+ return silence;
119
+ let out;
191
120
  try {
192
- door = this.#boot(key, (stance) => this.g.instantiate(className, stance));
121
+ out = await resident.being.answer(asker, method, args);
193
122
  }
194
123
  catch {
195
- return 'threw';
196
- }
197
- finally {
198
- this.#birthing.delete(key);
124
+ return silence;
199
125
  }
200
- if (!door)
201
- return null;
202
- unguarded(door.cells).class = className; // so a restart finds her. the ward's key, written behind her guard
203
- this.#wrote(key);
204
- return door;
126
+ return out === undefined || isWord(out) ? silence : out;
205
127
  }
206
- // The inverse of boot, and the only way a being leaves a ward. Her
207
- // relations go first and by the same calls she would have used herself,
208
- // so every occupant hears `removed` at the door from the keys it kept,
209
- // rather than meeting a being who is simply not there any more. What is
210
- // left after that is her cells and her bind table, and they go with her:
211
- // a row naming a being no door holds would be read on the next restart
212
- // and boot her again. An absent being leaves the same way: she has no
213
- // stance to speak for her, so the ward closes her heirs itself.
214
- #unboot(key) {
215
- if (key === this.pk)
216
- return null;
217
- const door = this.doors.get(key);
218
- const cells = door?.cells ?? at(this.p.beings, key);
219
- if (!cells)
220
- return null;
221
- const ids = (r) => (r !== null && typeof r === 'object' ? Object.keys(r) : []);
222
- const occupants = ids(cells.occupants), standings = ids(cells.standings);
223
- if (door) {
224
- for (const id of occupants)
225
- door.stance.occupants.remove(id);
226
- for (const id of standings)
227
- door.stance.standings.remove(id);
228
- }
229
- else {
230
- const bind = at(this.p.bind, key);
231
- for (const heir of Object.values(bind?.occupants ?? {}))
232
- this.heirs.close(heir);
233
- }
234
- // A ward whose public being is gone answers arrivals for no heir with
235
- // silence, as a ward that never had one does.
236
- if (this.p.public === key)
237
- this.p.public = null;
238
- drop(this.p.beings, key);
239
- drop(this.p.bind, key);
240
- this.doors.delete(key);
241
- this.#wrote(key); // she is gone, and her row goes with her
242
- this.#wrote(HEAD); // her heirs were closed, and the public being may have been her
243
- return [...occupants, ...standings];
128
+ // Inside: what every stance of this ward stands on.
129
+ // One path through a door for every ask, this ward's own included.
130
+ async send(pk, bytes) {
131
+ if (pk === this.pk)
132
+ return (await this.door.arrive(bytes)).bytes;
133
+ return this.#parts.carrier.carry(pk, bytes);
244
134
  }
245
- // Nothing is written until there is somebody to write it for: a class the
246
- // harbor does not know makes no being, and a boot that made nobody must
247
- // leave the partition as it found it.
248
- //
249
- // `make` is who to build with the stance this raises. Two callers and two
250
- // makers: the ground's `instantiate` for a being of the ward, and the ward
251
- // itself, which is the first being in its own map and is already made.
252
- #boot(key, make) {
253
- const cells = guardCells(at(this.p.beings, key) ?? emptyCells(), () => this.#wrote(key));
254
- const bind = at(this.p.bind, key) ?? emptyBind();
255
- const stance = buildStance({
256
- pk: this.pk,
257
- // This stance, not merely this key: unboot and boot again under the
258
- // same key makes a new being, and the old stance is not hers.
259
- live: () => this.doors.get(key)?.stance === stance,
260
- mintKey: (b) => this.#mintKey(b, key),
261
- openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
262
- closeHeir: (heir) => this.heirs.close(heir),
263
- send: (b, keys, method, args, wanted) => this.#send(b, keys, method, args, wanted, key),
264
- // A throw at birth is null to her, not a throw in her method: she asked
265
- // for a being and got none, and her own answer is still hers to give.
266
- instantiate: (className, k) => {
267
- const made = this.#instantiate(className, k);
268
- return made && made !== 'threw' ? made.key : null;
269
- },
270
- // The two halves of a maker's way back, each the ward's own call: the
271
- // being made mints an occupant for her maker, and a relation that
272
- // never came about takes her out again.
273
- relate: async (k, id) => (await this.doors.get(k)?.stance.occupants.invite(id)) ?? null,
274
- unmake: (k) => void this.#unboot(k),
275
- lend: (name, take) => this.#lend(name, take),
276
- wrote: () => this.#wrote(key),
277
- }, key, cells, bind);
278
- const being = make(stance);
279
- if (!being || typeof being.answer !== 'function')
280
- return null; // a being is anything with an answer, and nothing without one
281
- put(this.p.beings, key, cells);
282
- put(this.p.bind, key, bind);
283
- const door = { key, cells, bind, stance, being };
284
- this.doors.set(key, door);
285
- this.#wrote(key);
286
- return door;
135
+ // A lend: the ward-being lends herself, as the dock does, or asks the dock
136
+ // through her standing.
137
+ async offer(contract) {
138
+ const own = this.#residents.get(WARD);
139
+ if (lends(own.being))
140
+ return own.being.offer(contract);
141
+ const out = await own.stance.standings.get(DOCK)?.ask('lend', { contract });
142
+ return readInvitation(out?.invitation);
287
143
  }
288
- async #mintKey(bind, row) {
289
- const k = await beingKey(this.g.random(32));
290
- // The last few she minted, and no more. A relation rotates on every ask,
291
- // so a list of all of them is a partition that grows for as long as she
292
- // keeps talking and can never be written down. Nothing is read from this
293
- // but the keys of the moment.
294
- bind.minted.push(k.pk);
295
- if (bind.minted.length > MINTED)
296
- bind.minted.splice(0, bind.minted.length - MINTED);
297
- this.#wrote(row);
298
- return k;
144
+ async retract(contract, heir) {
145
+ const own = this.#residents.get(WARD);
146
+ if (lends(own.being))
147
+ return own.being.withdraw(contract, heir);
148
+ await own.stance.standings.get(DOCK)?.ask('retract', { contract, heir });
299
149
  }
300
- // One send for every door, the ward's own included. Mine: never leaves.
301
- // Not mine: carry. Signs as the standing's current key, announces its
302
- // next, seals to the far ward, opens the reply with the ephemeral secret,
303
- // and rotates to the announced key once the far door has answered under
304
- // the current one.
305
- async #send(bind, keys, method, args, wanted, row) {
306
- // The harbor is told a call is in flight for as long as this one lasts,
307
- // and told again when it ends. Everything this ward writes between the
308
- // two is half of a relation: the count taken here, the key rotated when
309
- // the reply opens, and whatever the being writes on either side of her
310
- // own ask. A harbor may write those rows down; what it may not do is put
311
- // the ward back to them.
312
- const lower = this.g.memory.during();
150
+ live(key, row) {
151
+ return this.#residents.get(key)?.row === row;
152
+ }
153
+ // A being boots another of this ward. With an id, the new being invites
154
+ // her maker under the maker's key, and the maker takes it under id; a
155
+ // relation that could not be made unmakes the new being.
156
+ async boot(maker, className, key, id) {
157
+ if ((await this.#make(key, className)) !== 'booted')
158
+ return null;
159
+ if (id === undefined)
160
+ return key;
161
+ const invitation = await this.#residents.get(key).stance.occupants.invite(maker);
162
+ const taken = invitation ? await this.#residents.get(maker)?.stance.standings.take(id, invitation) : null;
163
+ if (taken)
164
+ return key;
165
+ this.#unboot(key);
166
+ return null;
167
+ }
168
+ async #make(key, className) {
169
+ if (key === '' || key === WARD || this.partition.being(key) || this.#birthing.has(key))
170
+ return 'key taken';
171
+ const C = this.#parts.catalogue.classOf(className);
172
+ if (!C)
173
+ return 'no such class';
174
+ this.#birthing.add(key);
313
175
  try {
314
- return await this.#sent(bind, keys, method, args, wanted, row);
176
+ const row = { class: className, cells: {}, occupants: {}, standings: {} };
177
+ this.partition.beings[key] = row;
178
+ if (!this.#bear(key, row, C)) {
179
+ delete this.partition.beings[key];
180
+ return 'threw at birth';
181
+ }
182
+ this.partition.told(key);
183
+ return 'booted';
315
184
  }
316
185
  finally {
317
- lower?.();
186
+ this.#birthing.delete(key);
318
187
  }
319
188
  }
320
- async #sent(bind, keys, method, args, wanted, row) {
321
- // What she asked for, held to what this ward allows. Asking for nothing is
322
- // the default, and asking for more than the ceiling is the ceiling: budget
323
- // is granted by a ward, never minted by a being.
324
- const budget = allow(wanted);
325
- const by = (await beingKey(unhex(keys.current))).pk;
326
- // A public being is reached without a heir, and the door keeps nothing for
327
- // whoever asked: no key it vouched for, so nothing to announce and nothing
328
- // to rotate to. A standing on her signs with one key for life. Every other
329
- // ask announces its next: there is no send that does not.
330
- if (keys.next === null && keys.heir !== null)
331
- keys.next = (await this.#mintKey(bind, row)).seed;
332
- const next = keys.next === null ? null : (await beingKey(unhex(keys.next))).pk;
333
- // Her count for this relation, one higher every call and never reused. The
334
- // far door honours each number once. One relation sends one at a time, so
335
- // the numbers leave in the order they were taken.
336
- const seq = (keys.seq += 1);
337
- // Named here, before the bytes go out, and not at the end with the keys.
338
- // The far door honours a number once, and it honours it before this side
339
- // hears anything back: a reply that is lost, a wait that ran out, a road
340
- // that took the bytes and said nothing, all end this call before the end
341
- // of it is reached. A count named only there is a count a restart forgets
342
- // while the door remembers, and she then speaks a number that door has
343
- // already spent, which it refuses as the replay it cannot tell it from.
344
- // So the rule is the send and not the reply: what she spoke under is
345
- // written down because she spoke it.
346
- this.#wrote(row);
347
- // Args that will not go through the seal are args that never left. A
348
- // being holds three answers and a throw is not one of them, so an ask
349
- // that cannot be sealed is told what is true of it: no door was reached.
350
- // One object of values, by the rule her cells keep and the far door
351
- // reads: a Date would arrive as a string, a function would not arrive at
352
- // all, a list or a string would be refused as a stranger's bytes, and
353
- // she would never know the far being was asked something other than
354
- // what she said.
355
- if (args === null || typeof args !== 'object' || Array.isArray(args) || cellFault(args, 'args') !== null)
356
- return unreached();
357
- let sealed;
189
+ #bear(key, row, C) {
190
+ const stance = new WardStance(this, key, row);
358
191
  try {
359
- sealed = await sealAsk(keys.heir, { by, next, seq, time: budget.time, method, args }, unhex(keys.current), wardPadlock(keys.ward), this.g.random(32));
192
+ this.#residents.set(key, { being: new C(stance), row, stance });
193
+ return true;
360
194
  }
361
195
  catch {
362
- return unreached();
196
+ this.#residents.delete(key);
197
+ return false;
363
198
  }
364
- const { bytes, ephemeral } = sealed;
365
- // The wait is bounded here, and again around the lane in the stance. Two
366
- // bounds and not one, because they end two different things: the stance's
367
- // ends the wait a being is held in, and this one ends the occupancy of the
368
- // relation's lane. A being holds three answers and a wait that does not
369
- // end is none of them, and a lane nobody ever leaves is a relation the
370
- // next ask never reaches. Only a bound on the wire breaks the second, and
371
- // taking it out would leave one quiet far side holding the lane for good.
372
- // What comes back late is not read.
373
- //
374
- // A wait that ran out is `late`, never unreached. Unreached promises
375
- // nothing was delivered and is safe to retry; a bound that expired knows
376
- // no such thing, because the far door may have heard and be working still.
377
- //
378
- // A harbor answers bytes or nothing. One that throws instead has answered
379
- // nothing in a louder voice, and is read as nothing: no door was reached.
380
- const carried = (keys.ward === this.pk ? this.door(new Uint8Array(bytes)).then((r) => r.bytes) : this.g.carry(keys.ward, bytes)).then((b) => b, () => undefined);
381
- const out = await within(budget.time, carried);
382
- if (out === LATE)
383
- return word('late');
384
- if (out === undefined)
385
- return unreached(); // nothing came back. no door was reached.
386
- const reply = await openReply(out, ephemeral, wardSignPk(keys.ward));
387
- if (!reply)
388
- return silence; // did not open, or not from that ward: not Quo's bytes
389
- if ('object' in reply && keys.next !== null) {
390
- keys.current = keys.next; // the far door holds `next` as announced. move to it.
391
- keys.next = null;
199
+ }
200
+ #unboot(key) {
201
+ const row = key === WARD ? undefined : this.partition.being(key);
202
+ if (!row)
203
+ return false;
204
+ for (const { heir } of Object.values(row.occupants)) {
205
+ this.door.remove(heir);
206
+ delete this.partition.head.bind[heir];
392
207
  }
393
- this.#wrote(row); // the count moved, and the keys may have
394
- return reply;
208
+ if (this.partition.head.public === key)
209
+ this.partition.head.public = null;
210
+ delete this.partition.beings[key];
211
+ this.#residents.delete(key);
212
+ this.partition.toldHead();
213
+ this.partition.told(key);
214
+ return true;
395
215
  }
216
+ // What the ward-being asks of her ward.
217
+ #steward = {
218
+ pk: () => this.pk,
219
+ classOf: (name) => this.#parts.catalogue.classOf(name),
220
+ beings: () => {
221
+ const out = {};
222
+ for (const [key, row] of Object.entries(this.partition.beings)) {
223
+ if (key !== WARD)
224
+ out[key] = { class: row.class, public: this.partition.head.public === key, absent: !this.#residents.has(key) };
225
+ }
226
+ return out;
227
+ },
228
+ boot: (key, className) => this.#make(key, className),
229
+ unboot: (key) => this.#unboot(key),
230
+ publish: (key) => {
231
+ if (key !== null && (key === WARD || !this.partition.being(key)))
232
+ return false;
233
+ this.partition.head.public = key;
234
+ this.partition.toldHead();
235
+ return true;
236
+ },
237
+ invite: (being, id, notes) => {
238
+ const resident = being === WARD ? undefined : this.#residents.get(being);
239
+ return resident ? resident.stance.occupants.invite(id, notes) : Promise.resolve(null);
240
+ },
241
+ ask: (being, method, args) => {
242
+ const key = being ?? this.partition.head.public;
243
+ if (key === null || key === WARD)
244
+ return Promise.resolve(word('unreached'));
245
+ return this.#answer(key, being === null ? {} : { id: OWNER }, method, args);
246
+ },
247
+ };
396
248
  }