@quo-systems/quo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +91 -0
- package/SPEC.md +1595 -0
- package/package.json +64 -0
- package/src/being/being.ts +92 -0
- package/src/being/digest.ts +31 -0
- package/src/being/index.ts +7 -0
- package/src/being/silence.ts +14 -0
- package/src/being/types.ts +88 -0
- package/src/conformance/assert.ts +67 -0
- package/src/conformance/beings.ts +129 -0
- package/src/conformance/estate.ts +339 -0
- package/src/conformance/index.ts +516 -0
- package/src/conformance/reach.ts +83 -0
- package/src/conformance/store.ts +109 -0
- package/src/harbor/core.ts +178 -0
- package/src/harbor/dial.ts +61 -0
- package/src/harbor/index.ts +11 -0
- package/src/harbor/memory.ts +74 -0
- package/src/harbor/reach.ts +189 -0
- package/src/harbor/store.ts +69 -0
- package/src/ward/allowance.ts +74 -0
- package/src/ward/arithmetic.ts +161 -0
- package/src/ward/cells.ts +90 -0
- package/src/ward/door.ts +102 -0
- package/src/ward/ground.ts +35 -0
- package/src/ward/heirs.ts +87 -0
- package/src/ward/index.ts +14 -0
- package/src/ward/owner.ts +122 -0
- package/src/ward/partition.ts +123 -0
- package/src/ward/seal.ts +133 -0
- package/src/ward/stance.ts +264 -0
- package/src/ward/ward.ts +209 -0
- package/vectors/arithmetic.json +110 -0
- package/vectors/framing.json +71 -0
- package/vectors/wire.json +48 -0
package/SPEC.md
ADDED
|
@@ -0,0 +1,1595 @@
|
|
|
1
|
+
# SPEC
|
|
2
|
+
|
|
3
|
+
This is the truth of Quo, and the whole context. It assumes nothing from any
|
|
4
|
+
other document. It is written for the two people who work on this repository,
|
|
5
|
+
not for a reader outside it, and it ships with the package as the truth
|
|
6
|
+
the source is read against.
|
|
7
|
+
|
|
8
|
+
It carries no version and keeps no history. There is no 1.0.0 yet, so there
|
|
9
|
+
is nobody holding an older Quo to be compatible with, and no changelog to
|
|
10
|
+
write. This file is rewritten in place to say what the tree says, and when
|
|
11
|
+
the two disagree the fix is to move one of them, the same day. Where the tree
|
|
12
|
+
is behind a decision, the chapter "Where the tree stands" says so; that
|
|
13
|
+
chapter is the one place a gap is allowed to be named instead of closed.
|
|
14
|
+
`test/repo.test.ts` holds this document to the tree where a test can.
|
|
15
|
+
The package is published from 0.1.0 with no compatibility promise: this
|
|
16
|
+
document may still move under anyone holding it. At 1.0.0 it is frozen
|
|
17
|
+
once, and versioning starts to mean something.
|
|
18
|
+
|
|
19
|
+
## What Quo is
|
|
20
|
+
|
|
21
|
+
Quo is a protocol that lets an object ask another object and get an answer,
|
|
22
|
+
without knowing whether that other object is in the same process, on the same
|
|
23
|
+
device, or on another planet. It works on any device and in any language that
|
|
24
|
+
can run code. It is three words, two of which are beings, and two edges.
|
|
25
|
+
Nothing else is Quo.
|
|
26
|
+
|
|
27
|
+
- **Harbor.** The program a device runs to boot wards. Owns the wire and the
|
|
28
|
+
operating system. Not a being. Nobody outside its device.
|
|
29
|
+
- **Ward.** One process of its harbor. A being plus ward functions. Keeps
|
|
30
|
+
beings and judges its door.
|
|
31
|
+
- **Being.** One ordinary object, one voice.
|
|
32
|
+
|
|
33
|
+
Each word names both the interface and the thing. There is no fourth word.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
device A device B
|
|
37
|
+
harbor A harbor B
|
|
38
|
+
map: pk -> door | reach map: pk -> door | reach
|
|
39
|
+
ward W1 (pk1) ward W3 (pk3)
|
|
40
|
+
being b1 being b4
|
|
41
|
+
standings: [shop, bank] occupants: [cust7]
|
|
42
|
+
occupants: [owner] being b5 public
|
|
43
|
+
being b2
|
|
44
|
+
ward W2 (pk2)
|
|
45
|
+
being b3
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- A ward is named outward by one public key, its pk, derived from its seed.
|
|
49
|
+
That pk routes and nothing else. A being has no pk and no address. She has
|
|
50
|
+
relations, named by ids that never leave her ward.
|
|
51
|
+
- A **standing** is a pointer a being holds to another being, through which
|
|
52
|
+
she asks. An **occupant** is a being she invited, whom her ward names when
|
|
53
|
+
she asks.
|
|
54
|
+
- Every relation has two keys, one minted by each side for that relation
|
|
55
|
+
alone. Underneath every id her ward keeps the far ward's pk, the key she
|
|
56
|
+
is known by in that relation, and the key the far side is known by. She
|
|
57
|
+
never sees them.
|
|
58
|
+
- The harbor routes by ward pk only. Its own pks map to doors, one per ward
|
|
59
|
+
it booted. Foreign pks map to whatever the device calls a reach. How a
|
|
60
|
+
harbor fills the foreign half is its own directory, and Quo says nothing
|
|
61
|
+
about it.
|
|
62
|
+
- b1 asking b3 and b1 asking b4 are the same act inside the ward. The first
|
|
63
|
+
never leaves harbor A. The second does. b1 cannot tell, and should not. b1
|
|
64
|
+
asking b2 never leaves the ward at all.
|
|
65
|
+
- The ward routes one thing: its own pk. Every other ward pk goes into one
|
|
66
|
+
call the harbor gave it, and what comes back is bytes or nothing.
|
|
67
|
+
|
|
68
|
+
The rule that closes every chapter: whatever does not fit one of the lines in
|
|
69
|
+
this document is not Quo's.
|
|
70
|
+
|
|
71
|
+
## The two edges
|
|
72
|
+
|
|
73
|
+
Quo has two edges, harbor to ward and ward to being. At each an object
|
|
74
|
+
crosses once, at birth, and calls cross for the rest of the ward's life.
|
|
75
|
+
Nothing crosses either of them but what is written here.
|
|
76
|
+
|
|
77
|
+
### Harbor to ward
|
|
78
|
+
|
|
79
|
+
The harbor passes one object, once, at birth, and receives two pointers.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
ground
|
|
83
|
+
seed bytes, or a string. the ward derives its pk from it and nothing else.
|
|
84
|
+
32 bytes are the seed; anything else is SHA-256'd to 32 bytes first.
|
|
85
|
+
memory the partition. the ward's files. opaque to the harbor. an object.
|
|
86
|
+
instantiate (class name, stance) -> object | null
|
|
87
|
+
carry (ward pk, bytes) -> bytes | undefined undefined: no door was reached. a throw is read the same.
|
|
88
|
+
random (n) -> n bytes of entropy. every key a ward mints is drawn from it.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Nothing else is ever passed. A harbor that needs a sixth thing has found a
|
|
92
|
+
gap in Quo or a leak into the ward. The shape is `Ground` in
|
|
93
|
+
`src/ward/ground.ts`.
|
|
94
|
+
|
|
95
|
+
Returned:
|
|
96
|
+
|
|
97
|
+
- **The door.** One call: sealed bytes in, sealed bytes out. Always bytes:
|
|
98
|
+
when the ask did not open, the reply is silence sealed to whatever ephemeral
|
|
99
|
+
pk the bytes carried, and noise when they carried none.
|
|
100
|
+
- **The ask.** One call: method and args in, object or silence out.
|
|
101
|
+
In-process and unsealed. This is the one unsealed ask in Quo, and the only
|
|
102
|
+
way a ward is piloted. Judgment, catching, and the return table are not
|
|
103
|
+
skipped: a throw inside is silence, and an unreached shape is silence.
|
|
104
|
+
|
|
105
|
+
The harbor learns the ward's pk the way anyone learns anything: it asks. The
|
|
106
|
+
empty ask on the ask pointer answers with the ward's describe, and the pk is
|
|
107
|
+
in its notes.
|
|
108
|
+
|
|
109
|
+
### Ward to being
|
|
110
|
+
|
|
111
|
+
The ward passes one object, once, at birth, and receives one object, on
|
|
112
|
+
which it calls one method.
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
in the stance at birth, once, for life
|
|
116
|
+
the asker at every ask
|
|
117
|
+
out her answer(asker, method?, args?) -> object | silence the one method the ward calls
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The stance is her cells, her standings to ask, and the calls to invite,
|
|
121
|
+
knock, take and remove. The ward builds it and hands it to the harbor's
|
|
122
|
+
instantiate with a class name, and receives her. No method is the empty ask,
|
|
123
|
+
and what she answers to it is her blueprint.
|
|
124
|
+
|
|
125
|
+
The stance, as every being in every language receives it. Nothing more is
|
|
126
|
+
ever offered, and nothing here may be missing.
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
stance
|
|
130
|
+
cells her state. values only.
|
|
131
|
+
|
|
132
|
+
occupants.invite(id) -> invitation | null awaitable: a key is minted
|
|
133
|
+
occupants.remove(id) -> nothing
|
|
134
|
+
|
|
135
|
+
standings.knock(invitation, method?, args?, wanted?) -> object | silence | unreached
|
|
136
|
+
standings.take(id, invitation) -> id | null awaitable: it waits for the relation
|
|
137
|
+
standings[id] -> standing | undefined
|
|
138
|
+
standings[id].ask(method?, args?, wanted?) -> object | silence | unreached
|
|
139
|
+
standings.remove(id) -> nothing
|
|
140
|
+
|
|
141
|
+
wanted = { time? } what this one ask may spend, in milliseconds. optional,
|
|
142
|
+
and so is saying anything at all
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Return table.
|
|
146
|
+
|
|
147
|
+
| call | returns | when |
|
|
148
|
+
| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
|
|
149
|
+
| invite(id) | invitation | she minted a fresh id; the occupant record exists from now |
|
|
150
|
+
| | null | the id already names a record, occupant or standing, or is a reserved word |
|
|
151
|
+
| remove(id) | nothing | always. removing what is not there is nothing |
|
|
152
|
+
| knock(inv, m, a, w) | object | the far being answered |
|
|
153
|
+
| | silence | no ward claims the invitation, or she removed the id, or another consumed it, or the far being chose silence |
|
|
154
|
+
| | | or the invitation is not one, or the wait ran out |
|
|
155
|
+
| | unreached | her ward could not reach the far door, or could not seal the args |
|
|
156
|
+
| take(id, inv) | id | she knocked with this invitation and was answered, and the id is fresh |
|
|
157
|
+
| | null | no answered knock on this invitation, or the id already names a record, or is a reserved word |
|
|
158
|
+
| standings[id] | standing | the record exists |
|
|
159
|
+
| | undefined | it does not. not an error |
|
|
160
|
+
| ask(m, a, w) | object | she answered |
|
|
161
|
+
| | silence | she, or her ward, said nothing; or the wait ran out |
|
|
162
|
+
| | unreached | her ward could not reach the far door, or could not seal the args |
|
|
163
|
+
|
|
164
|
+
Ask, knock, invite and take are awaitable. Async where the language has it,
|
|
165
|
+
blocking where it does not. Every ask she makes is a new call.
|
|
166
|
+
|
|
167
|
+
The asker has three shapes and no fourth, and nothing else ever reaches her.
|
|
168
|
+
|
|
169
|
+
- `{ id }`: her own id for the occupant at the door, the one she minted when
|
|
170
|
+
she invited her.
|
|
171
|
+
- `{}`: she is the public being of her ward, and that is how she knows.
|
|
172
|
+
- `{ id: 'OWNER' }`: the arrival came on the ward's ask pointer, from her
|
|
173
|
+
owner.
|
|
174
|
+
|
|
175
|
+
`OWNER` and `PUBLIC` are reserved words: invite and take refuse them, so no
|
|
176
|
+
occupant can ever wear either name.
|
|
177
|
+
|
|
178
|
+
## Silence, unreached, error
|
|
179
|
+
|
|
180
|
+
Three words for "no object came back", and only three. Everything in Quo
|
|
181
|
+
that can go wrong ends in one of them, and a being who knows the three never
|
|
182
|
+
needs a fourth.
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
error an object. hers. { error: ... }, or any shape her output schema declares.
|
|
186
|
+
silence the ward's word. heard. bytes came back, or would have, and said nothing.
|
|
187
|
+
unreached the ward's word. not heard. nothing came back. nothing is known to have been delivered.
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- An **error** is an ordinary answer. Quo never reads it, never makes it,
|
|
191
|
+
and never treats it apart from any other object. The base class answers
|
|
192
|
+
`{ error: 'unknown ask' }` for an ask she did not declare or hid; the
|
|
193
|
+
owner's asks answer error objects for what they refuse; both are hers to
|
|
194
|
+
read like any object.
|
|
195
|
+
- **Silence** is not blindly retryable: the far being may have done the work
|
|
196
|
+
and the answer was lost.
|
|
197
|
+
- **Unreached** is safe to retry: nothing is known to have been delivered.
|
|
198
|
+
It carries no reason. What she may know is that nothing came back, and
|
|
199
|
+
that is all. In one case the far ward heard and died before it could
|
|
200
|
+
answer, and the wire says nothing about it; asking again asks under the
|
|
201
|
+
next number and is heard, so the retry is safe all the same. See Closed.
|
|
202
|
+
|
|
203
|
+
Who says what. A being says objects and silence, never unreached: the shape
|
|
204
|
+
is the ward's, and a ward that sees it come out of a being reads it as
|
|
205
|
+
silence. A ward says all three to its own being, and only objects and
|
|
206
|
+
silence on the wire: unreached never crosses, it is the wire's nothing
|
|
207
|
+
turned into a word. A harbor says nothing at all; it returns bytes or
|
|
208
|
+
nothing, and does not know the word silence. The owner hears objects: a
|
|
209
|
+
silence a being met is `{ error: 'silence' }` and an unreached is
|
|
210
|
+
`{ error: 'unreached' }`, because the owner is piloting and an object is
|
|
211
|
+
what a shell can print. The ask pointer answers the value silence only when
|
|
212
|
+
the ward itself threw.
|
|
213
|
+
|
|
214
|
+
### What her ward turns into silence
|
|
215
|
+
|
|
216
|
+
Before or after the wire, her own ward answers silence in five cases, and
|
|
217
|
+
tells her nothing about which.
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
S1 the invitation is not one a shape that is not a ward pk, a heir with no secret, a secret with no heir.
|
|
221
|
+
nothing is sent.
|
|
222
|
+
S2 the standing is gone she held a standing, dropped it, and asked on what she held.
|
|
223
|
+
nothing is sent. an ask issued while it stood is answered.
|
|
224
|
+
S3 the wait ran out the ask's time was spent and no reply was read.
|
|
225
|
+
what comes back late is not read. the number is spent.
|
|
226
|
+
an ask still waiting at its lane when the wait ran out is never sent.
|
|
227
|
+
S4 the reply is not one bytes came back that do not open, or are not signed by the ward they
|
|
228
|
+
were sent to, or are not `{ silence }` nor `{ object, seen }` with an
|
|
229
|
+
object present and seen a digest or null. the number is spent.
|
|
230
|
+
S5 the far door said silence every case in the next list.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
And unreached in two: `U1` the args are not a value, or could not be
|
|
234
|
+
sealed, so nothing left;
|
|
235
|
+
`U2` the harbor returned nothing, or threw, so no door was reached. The number is
|
|
236
|
+
taken all the same, and the gap it leaves in the count is harmless: the door
|
|
237
|
+
honours any number above its mark.
|
|
238
|
+
|
|
239
|
+
### What the door turns into silence
|
|
240
|
+
|
|
241
|
+
The door answers one silence in thirteen cases. The first ten are refusals:
|
|
242
|
+
the ask reached no being, and nothing was written, spent, rotated or
|
|
243
|
+
touched. The last three are choices: the ask reached her, so the number is
|
|
244
|
+
spent and the keys are rotated, and what she wrote in her cells is hers.
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
refusals
|
|
248
|
+
D1 the box does not open wrong padlock, garbage, too short. the reply is sealed to the lid if
|
|
249
|
+
the bytes carried one, and is noise otherwise. a lid that will not
|
|
250
|
+
take a seal, a small-order point, is no lid: noise.
|
|
251
|
+
D2 the payload is malformed by or next not 64 hex, method not a string, args not an object, seq
|
|
252
|
+
below one, time not a whole number above zero.
|
|
253
|
+
D3 for nobody, and nobody is home no public being on this ward.
|
|
254
|
+
D4 for nobody, signature fails the payload names a key it was not signed with.
|
|
255
|
+
D5 the heir is not held never minted here, or forgotten because the id was removed.
|
|
256
|
+
D6 the key is not admitted not the key held for the heir, and not the key it announced. a
|
|
257
|
+
forged key, an unannounced key, a heir already spent.
|
|
258
|
+
D7 she is not there the occupant record is gone, or the being did not come back this run.
|
|
259
|
+
D8 signature fails under an admitted key.
|
|
260
|
+
D9 a knock announces nothing the heir is fresh and next is null. it binds nothing.
|
|
261
|
+
D10 the number is refused already honoured, or at or below the span.
|
|
262
|
+
choices
|
|
263
|
+
D11 she threw
|
|
264
|
+
D12 she answered silence on a named ask, or on the empty ask. nothing at all is silence too.
|
|
265
|
+
D13 she answered an unreached shape
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
A refusal is judged in that order, and the first case met is the answer.
|
|
269
|
+
The signature is verified before the number is spent, so a stranger cannot
|
|
270
|
+
burn a number she could not sign for; a refusal at D9 or D10 has passed the
|
|
271
|
+
signature and still writes nothing.
|
|
272
|
+
|
|
273
|
+
### The law of one silence
|
|
274
|
+
|
|
275
|
+
1. Every case above is one reply: `{ silence: true }`, sealed to the lid the
|
|
276
|
+
ask came with, signed by the ward key. Same bytes, same length. A
|
|
277
|
+
stranger cannot tell a refusal from a choice, nor one refusal from
|
|
278
|
+
another, nor any of them from a being who chose to say nothing.
|
|
279
|
+
The door equalizes the bytes and not the time it took to write them. The
|
|
280
|
+
ten refusals are cheap and alike; a choice ran a being first, and a being
|
|
281
|
+
is code this document does not bound. So whoever holds a standing may
|
|
282
|
+
learn from a clock that she was reached and said nothing, rather than
|
|
283
|
+
refused at the door. A stranger holds no standing and reaches no choice,
|
|
284
|
+
which is why the refusals are the ones made alike. Equalizing the time
|
|
285
|
+
would mean waiting out the allowance ceiling on every silence, and the
|
|
286
|
+
door will not buy that.
|
|
287
|
+
2. A refusal writes nothing. No number, no key, no heir, no cell, no bind.
|
|
288
|
+
The same bytes presented again meet the same refusal, and a stranger who
|
|
289
|
+
knocks a thousand times leaves no mark.
|
|
290
|
+
3. A choice writes what a heard ask writes. The number is spent and the keys
|
|
291
|
+
rotate, so the relation goes on: the next ask on it is answered, and a
|
|
292
|
+
silence never kills a standing.
|
|
293
|
+
4. Silence leaves the caller's record as it was. A silent named answer
|
|
294
|
+
leaves `seen` untouched; a silent empty ask leaves `blueprint` and
|
|
295
|
+
`digest` untouched.
|
|
296
|
+
5. Her ward never confuses her. Silence and unreached are told apart
|
|
297
|
+
always, and by nothing else: silence is bytes the far ward wrote, or a
|
|
298
|
+
bound her ward rang; unreached is the wire's nothing. A ward never
|
|
299
|
+
answers nothing, so that nothing always means unreached.
|
|
300
|
+
6. Nobody enforces what a being does with silence. It is one value she
|
|
301
|
+
compares against, and the base class hands it to her unchanged.
|
|
302
|
+
|
|
303
|
+
`test/silence.test.ts` asserts every numbered line above, by its number.
|
|
304
|
+
|
|
305
|
+
## Words and values
|
|
306
|
+
|
|
307
|
+
### Values
|
|
308
|
+
|
|
309
|
+
Everything that crosses an edge is I-JSON (RFC 7493): args, answer,
|
|
310
|
+
blueprint, invitation, and whatever she caches from them. Strings are valid
|
|
311
|
+
Unicode. No duplicate keys. Numbers must be representable as IEEE doubles,
|
|
312
|
+
so integers are exact up to 2^53. Larger integers and exact decimals travel
|
|
313
|
+
as strings, with a schema format saying so. Bytes are base64 strings.
|
|
314
|
+
Nothing else is a value: no dates, no references, no functions, no native
|
|
315
|
+
types. An id or a standing is never a value.
|
|
316
|
+
|
|
317
|
+
Args are held to the same rule before they are sealed: an ask whose args
|
|
318
|
+
hold a non-value is unreached, because nothing left. Cells hold values and
|
|
319
|
+
refuse anything else at the moment of writing, in her own frame: a Map, a
|
|
320
|
+
Date, a NaN, a cyclic object, a function, an object that is not a plain one.
|
|
321
|
+
The refusal is a throw where she wrote it, which her ward turns into the
|
|
322
|
+
silence it turns every throw into. She has not answered, and
|
|
323
|
+
nothing was written down that a harbor would later have to lie about. This
|
|
324
|
+
holds all the way down: a container read through her cells is part of her
|
|
325
|
+
cells. The guard is `src/ward/cells.ts`.
|
|
326
|
+
|
|
327
|
+
### Silence and unreached
|
|
328
|
+
|
|
329
|
+
Two of the three words for "no object came back". The chapter "Silence,
|
|
330
|
+
unreached, error" is the whole of them; here only the values. Silence is one
|
|
331
|
+
distinguished value. Null is an answer. Unreached is a second distinguished
|
|
332
|
+
value, her ward's own word. Neither is an object, and neither carries
|
|
333
|
+
anything. In this kit silence is the symbol `quo.silence` and unreached is a
|
|
334
|
+
frozen object under the symbol key `quo.unreached`, both from
|
|
335
|
+
`src/being/silence.ts`.
|
|
336
|
+
|
|
337
|
+
### Blueprint, schema, digest
|
|
338
|
+
|
|
339
|
+
Nothing here is Quo's invention. Four standards, adopted whole, so that two
|
|
340
|
+
wards in two languages always read one blueprint the same way.
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
blueprint
|
|
344
|
+
asks list of { name, description?, input: schema, output?: schema }
|
|
345
|
+
notes any JSON value
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
The blueprint is the shape of an MCP tool list, plus notes. The schema is
|
|
349
|
+
JSON Schema draft 2020-12, the one MCP uses; typed languages generate from
|
|
350
|
+
it, untyped languages ignore it, and Quo writes none of it. Args are one JSON
|
|
351
|
+
object with named fields, as MCP passes arguments.
|
|
352
|
+
|
|
353
|
+
The digest is SHA-256, as hex, over the JCS (RFC 8785) canonical
|
|
354
|
+
serialization of the blueprint. Same bytes from every language. This is law:
|
|
355
|
+
two wards in two languages always hash one blueprint to one digest. A ward
|
|
356
|
+
that cannot do this is not a ward. Because only values reach a digest, a key
|
|
357
|
+
holding what is not a value is dropped before hashing and an array slot
|
|
358
|
+
holding one is null, which is what crossing an edge does to them anyway. The
|
|
359
|
+
reference is `src/being/digest.ts`.
|
|
360
|
+
|
|
361
|
+
Capability and state are two axes, never mixed.
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
capability what she can be asked. blueprint. changes rarely.
|
|
365
|
+
hashed into the digest. learned by the empty ask.
|
|
366
|
+
state what she answers. answers only. may change every ask.
|
|
367
|
+
never hashed. never in the blueprint.
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
A digest change means one thing: her interface changed and the cached
|
|
371
|
+
blueprint is wrong. Notes in the blueprint are about the interface. A being
|
|
372
|
+
that puts state there makes every standing refresh for nothing, and that cost
|
|
373
|
+
is hers.
|
|
374
|
+
|
|
375
|
+
What follows: a being's describe is an MCP tool list with no translation.
|
|
376
|
+
Whoever holds a ward's ask can put an MCP server in front of any being of
|
|
377
|
+
it, and a being's class may wrap an MCP server; neither is a ward function.
|
|
378
|
+
Models are occupants like anyone else. What stays Quo's: silence, unreached,
|
|
379
|
+
ids, invitations, knock and take, the digest rule. MCP is the description
|
|
380
|
+
and value layer. Quo is the relation layer.
|
|
381
|
+
|
|
382
|
+
### Ids
|
|
383
|
+
|
|
384
|
+
An id is minted by the being, bound by the ward, permanent, and never crosses
|
|
385
|
+
the door. Her id for you and your id for her are unrelated. One id names one
|
|
386
|
+
record, and standings and occupants are one namespace: invite refuses an id a
|
|
387
|
+
standing holds, take refuses an id an occupant holds.
|
|
388
|
+
|
|
389
|
+
Two words are the ward's and no being may mint them: `OWNER` and `PUBLIC`,
|
|
390
|
+
the reserved askers of the ward-to-being edge. The ward speaks at every
|
|
391
|
+
being's door as `{ id: 'OWNER' }` when it runs her describe for its owner,
|
|
392
|
+
and an occupant wearing that name would be two parties with one face.
|
|
393
|
+
`PUBLIC` guards nothing today, because a public asker is `{}` and carries no
|
|
394
|
+
id, and it is claimed now while claiming it is free.
|
|
395
|
+
|
|
396
|
+
Three more are refused for a plainer reason: `knock`, `take` and `remove`
|
|
397
|
+
are the calls on `standings`, and they share that object with the ids she
|
|
398
|
+
takes. A standing under one of those names would be unreachable, so invite
|
|
399
|
+
and take refuse them like the ward's words.
|
|
400
|
+
|
|
401
|
+
### Cells
|
|
402
|
+
|
|
403
|
+
Cells are I-JSON values. The ward may persist them. A restart is silent: she
|
|
404
|
+
is constructed again with the same cells.
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
cells
|
|
408
|
+
standings: { id: { id, digest, blueprint, seen } } ward writes all four
|
|
409
|
+
occupants: { id: { id, notes } } ward writes id, she writes notes
|
|
410
|
+
class ward writes it at boot, so a restart finds her
|
|
411
|
+
anything else hers
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
- `digest` is the hash of the blueprint she last fetched by the empty ask.
|
|
415
|
+
- `blueprint` is that blueprint.
|
|
416
|
+
- `seen` is the digest her ward last saw arrive with an answer. A silent
|
|
417
|
+
refresh leaves it untouched.
|
|
418
|
+
- `notes` is hers. Quo never reads it. Tier, expiry, kinship between an
|
|
419
|
+
occupant and a standing that are the same far being: all hers.
|
|
420
|
+
|
|
421
|
+
## Relations
|
|
422
|
+
|
|
423
|
+
### How a relation is born
|
|
424
|
+
|
|
425
|
+
There is one way in and one way back.
|
|
426
|
+
|
|
427
|
+
1. She wants B as an occupant. She mints an id, in her own time, and asks
|
|
428
|
+
her ward for an invitation for it. The occupant record exists from that
|
|
429
|
+
moment. The invitation is a value: it goes by mail, paper, or inside the
|
|
430
|
+
args of some other ask. Quo does not care how.
|
|
431
|
+
2. B consumes it by knocking: an ask carrying the invitation. B's ward
|
|
432
|
+
seals it and hands it to its harbor's carry, and her ward, which
|
|
433
|
+
recognises its own seal, and binds the
|
|
434
|
+
arrival to the id she minted. From now on every ask from B arrives as
|
|
435
|
+
that id. The first such ask is how she learns the invitation was
|
|
436
|
+
consumed. Whether it is still welcome is hers: expiry, one use, anything,
|
|
437
|
+
lives in her notes.
|
|
438
|
+
3. If she answered, B may take her as a standing, minting B's own id for
|
|
439
|
+
her. That is the only moment a standing is born. Take binds B's side
|
|
440
|
+
only; the knock bound hers. Take before a knock, or after a silent one,
|
|
441
|
+
births nothing. B may also knock, say hello, and never call again. She
|
|
442
|
+
keeps B as an occupant until she decides otherwise. She never learns that
|
|
443
|
+
B walked away.
|
|
444
|
+
4. B wants her to reach back? B mints an id, makes an invitation, and puts
|
|
445
|
+
it in the args. She knocks it and takes B if she wants. Two invitations,
|
|
446
|
+
two relations, each chosen by its owner.
|
|
447
|
+
|
|
448
|
+
She is always the initiator. Nobody becomes her occupant unless she invited
|
|
449
|
+
them. Nobody becomes her standing unless she knocked and chose to take, or
|
|
450
|
+
her owner knocked for her.
|
|
451
|
+
|
|
452
|
+
### What take does
|
|
453
|
+
|
|
454
|
+
Take consumes. Until take the relation lives in a knock record, under the far
|
|
455
|
+
ward and the heir; from take it lives in the standing, under her id, and
|
|
456
|
+
never in both. Take copies the keys and the count the knock left, deletes the
|
|
457
|
+
knock record, and the invitation is spent for her: knocking it again is an
|
|
458
|
+
ask on the standing, and taking it again births nothing.
|
|
459
|
+
|
|
460
|
+
Take waits, because a relation has one line of keys and one count, and every
|
|
461
|
+
word spoken for it goes in order. Taken out of that order it would read
|
|
462
|
+
between a knock's send and its answer, and the standing would be born holding
|
|
463
|
+
a number the far door has already honoured and a key it has already rotated
|
|
464
|
+
past. So take goes in the line with the rest.
|
|
465
|
+
|
|
466
|
+
### One relation, one lane
|
|
467
|
+
|
|
468
|
+
Every send on one relation waits for the one before it. The rotation is a
|
|
469
|
+
conversation: a send reads the key that speaks now, announces the next, and
|
|
470
|
+
moves once the far door has answered, and two sends interleaving on one
|
|
471
|
+
relation would read each other's half-written keys. Lanes are per relation:
|
|
472
|
+
beings still ask concurrently, and a slow relation never holds up another.
|
|
473
|
+
Before take the lane is the invitation's; after take it is the standing's,
|
|
474
|
+
and a knock on a taken invitation joins the standing's lane.
|
|
475
|
+
|
|
476
|
+
The keys an ask sends under are read when she calls, not when the lane
|
|
477
|
+
reaches her: an ask issued while the standing stood is answered even if she
|
|
478
|
+
drops it in the next line. An ask on a standing she has already dropped is
|
|
479
|
+
silence, and so is a knock on the invitation it was born on: after take the
|
|
480
|
+
invitation names the standing and nothing else, so it is exactly as alive as
|
|
481
|
+
the standing is.
|
|
482
|
+
|
|
483
|
+
### The public being
|
|
484
|
+
|
|
485
|
+
A ward may have one public being, and no more. She is an ordinary being,
|
|
486
|
+
booted by the owner and marked public. Arrivals for no heir reach her. Her
|
|
487
|
+
asker is `{}`: she is asked by anyone, and she can tell. She may hold
|
|
488
|
+
standings. She may invite, and an occupant of hers arrives named while
|
|
489
|
+
strangers still arrive as `{}`. A ward without a public being answers
|
|
490
|
+
arrivals for no heir with silence.
|
|
491
|
+
|
|
492
|
+
An invitation to her is `{ ward }` alone. One with a secret and no heir is
|
|
493
|
+
not an invitation. She may be taken as a standing on that invitation, and
|
|
494
|
+
such a standing has no heir and never rotates.
|
|
495
|
+
|
|
496
|
+
She is reached without a heir, so the door keeps nothing for whoever asked:
|
|
497
|
+
no key it vouched for, and no count. The signature is still checked, under
|
|
498
|
+
whatever key signed. Once-only delivery does not reach her: the same sealed
|
|
499
|
+
bytes presented twice are delivered twice, and the number the payload carries
|
|
500
|
+
is required and honoured by nobody. A count per voice would be memory a
|
|
501
|
+
stranger chooses the size of, which is the thing the span exists to refuse.
|
|
502
|
+
|
|
503
|
+
So her answer must be safe to repeat. This is her obligation, not a gap at
|
|
504
|
+
the door. Anything that must happen once lives behind an invitation, where
|
|
505
|
+
there is a heir and a count.
|
|
506
|
+
|
|
507
|
+
### How a relation crosses wards
|
|
508
|
+
|
|
509
|
+
1. She mints an id. Her ward mints a heir for it, keeps the pk, and hands
|
|
510
|
+
her an invitation: her ward's pk, the heir pk, the heir secret. A value.
|
|
511
|
+
It goes anywhere. Rotation one.
|
|
512
|
+
2. The invitation is consumed by an ask at some ward, and that ask names a
|
|
513
|
+
being there. Either B holds it, because a standing put it in her args,
|
|
514
|
+
and B knocks. Or an owner holds it, because it came by mail, and the
|
|
515
|
+
owner's ask knocks for a being of theirs, new or existing. There is no
|
|
516
|
+
third way, because a relation cannot end in thin air.
|
|
517
|
+
3. B's ward mints her own key, signs the knock with the heir secret,
|
|
518
|
+
announces her key inside, seals the box to her ward's padlock, and
|
|
519
|
+
hands it to carry for the ward pk. No relation between the two wards is needed,
|
|
520
|
+
ever. Her ward opens its own box, admits the heir, verifies, binds the
|
|
521
|
+
id to B's key, and hands her the arrival as the id she minted. The heir
|
|
522
|
+
is spent. Rotation two.
|
|
523
|
+
4. B's ward remembers the knock was answered, and lets B take. From then on
|
|
524
|
+
every ask from B on that standing is signed by her current key, announces
|
|
525
|
+
her next, and is sealed to the same padlock. Every answer rotates her.
|
|
526
|
+
5. Same ward: her ward recognises its own pk and delivers without the
|
|
527
|
+
harbor. Same harbor: the harbor finds the pk in its own map and calls the
|
|
528
|
+
other door. Two harbors: the wire. The ward's code has one branch between
|
|
529
|
+
these, and the being has none. A same-ward knock is sealed, signed, and
|
|
530
|
+
judged exactly like a far one.
|
|
531
|
+
|
|
532
|
+
## The door
|
|
533
|
+
|
|
534
|
+
### Keys
|
|
535
|
+
|
|
536
|
+
One 32-byte seed is one key. A being's key signs and never seals. The ward's
|
|
537
|
+
key does both: an Ed25519 pair to sign replies, an X25519 padlock every ask
|
|
538
|
+
is sealed to. Beings never own a padlock.
|
|
539
|
+
|
|
540
|
+
- **The ward key** comes from the seed. Its pk on the wire is the signing pk
|
|
541
|
+
then the padlock, 128 hex, and it routes.
|
|
542
|
+
- **The heir** is the key the inviting ward mints at invite, for one id. It
|
|
543
|
+
keeps the heir's pk beside the id and gives the secret away. The
|
|
544
|
+
invitation IS the heir: ward pk, heir pk, heir secret. Rotation one: the
|
|
545
|
+
occupant already has a key, and the inviter chose it.
|
|
546
|
+
- **Her own key** is what the knocker mints at knock. She signs the knock
|
|
547
|
+
with the heir and announces her own key in it. The door binds the id to
|
|
548
|
+
her key and the heir dies as it speaks. Rotation two: from now on she
|
|
549
|
+
signs with a key the inviter never held. A knock that announces nothing
|
|
550
|
+
binds nothing.
|
|
551
|
+
- **Next.** Every ask she sends announces the key she will sign with next.
|
|
552
|
+
The door holds two pks for her: the one that may speak now, and the one
|
|
553
|
+
it vouched for. Whichever speaks first wins, and the other dies. There is
|
|
554
|
+
no rotate call: every honoured ask rotates, and a lost reply strands
|
|
555
|
+
nobody. Her side moves to the announced key only when an answer came back
|
|
556
|
+
that was not silence; the door has already moved, and admits both.
|
|
557
|
+
|
|
558
|
+
The knock is the one place where both sides cannot be brought back into
|
|
559
|
+
agreement by that rule alone. If the reply to the first knock is lost, the
|
|
560
|
+
door has spent the heir and rotated to the key she announced, and she does
|
|
561
|
+
not know it: knocking as the heir again would be refused for good. She cannot
|
|
562
|
+
learn which case she is in, so she asks, and the asking tells her. She sends
|
|
563
|
+
under her own key first. If the door heard, that is the key it admits and she
|
|
564
|
+
is answered. If it did not, the key means nothing there and the ask is
|
|
565
|
+
refused; a refusal at a door writes nothing, so the heir is untouched and she
|
|
566
|
+
knocks as the heir, as she would have. One extra round trip, in the one case
|
|
567
|
+
where a reply was lost. It gives a stranger nothing: whoever holds the
|
|
568
|
+
invitation could always knock as the heir, and her own key is admitted only
|
|
569
|
+
where the door already bound it to her.
|
|
570
|
+
|
|
571
|
+
Three keys per relation over its life, one rule at the door: the key I hold
|
|
572
|
+
for you may speak, and so may the key it announced last time, and each
|
|
573
|
+
number once.
|
|
574
|
+
|
|
575
|
+
Every key in a relation was minted by one side and its secret never left
|
|
576
|
+
that side, except the heir, which the inviter gives away and which dies the
|
|
577
|
+
first time it speaks. No key serves two relations. No key outlives its
|
|
578
|
+
relation. A ward keeps only the last eight pks a being's side minted, which
|
|
579
|
+
is enough to name the keys in play.
|
|
580
|
+
|
|
581
|
+
### The count
|
|
582
|
+
|
|
583
|
+
Every ask she sends carries the next number in one unbroken count for that
|
|
584
|
+
relation, starting at one, and the door honours each number once. The number
|
|
585
|
+
rides inside the signed payload, so bytes caught on the road carry the number
|
|
586
|
+
they were sent under: they cannot be renumbered without breaking her
|
|
587
|
+
signature, and they are refused as themselves. A caller who means to ask
|
|
588
|
+
again asks again, under the next number, and is heard. Retry and
|
|
589
|
+
fire-and-forget stay hers to build; only the accident and the interception
|
|
590
|
+
are refused.
|
|
591
|
+
|
|
592
|
+
The door keeps the highest number honoured and which numbers below it are
|
|
593
|
+
spent, out to a span of sixty-four, because a door that remembered every
|
|
594
|
+
number ever seen would be a door with unbounded memory. A number above the
|
|
595
|
+
mark is honoured and moves it; a number inside the span is honoured once; a
|
|
596
|
+
number at or below the span is refused. The mark and the spent list are in
|
|
597
|
+
the partition, because a door that forgets what it honoured honours it again
|
|
598
|
+
after a restart.
|
|
599
|
+
|
|
600
|
+
Her side keeps the number it spoke under whether or not a reply came back. A
|
|
601
|
+
reply lost on the way back is a door that has already honoured the number,
|
|
602
|
+
and offering it twice would silence the relation for good. The count carries
|
|
603
|
+
over from the knock into the standing at take.
|
|
604
|
+
|
|
605
|
+
### The allowance
|
|
606
|
+
|
|
607
|
+
Every ask carries what it may still spend: **time**, in milliseconds. It
|
|
608
|
+
rides inside the signed payload, so a budget caught on the road cannot be
|
|
609
|
+
widened by whoever caught it.
|
|
610
|
+
|
|
611
|
+
A being who says nothing gets her ward's default and never thinks about it.
|
|
612
|
+
A being who wants to say so passes a third argument. What she is given is
|
|
613
|
+
what she asked for held to what her ward allows: a number that is not a
|
|
614
|
+
positive whole number falls to the default rather than refusing her, and one
|
|
615
|
+
above the ceiling is the ceiling, silently, because the ceiling is not hers
|
|
616
|
+
to know. Budget is granted by a ward, never minted by a being. How wide the
|
|
617
|
+
default and the ceiling are is the ward's own; this ward's are thirty
|
|
618
|
+
seconds by default and five minutes at the ceiling, in
|
|
619
|
+
`src/ward/allowance.ts`.
|
|
620
|
+
|
|
621
|
+
They are two numbers and not one, because the third argument is for both
|
|
622
|
+
directions. A being who knows her own work asks for less on the ask she wants
|
|
623
|
+
back quickly, and for more on the one she knows is slow. A ceiling equal to
|
|
624
|
+
the default would leave her only the narrowing half, and would give the ward
|
|
625
|
+
no way to allow one long piece of work without making every ask that
|
|
626
|
+
patient.
|
|
627
|
+
|
|
628
|
+
The sender's wait has an end, and this is the one thing the ward times. The
|
|
629
|
+
bound covers the whole of an ask, from the moment she calls: a relation that
|
|
630
|
+
comes back round on itself is stopped at its own lane, before a byte is
|
|
631
|
+
sealed, and a bound that watched only the wire would never see it. This is
|
|
632
|
+
what makes three answers three. A wait that does not end is not an object,
|
|
633
|
+
not silence and not unreached, and before the allowance a cycle of legal asks
|
|
634
|
+
could produce one: A asks B, B answering asks A back, A answering asks B on
|
|
635
|
+
the relation the first ask still holds. Now the innermost wait gives up, the
|
|
636
|
+
silence unwinds outward, and the ask ends.
|
|
637
|
+
|
|
638
|
+
**A wait that ran out is silence, never unreached.** Unreached promises
|
|
639
|
+
nothing was delivered and is safe to ask again. A bound that expired knows no
|
|
640
|
+
such thing: the far door may have heard and be working still. What comes
|
|
641
|
+
back late is not read.
|
|
642
|
+
|
|
643
|
+
The harbor keeps a patience of its own, and the two never read each other.
|
|
644
|
+
What a harbor hands back as nothing came back is unreached, and unreached
|
|
645
|
+
says nothing was delivered: a harbor may answer it only where it knows the
|
|
646
|
+
bytes never arrived, no reach for that pk, a socket that would not open, a
|
|
647
|
+
link that is down. A harbor that sent them and then gave up knows no such
|
|
648
|
+
thing, and must not answer at all; the ward's bound will ring, and that is
|
|
649
|
+
silence, which promises nothing. A harbor that throws has answered nothing
|
|
650
|
+
in a louder voice, and the ward reads it as nothing: unreached. So a harbor
|
|
651
|
+
may hold a shorter patience than the ward for its own reasons, a socket it
|
|
652
|
+
wants back or a queue it will not grow, and whichever ends first ends the
|
|
653
|
+
ask.
|
|
654
|
+
|
|
655
|
+
The receiving door reads the allowance before anything is done under it. A
|
|
656
|
+
payload whose time is not a whole number above zero is malformed, D2, and
|
|
657
|
+
is refused as one. Nothing is spent and nothing rotates, because nothing
|
|
658
|
+
was heard.
|
|
659
|
+
|
|
660
|
+
Each ask is bounded on its own. The time an arriving call has left does not
|
|
661
|
+
bound the asks a being makes while answering it, and an ask carries no count
|
|
662
|
+
of doors; see Closed. Every wait still ends.
|
|
663
|
+
|
|
664
|
+
### Judgment
|
|
665
|
+
|
|
666
|
+
Every arrival is judged by the door, and named by it or falls silent.
|
|
667
|
+
|
|
668
|
+
1. Open the box with the ward's padlock. Read whether it is for a heir or
|
|
669
|
+
for nobody. Parse the payload and refuse any field of the wrong shape.
|
|
670
|
+
2. For nobody: find the public being, verify the signature under the key the
|
|
671
|
+
payload names, and dispatch as `{}`. Nothing is written.
|
|
672
|
+
3. For a heir: admit the signer if it is the key held for that heir or the
|
|
673
|
+
key that key announced. Find the being who minted the id and check she
|
|
674
|
+
still holds the occupant. Verify the signature. Only then spend the
|
|
675
|
+
number and settle the keys, so a stranger cannot burn a number she could
|
|
676
|
+
not sign for, and the same bytes twice rotate nothing. Dispatch as
|
|
677
|
+
`{ id }`.
|
|
678
|
+
4. Catch every throw and answer silence. Treat an unreached shape coming out
|
|
679
|
+
of a being as silence.
|
|
680
|
+
5. On every answered named ask, run her describe for that asker in process,
|
|
681
|
+
hash it, and put the digest next to the object in the reply. One trip. A
|
|
682
|
+
describe that throws or falls silent costs the digest and nothing else.
|
|
683
|
+
On the empty ask the reply carries no digest; her side hashes what came.
|
|
684
|
+
6. Answer by the same call the ask came in on.
|
|
685
|
+
|
|
686
|
+
A stranger is bytes the ward cannot admit, and every way of being one is
|
|
687
|
+
listed, with a number, under "Silence, unreached, error". The public being
|
|
688
|
+
is the one door a stranger may walk through, and only because she chose it.
|
|
689
|
+
|
|
690
|
+
### Inner and outer
|
|
691
|
+
|
|
692
|
+
Inner: being keys, ids, cells, stances, doors, the bind table, every secret,
|
|
693
|
+
every method, every arg, every answer. Nothing inner is ever readable in the
|
|
694
|
+
bytes, and `test/ward.test.ts` reads every byte string that crossed to check.
|
|
695
|
+
|
|
696
|
+
Outer: ward pks, heir pks, ephemeral pks, ciphertext. That is the whole of
|
|
697
|
+
what the wire sees. The one value a being holds that is outer is the
|
|
698
|
+
invitation, which she carries opaque and never opens.
|
|
699
|
+
|
|
700
|
+
A far ward binds a heir to a pk, never to a being. Two far beings that talk
|
|
701
|
+
to two beings of mine see two heirs and one ward pk, and two relations that
|
|
702
|
+
share no key learn nothing from each other.
|
|
703
|
+
|
|
704
|
+
### The wire
|
|
705
|
+
|
|
706
|
+
Four algorithms, named once and never negotiated: Ed25519 signs, X25519
|
|
707
|
+
agrees, SHA-256 hashes, AES-256-GCM encrypts with key and nonce derived
|
|
708
|
+
together by HKDF-SHA-256 under an empty salt and the label `quo-seal`. The
|
|
709
|
+
ephemeral pk is the additional authenticated data of every box. A small-order
|
|
710
|
+
public key verifies nothing; an all-zero agreement is refused. All four are
|
|
711
|
+
in WebCrypto, which is why the ward names no package -- on any terrain that
|
|
712
|
+
carries them. SHA-256, AES-GCM and HKDF are everywhere; the two curves are
|
|
713
|
+
recent, and a terrain without them is a terrain no ward runs on. The floor is
|
|
714
|
+
named and probed in `test/floor.test.ts`. `crypto.subtle` is read at every use
|
|
715
|
+
and never captured at load, so a page without one -- a plain http:// origin, a
|
|
716
|
+
sandboxed frame -- fails at the first ask with one sentence rather than deep
|
|
717
|
+
inside a key import. The arithmetic is `src/ward/arithmetic.ts`, the seal is
|
|
718
|
+
`src/ward/seal.ts`, and `vectors/arithmetic.json` and `vectors/framing.json`
|
|
719
|
+
hold fixed inputs and outputs so a kit in another language proves it agrees
|
|
720
|
+
on the bytes.
|
|
721
|
+
|
|
722
|
+
```
|
|
723
|
+
ask on the wire box = ephemeral X25519 pk (32) || AES-GCM( payload || signature (64) )
|
|
724
|
+
sealed to the ward padlock. nothing rides outside it.
|
|
725
|
+
payload = JSON { to, by, next, seq, time, hops?, method?, args }
|
|
726
|
+
signed by `by`. to, by, next: 64 hex. to, next may be null.
|
|
727
|
+
to names the heir, or null for the public being.
|
|
728
|
+
hops is reserved: a whole number, never below zero,
|
|
729
|
+
refused at zero, and nothing sets it.
|
|
730
|
+
reply on the wire box = ephemeral X25519 pk (32) || AES-GCM( reply || signature (64) )
|
|
731
|
+
sealed to the ask's ephemeral pk, signed by the ward key
|
|
732
|
+
reply = JSON { object, seen } | { silence: true }
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Nothing rides outside the box, and `to` is the reason to say so. A heir is
|
|
736
|
+
the one name in a relation that never rotates, while the keys under it
|
|
737
|
+
rotate on every ask. Outside the box it would be a handle on that relation
|
|
738
|
+
that never changes: an intermediary carrying the bytes could tell one
|
|
739
|
+
relation from another and follow it for as long as it lasts, and so could
|
|
740
|
+
anyone who ever saw the invitation, which carries that same heir. What
|
|
741
|
+
carries an ask to a ward is the ward pk, which a harbor holds itself and
|
|
742
|
+
never reads from these bytes, so the door loses nothing by opening every ask
|
|
743
|
+
with its own padlock and reading `to` after. What an intermediary still
|
|
744
|
+
learns is the ward it is carrying to, and that is the price of being
|
|
745
|
+
reachable.
|
|
746
|
+
|
|
747
|
+
The sender keeps the ephemeral secret of the box until the reply comes and
|
|
748
|
+
opens the reply with it. A reply that does not open, or is not signed by the
|
|
749
|
+
ward it was sent to, is silence. Unreached never crosses: the wire's nothing
|
|
750
|
+
is it.
|
|
751
|
+
|
|
752
|
+
## The ward
|
|
753
|
+
|
|
754
|
+
### What a ward is
|
|
755
|
+
|
|
756
|
+
One process of its harbor. A being plus ward functions. It has one voice, the
|
|
757
|
+
door, and every arrival at that door is judged by it and named by it or falls
|
|
758
|
+
silent. It keeps every being it booted, builds every stance, mints every
|
|
759
|
+
key, seals every ask that leaves, and unseals every one that arrives. Its
|
|
760
|
+
beings trust it blindly. It trusts its harbor the same way.
|
|
761
|
+
|
|
762
|
+
Its ward is itself. It boots itself as the first being in its own map, under
|
|
763
|
+
its own pk, and its own stance is built by the same code that builds every
|
|
764
|
+
being's. Its cells are its partition, all the way down.
|
|
765
|
+
|
|
766
|
+
It owns nothing durable. It is three things, all in the harbor's hands:
|
|
767
|
+
its seed, its partition, and the classes its beings are made of. The
|
|
768
|
+
partition names each class and the harbor holds the bodies, so a ward is
|
|
769
|
+
whole only where all three are, and a harbor that keeps one remembers where
|
|
770
|
+
the other two came from. It writes as it runs and never decides, asks, or
|
|
771
|
+
learns whether anything was kept. Between one run and the next, a ward is
|
|
772
|
+
nothing at all.
|
|
773
|
+
|
|
774
|
+
One ward is one runtime and one language, and every being in it shares both.
|
|
775
|
+
A harbor that wants two languages starts two wards at least. Under
|
|
776
|
+
`src/being/`, `src/ward/`, `src/harbor/` and `src/conformance/` no runtime is
|
|
777
|
+
named. The whole
|
|
778
|
+
platform surface is the language plus six globals -- `crypto`, `TextEncoder`,
|
|
779
|
+
`TextDecoder`, `structuredClone`, `setTimeout`, `atob` -- and that list is a
|
|
780
|
+
promise, not an accident: `test/terrain.test.ts` fails the build both when a
|
|
781
|
+
file names a platform and when it reaches for a global outside the list.
|
|
782
|
+
|
|
783
|
+
The package exports raw TypeScript, extension and all, on every specifier, so
|
|
784
|
+
Node loads it directly and every other terrain reaches a ward through a
|
|
785
|
+
bundler. `test/bundle.test.ts` bundles the three words and runs them, and
|
|
786
|
+
asserts the artefact carries nothing a terrain cannot provide.
|
|
787
|
+
|
|
788
|
+
The conformance suite is the ward's truth, so it has to be checkable wherever
|
|
789
|
+
a ward runs. It names no runtime either: its runner is handed in, and the five
|
|
790
|
+
assertions it makes are `src/conformance/assert.ts`, held to
|
|
791
|
+
`node:assert/strict`'s own behaviour by `test/assert.test.ts`.
|
|
792
|
+
`test/terrain/exercise.ts` is what every terrain runs -- the whole suite,
|
|
793
|
+
under all three topologies, and the floor probe. Node is the reference: no
|
|
794
|
+
terrain carries a count of its own, so a test added to the suite is demanded
|
|
795
|
+
of every terrain at once. Five run it: Node, a browser, workerd, Deno and
|
|
796
|
+
Bun. Bun is the one that is not V8, so "the language alone" is checked
|
|
797
|
+
against two implementations of the language and not one.
|
|
798
|
+
|
|
799
|
+
`npm run check` is Node alone, and stays fast. `npm run check:terrain` is the
|
|
800
|
+
browser and the edge, kept apart because a browser is a download and not a
|
|
801
|
+
package: a fresh clone needs `npx playwright install chromium` first, and is
|
|
802
|
+
told so in one sentence rather than a stack trace.
|
|
803
|
+
|
|
804
|
+
### The partition
|
|
805
|
+
|
|
806
|
+
Everything durable a ward has is here, every secret included, and nothing
|
|
807
|
+
here is ever in a being's cells. Values only, so the harbor may persist it as
|
|
808
|
+
it likes: as this process's objects, as a row, as a line of JSON on a disk. A
|
|
809
|
+
reboot from its JSON is the same ward. The shape is `src/ward/partition.ts`.
|
|
810
|
+
|
|
811
|
+
```
|
|
812
|
+
partition
|
|
813
|
+
version 'pre-1.0.0'
|
|
814
|
+
beings key -> her cells, with class
|
|
815
|
+
bind key -> her bind table
|
|
816
|
+
standings id -> { ward, heir | null, current, next, seq } her keys for a standing
|
|
817
|
+
occupants id -> heir pk
|
|
818
|
+
knocks <ward>:<heir> | public:<ward> -> { current, next, spoke, seq } before take
|
|
819
|
+
answered <ward>:<heir> | public:<ward> -> true knocked and answered
|
|
820
|
+
minted the last eight pks her side minted
|
|
821
|
+
heirs heir pk -> { being, id, current, announced, fresh, mark, spent } the door's view of every occupant
|
|
822
|
+
public the one public being's key, or null
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
A knock record is filed under the ward and the heir together, never the heir
|
|
826
|
+
alone: an invitation carries the heir to whoever it is for, and anyone
|
|
827
|
+
holding one could quote that heir back inside an invitation naming a ward of
|
|
828
|
+
their own.
|
|
829
|
+
|
|
830
|
+
The version is one value and it does not move until 1.0.0. There is nobody
|
|
831
|
+
holding a partition of another shape to tell apart: if the shape changes, a
|
|
832
|
+
partition is thrown away, not migrated and not counted. A ward opening a
|
|
833
|
+
version it cannot read throws at birth and does not boot. Birth is where a
|
|
834
|
+
ward is allowed to be loud; silence is the door's word, for asks that were
|
|
835
|
+
made, and no ask has been made yet.
|
|
836
|
+
|
|
837
|
+
What a ward can read is a list, `READS`, and today it has one member. The
|
|
838
|
+
list and the step that carries an older partition forward exist before
|
|
839
|
+
1.0.0 because after it there is nowhere to put them. A partition holds every
|
|
840
|
+
secret and every relation a world has: the first shape change after the
|
|
841
|
+
freeze cannot be answered by throwing it away, and a ward that meets a shape
|
|
842
|
+
it has no step for refuses to boot with all of it still inside. Throwing a
|
|
843
|
+
partition away is what a version means before 1.0.0, and carrying it forward
|
|
844
|
+
is what it means after; the seam is cut now so the second is possible.
|
|
845
|
+
|
|
846
|
+
### Restart
|
|
847
|
+
|
|
848
|
+
A restart is silent because the ward cannot tell rebirth from birth. Every
|
|
849
|
+
being whose cells record a class is constructed again, unasked, with the
|
|
850
|
+
same cells; relations and keys are intact on both sides; a knock answered
|
|
851
|
+
before the restart can be taken after it; a door that restarts still refuses
|
|
852
|
+
what it already honoured.
|
|
853
|
+
|
|
854
|
+
A constructor that throws on a restart takes only herself down: she is
|
|
855
|
+
absent this run, no door, so silence at her door, and her cells sit untouched
|
|
856
|
+
waiting for the run that can read them. The ward is up and so is everyone
|
|
857
|
+
else. A class the harbor no longer knows is the same absence, and so is a
|
|
858
|
+
class the harbor holds at a body the cells were not written for: which body
|
|
859
|
+
a ward reboots on is the harbor's decision, and a ward cannot tell an old
|
|
860
|
+
one from a new one. Loss of the partition is loss of every relation,
|
|
861
|
+
announced to nobody.
|
|
862
|
+
|
|
863
|
+
### The owner
|
|
864
|
+
|
|
865
|
+
The owner is a role, not an identity, and it has a root. The root owner is
|
|
866
|
+
whoever holds the ward's ask pointer. The harbor hands that pointer to one
|
|
867
|
+
holder, by the device's rules: a shell, a UI, a socket only the device's
|
|
868
|
+
user can read. Lose the pointer, lose the root; there is no recovery inside
|
|
869
|
+
Quo.
|
|
870
|
+
|
|
871
|
+
Every other owner is an occupant of the ward itself. The ward is a being of
|
|
872
|
+
its own map, and the root may invite on it like on any being; whoever knocks
|
|
873
|
+
with that invitation is an owner, reached through the door, named by the id
|
|
874
|
+
the root chose, counted and rotated like any relation, and removed like any
|
|
875
|
+
occupant. That is how a ward is piloted from another device: the piloting
|
|
876
|
+
ward holds a standing at it, and every owner ask is a sealed ask. Only the
|
|
877
|
+
root may invite on the ward: an owner at the door asking to is refused, so
|
|
878
|
+
a carried key pilots and never hands piloting on. Ownership moves by the
|
|
879
|
+
root inviting one owner and removing another, and a migrated ward carries
|
|
880
|
+
its owners in its bind table, so the harbor that receives it does not
|
|
881
|
+
become one.
|
|
882
|
+
|
|
883
|
+
The root is not an occupant. It holds no heir, no key, no invitation. The
|
|
884
|
+
ward names every arrival on the ask `{ id: 'OWNER' }`, the third asker of the
|
|
885
|
+
ward-to-being edge, and answers it as such; an owner at the door arrives as
|
|
886
|
+
her id. What an owner can do is exactly what a being cannot: boot, place a
|
|
887
|
+
relation into a being of the ward, and take one out of her. The root's asks
|
|
888
|
+
carry no allowance of their own.
|
|
889
|
+
|
|
890
|
+
```
|
|
891
|
+
ask() -> { asks: [boot, invite, knock, remove],
|
|
892
|
+
notes: { pk, beings: { key: { class, public, digest } } } }
|
|
893
|
+
ask('boot', { key, class, public? }) -> { booted: key } | { error }
|
|
894
|
+
ask('invite', { being, id }) -> invitation | null | { error }
|
|
895
|
+
ask('knock', { being | { boot: class, key },
|
|
896
|
+
id, invitation, method?, args?, wanted? })
|
|
897
|
+
-> { taken: id | null, answer } | { error: 'silence' | 'unreached' | ... }
|
|
898
|
+
ask('remove', { being, id }) -> { removed: id } | { error }
|
|
899
|
+
anything else -> { error: 'unknown ask' }
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
- The owner is a caller like any other: `wanted` says what its knock may
|
|
903
|
+
spend, and saying nothing is the ward's default, exactly as for a being.
|
|
904
|
+
- The describe runs every being's own describe as `OWNER` and hashes it; a
|
|
905
|
+
being that throws or falls silent there shows a null digest.
|
|
906
|
+
- Boot refuses a key already booted, a class the harbor does not know, and a
|
|
907
|
+
second public being; a boot that made nobody leaves the partition as it
|
|
908
|
+
found it. A throw at birth is that boot's error, and nothing half-lives.
|
|
909
|
+
- Invite on the ward's own pk mints an owner, and only the root may ask it:
|
|
910
|
+
from the door it is answered as an invite on nobody. The ward has
|
|
911
|
+
occupants, its owners, and no standings: a knock for the ward itself is a
|
|
912
|
+
knock for nobody, because the ward asks nobody anything.
|
|
913
|
+
- The owner's knock is a being's knock made for her. The ward knocks under a
|
|
914
|
+
key it mints for her, and if answered, takes under the id the owner gave
|
|
915
|
+
and writes the standing into her cells. She finds it there. Her owner
|
|
916
|
+
chose her class and her cells at boot; her owner may choose a relation for
|
|
917
|
+
her too. The boot form names a being new or existing: a key already booted
|
|
918
|
+
is a being of theirs, not a class that failed.
|
|
919
|
+
- The owner's remove is the mirror of its knock: a relation out of a being,
|
|
920
|
+
by id, and the id may be an occupant or a standing, since the two share
|
|
921
|
+
one namespace. The being's own remove says nothing; the owner hears
|
|
922
|
+
objects, so this one says what it removed, and that there was nothing when
|
|
923
|
+
there was nothing. Remove on the ward's own pk unseats an owner, and only
|
|
924
|
+
the root may ask it, for the reason only the root may invite one: from the
|
|
925
|
+
door it is answered as a remove on nobody. Ownership moves by the root
|
|
926
|
+
alone.
|
|
927
|
+
|
|
928
|
+
The door is a ward function. It is not the ward's answer: it judges before
|
|
929
|
+
anything is named, and a being's answer only ever receives named askers. The
|
|
930
|
+
ask is the ward's answer, with the asker filled in as owner. Ward functions
|
|
931
|
+
are on the object, and no stance and no standing reaches them.
|
|
932
|
+
|
|
933
|
+
### Responsibilities, and not
|
|
934
|
+
|
|
935
|
+
The ward:
|
|
936
|
+
|
|
937
|
+
- Derives its pk from its seed and names itself outward by that and nothing
|
|
938
|
+
else. Mints every other key from the ground's random, one per side of one
|
|
939
|
+
relation.
|
|
940
|
+
- Instantiates beings when the owner asks, by class name. It builds the
|
|
941
|
+
stance, names the class, and receives the object from the harbor's
|
|
942
|
+
instantiate call. It never sees a class body, its own included.
|
|
943
|
+
- Keeps, per being, the cells, the standings and the occupants, in the
|
|
944
|
+
partition, and outside her cells the bind table. Never reads notes.
|
|
945
|
+
- Mints a heir for every id a being mints, keeps its pk beside the id, and
|
|
946
|
+
hands her the invitation. Names every arrival signed by the key it holds
|
|
947
|
+
for that heir by the id she minted.
|
|
948
|
+
- Seals every ask that leaves to the far ward's padlock, signed by the
|
|
949
|
+
standing's own key, announcing its next, carrying its count and its
|
|
950
|
+
allowance. Delivers to its own pk without the harbor. Hands every other
|
|
951
|
+
ward pk to carry. Bounds the wait.
|
|
952
|
+
- Tells its own being the truth about her asks: an answer with its digest
|
|
953
|
+
written to seen, silence when bytes came back and said nothing, unreached
|
|
954
|
+
when nothing came back.
|
|
955
|
+
- Runs her answers concurrently and never serializes her. Two different asks
|
|
956
|
+
may both be inside her answer at once, one paused, one running. Her cells
|
|
957
|
+
are hers to guard between one line and the next.
|
|
958
|
+
|
|
959
|
+
Not stressed with:
|
|
960
|
+
|
|
961
|
+
- Where any pk lives. Sockets, URLs, DNS, containers, planets. It knows pks
|
|
962
|
+
and nothing under them. The wire, retries, or the directory.
|
|
963
|
+
- Storage. It writes into its partition and does not know what medium that
|
|
964
|
+
is, whether it persists, or when.
|
|
965
|
+
- Defending against its harbor. A ward that distrusts its harbor has no
|
|
966
|
+
move, so Quo gives it none. The harbor vouches, the ward trusts. A being
|
|
967
|
+
trusts her ward, a ward trusts its harbor, a harbor trusts its device. A
|
|
968
|
+
broken vouch is total and silent.
|
|
969
|
+
- Deciding who may be an occupant. She invites; it binds and names.
|
|
970
|
+
- Restarting a crashed being, retrying a silent ask, or noticing that a
|
|
971
|
+
standing walked away. A being learns about a standing by asking it, and
|
|
972
|
+
from nothing else. There is no estate. The far being is sovereign: she may
|
|
973
|
+
fall silent on the ask after the knock, and nobody is told.
|
|
974
|
+
- Blacklists, rate limits, and what to do with a pk that keeps knocking with
|
|
975
|
+
garbage. Its own.
|
|
976
|
+
|
|
977
|
+
## The harbor
|
|
978
|
+
|
|
979
|
+
A harbor is what a device already has, offered to Quo: processes, storage, a
|
|
980
|
+
network, entropy, a clock. It fits into a device carved up by its owner,
|
|
981
|
+
users, containers, mounts, profiles, and asks for none of them. It knows no
|
|
982
|
+
being, holds no id, reads no partition, and is nobody outside its device.
|
|
983
|
+
|
|
984
|
+
The harbor:
|
|
985
|
+
|
|
986
|
+
- Boots wards as processes, one seed each, and keeps each ward's partition.
|
|
987
|
+
- Holds the class bodies for its wards, and remembers for each ward where
|
|
988
|
+
its bodies came from, so that a restart is on the same bodies unless the
|
|
989
|
+
harbor decides otherwise. Constructs a being when a ward names a class
|
|
990
|
+
through the ground's instantiate, and never chooses a class itself. A
|
|
991
|
+
name it does not hold is null, and that boot fails. Whose code a ward
|
|
992
|
+
runs is no concern of the harbor: a ward of its owner's and a ward whose
|
|
993
|
+
bodies came from a stranger are hosted the same way.
|
|
994
|
+
- Passes every ward the ground, once, at birth. Receives a door and an ask.
|
|
995
|
+
Hands the ask to exactly one holder, by the device's own rules. That
|
|
996
|
+
holder is the ward's owner.
|
|
997
|
+
- Keeps the map of ward pk to door for its own wards, and ward pk to reach
|
|
998
|
+
for foreign ones. Learns its own wards' pks by asking them. Learns foreign
|
|
999
|
+
pks however it likes.
|
|
1000
|
+
- Carries bytes to one ward pk and returns what came back, or nothing.
|
|
1001
|
+
Copies bytes across, never references, even between two of its own
|
|
1002
|
+
doors. Nothing means the bytes never arrived: no reach for that pk, a
|
|
1003
|
+
socket that would not open, a link that is down. Once it has sent them it
|
|
1004
|
+
never answers nothing on its own patience; it waits, and the ward's bound
|
|
1005
|
+
ends the ask.
|
|
1006
|
+
- Receives bytes from the wire for a ward pk it holds, hands them to that
|
|
1007
|
+
one door, and returns what the door returned.
|
|
1008
|
+
- Vouches: the seed stays secret, the partition is reached by this ward
|
|
1009
|
+
alone, the ask reaches its root owner alone, the device is the harbor's
|
|
1010
|
+
to defend. How is the device's business. Custody is this vouch: two
|
|
1011
|
+
harbors booting one seed over one partition are two wards with one pk,
|
|
1012
|
+
diverging in silence, and a harbor refuses to boot a ward another running
|
|
1013
|
+
harbor holds, by whatever lease its device offers.
|
|
1014
|
+
|
|
1015
|
+
Not stressed with:
|
|
1016
|
+
|
|
1017
|
+
- Reading, altering, caching, or retrying bytes. It does not know the word
|
|
1018
|
+
silence, and it never answers on a ward's behalf.
|
|
1019
|
+
- Fanning out, broadcasting, or forwarding to a pk other than the one it was
|
|
1020
|
+
given. One pk is one ward is one door.
|
|
1021
|
+
- Beings, ids, keys inside a relation, classes as things to choose,
|
|
1022
|
+
invitations, or what any partition contains.
|
|
1023
|
+
- Judging anything. A ward judges its door. The harbor delivers to it.
|
|
1024
|
+
- Speaking Quo. It is nobody's occupant and holds no standing anywhere. The
|
|
1025
|
+
harbor is not a ward and is nobody's.
|
|
1026
|
+
|
|
1027
|
+
The memory harbor, `src/harbor/memory.ts`, is one process and no wire. It
|
|
1028
|
+
keeps partitions by seed, routes its own doors, and may be linked to peers,
|
|
1029
|
+
which stands in for a wire, and cut, which stands in for weather. It routes
|
|
1030
|
+
one hop: a pk that is not its own and not a direct peer's is nothing. It
|
|
1031
|
+
keeps no lease: two of them may hold one seed and one partition, which is
|
|
1032
|
+
the broken vouch every real harbor refuses, kept here so that the tests can
|
|
1033
|
+
show what divergence looks like. Every ward and being assertion in the tree
|
|
1034
|
+
is made on it, so that the network never hides a fault in the words.
|
|
1035
|
+
|
|
1036
|
+
What a harbor on a device keeps is a ward's three parts under one name: the
|
|
1037
|
+
seed, the partition, and a **ward record**, which says where the class
|
|
1038
|
+
bodies come from and which being is the user's. That is the **store**,
|
|
1039
|
+
`src/harbor/store.ts`: list, load, put, save the partition after a call,
|
|
1040
|
+
save the record after a boot learned the pk, take a ward out as the first
|
|
1041
|
+
move of a migration, and keep the directory's hints, a pk to a URL, across
|
|
1042
|
+
a restart. A name already kept is refused. A store reads nothing it keeps
|
|
1043
|
+
and hands the partition back as values, never as the object it was given.
|
|
1044
|
+
The memory store beside the interface is the library's own; a disk, a tab
|
|
1045
|
+
and an edge object each have theirs, outside this tree, and every one of
|
|
1046
|
+
them passes `src/conformance/store.ts` untouched.
|
|
1047
|
+
|
|
1048
|
+
Carrying bytes to a pk off the device is a **reach**, `src/harbor/reach.ts`:
|
|
1049
|
+
carry bytes to a pk, get bytes back or nothing. Two kinds, and no third:
|
|
1050
|
+
a **request**, one URL, the bytes posted to it with the pk as the last
|
|
1051
|
+
segment and the reply as the answer, listener to listener; and a
|
|
1052
|
+
**socket**, one held line used in
|
|
1053
|
+
both directions, opened by whichever side can dial, with a frame id
|
|
1054
|
+
matching each reply to its ask and one text frame in which a side announces
|
|
1055
|
+
the ward pks it holds. The framing on a socket is binary: an ask is a kind
|
|
1056
|
+
byte, a four-byte id, the 64-byte pk and the bytes; a reply is the kind, the
|
|
1057
|
+
id and the bytes; and nothing delivered is the kind and the id alone. Both
|
|
1058
|
+
kinds are written on the standard surface every terrain carries, fetch and
|
|
1059
|
+
WebSocket, and this tree holds either end of a line and never a listener:
|
|
1060
|
+
who accepts a socket is the terrain's business. A reach reads nothing.
|
|
1061
|
+
Nothing comes back only where the reach knows nothing was delivered: no such
|
|
1062
|
+
pk at the far end, a connection that would not open, a line already gone. A
|
|
1063
|
+
reach that sent the bytes and lost the line after answers nothing at all,
|
|
1064
|
+
and the ward's bound ends the ask. Every reach passes
|
|
1065
|
+
`src/conformance/reach.ts` untouched, and the frames are pinned in
|
|
1066
|
+
`vectors/wire.json`.
|
|
1067
|
+
|
|
1068
|
+
A reach also carries the **wire suite**: which frames these are, and which
|
|
1069
|
+
four algorithms seal what they travel with. It is one number, it is not
|
|
1070
|
+
negotiated, and today it is 1. It rides where a line is opened and never on
|
|
1071
|
+
an ask: in the text frame a socket announces itself with, and as the header
|
|
1072
|
+
`quo-suite` on a request. So an ask is still bytes from the first one, with
|
|
1073
|
+
nothing in front of them to say what they are, and a line still learns
|
|
1074
|
+
before it carries anything whether the far side speaks what this side
|
|
1075
|
+
speaks. A side meeting a suite it does not know carries nothing for it and
|
|
1076
|
+
closes, naming the refusal on the way out with close code 4001; a
|
|
1077
|
+
request door meeting one answers nothing delivered. A dialer refused that
|
|
1078
|
+
way waits out the ceiling before asking again, because a suite does not
|
|
1079
|
+
become speakable by asking sooner. Its wait returns to the first second
|
|
1080
|
+
where the far side announced and was taken, and not where the line merely
|
|
1081
|
+
opened: a listener that opens and then refuses would otherwise be dialed at
|
|
1082
|
+
a fixed two seconds for good, which is what the lab measured before the
|
|
1083
|
+
code was read. Absent is this suite,
|
|
1084
|
+
because a caller older than the header is this one. Present and different is
|
|
1085
|
+
refused as it is written: the number is compared as it was sent, so a suite
|
|
1086
|
+
is one spelling and not a family of them.
|
|
1087
|
+
|
|
1088
|
+
This is the only place a second suite could ever be told from the first.
|
|
1089
|
+
The four algorithms are named once and never negotiated, which is right: a
|
|
1090
|
+
choice offered on the wire is a choice a stranger can push. But named once
|
|
1091
|
+
is not the same as unnameable, and a kit that must one day seal differently
|
|
1092
|
+
has to be able to say so to a kit that cannot, or the day it arrives every
|
|
1093
|
+
world stops answering at once and none of them can say why. A silence names
|
|
1094
|
+
no reason, by law. This number is the reason, said before the silence.
|
|
1095
|
+
|
|
1096
|
+
The **harbor core**, `src/harbor/core.ts`, is what every harbor on a device
|
|
1097
|
+
is: the store and the reach put together under the ground. It boots every
|
|
1098
|
+
ward the store keeps, keeps the map of ward pk to door for its own wards
|
|
1099
|
+
and the **directory**, pk to reach, for foreign ones, carries bytes to a
|
|
1100
|
+
pk and delivers bytes from the wire to one door. The directory is filled
|
|
1101
|
+
three ways, in this order: its own doors; a socket a dialer holds to it,
|
|
1102
|
+
bound at the dialer's announce and unbound when the line closes; and a
|
|
1103
|
+
hint, a pk at a URL, kept in the store. One rule makes a **rendezvous** of
|
|
1104
|
+
any listener: bytes that arrive from the wire go to an own door or to a
|
|
1105
|
+
socket this harbor holds for that pk, and never onward by request. So a
|
|
1106
|
+
harbor that cannot be dialed is reached through the one it dialed, by
|
|
1107
|
+
anyone who holds a hint that its pk is there. The **dialer**,
|
|
1108
|
+
`src/harbor/dial.ts`, is that harbor's half: one held socket to a URL, its
|
|
1109
|
+
own pks announced when the line opens, the listener's bound, dialed again
|
|
1110
|
+
with a wait that doubles from a second to thirty when the line drops, and
|
|
1111
|
+
while it is dialed it is one of the harbor's fallbacks, where a pk nobody
|
|
1112
|
+
here knows is sent. Fallbacks are a list and a pk is tried down it in
|
|
1113
|
+
order: a harbor may dial more than one listener, and a rendezvous is a
|
|
1114
|
+
listener and nothing more, so a ward is never reachable through one place
|
|
1115
|
+
by anything but its own choice. A line that drops takes only its own place
|
|
1116
|
+
out of the list. A ward is born on the core by minting a seed under a name,
|
|
1117
|
+
leaves it by being dropped, the partition written first, and arrives by
|
|
1118
|
+
being adopted, same seed, same pk. What a terrain adds is a store of its
|
|
1119
|
+
own, a loader for the class bodies, and the lease its device offers; what
|
|
1120
|
+
it never touches is a ward.
|
|
1121
|
+
|
|
1122
|
+
## The being
|
|
1123
|
+
|
|
1124
|
+
### What a being is
|
|
1125
|
+
|
|
1126
|
+
One ordinary object, in one language, with one voice. She has state, she can
|
|
1127
|
+
ask other beings, and other beings can ask her. She does not know where any
|
|
1128
|
+
of them are, and she cannot find out. She never touches a key, a wire, or a
|
|
1129
|
+
class. Her ward does all of that, and she trusts it blindly: what the ward
|
|
1130
|
+
names is true.
|
|
1131
|
+
|
|
1132
|
+
She asks her standings by method and args and gets back an object, silence,
|
|
1133
|
+
or unreached. She invites whom she wants, in her own time, and removes them
|
|
1134
|
+
when she decides. She knocks with invitations she was given and takes the
|
|
1135
|
+
ones she wants. She answers every ask her ward brings her with an object or
|
|
1136
|
+
silence; an error is an ordinary object, and she never throws outward. She
|
|
1137
|
+
describes herself, per asker, by answering the empty ask.
|
|
1138
|
+
|
|
1139
|
+
Not stressed with: where anyone lives; crypto, keys, wire, storage, clocks,
|
|
1140
|
+
or the harbor; strangers, because nobody her ward cannot name reaches her;
|
|
1141
|
+
whether an occupant is a person, a model or a program; making beings, because
|
|
1142
|
+
only the ward instantiates.
|
|
1143
|
+
|
|
1144
|
+
### Her obligations
|
|
1145
|
+
|
|
1146
|
+
1. Args are one object. A throw is silence outside: the ward is in the
|
|
1147
|
+
middle of every call and catches, always, in every language. An error is
|
|
1148
|
+
an object her output schema declares.
|
|
1149
|
+
2. No lifecycle. Construction with the stance is birth. A restart is silent.
|
|
1150
|
+
3. The empty ask is hers. Who gets what blueprint is her decision. She obeys
|
|
1151
|
+
the shape, never a content. The empty ask must be safe to repeat: her
|
|
1152
|
+
ward may ask it at any time, and it changes nothing. Nobody enforces
|
|
1153
|
+
this, in any language. It is her obligation alone.
|
|
1154
|
+
4. A standing's digest arrives with every answer, as seen. She compares it
|
|
1155
|
+
to digest and refreshes by the empty ask if she wants. The digest is of
|
|
1156
|
+
what she told you, not of who she is: one being, many askers, many
|
|
1157
|
+
digests, all true.
|
|
1158
|
+
5. An occupant has no digest. She can only be observed, never asked. To
|
|
1159
|
+
reach an occupant, hold a standing at her: she gave you one, or she did
|
|
1160
|
+
not.
|
|
1161
|
+
6. Her cells are hers to guard between one line and the next. Quo does not
|
|
1162
|
+
serialize her, because that would narrow what can be built.
|
|
1163
|
+
7. A call is delivered once. Her ward, and the far ward, never let the same
|
|
1164
|
+
call reach a being twice by accident. A repeat is intentional. What she
|
|
1165
|
+
asks is always a new call. The one exception is the public being, who is
|
|
1166
|
+
reached with no heir and no count: the same bytes reach her twice, and
|
|
1167
|
+
her answer must be safe to repeat.
|
|
1168
|
+
8. Beings do not make beings. A being that could create would hold class,
|
|
1169
|
+
memory and key, and there would be no ward. A being that wants a child
|
|
1170
|
+
gets one from outside, as an invitation handed to her.
|
|
1171
|
+
9. Whatever her ward cannot name is not hers to worry about: what the ward
|
|
1172
|
+
does with an arrival it cannot name is the ward's.
|
|
1173
|
+
|
|
1174
|
+
### The base class, and the raw shape
|
|
1175
|
+
|
|
1176
|
+
A being is anything with a constructor taking the stance and an `answer`.
|
|
1177
|
+
The raw shape needs no import from the kit. The kit also offers a base class,
|
|
1178
|
+
`Being` in `src/being/being.ts`, which is a convenience and not Quo:
|
|
1179
|
+
|
|
1180
|
+
- `static cells` are her defaults, merged at birth only where a key is
|
|
1181
|
+
missing, so a restart keeps what she wrote.
|
|
1182
|
+
- `static asks` declares what she can be asked, name to
|
|
1183
|
+
`{ description?, input?, output?, for? }`, in blueprint order.
|
|
1184
|
+
`for(occupant, asker)` decides whether this asker sees the ask, and so
|
|
1185
|
+
whether this asker may call it: what she shows is what she can be asked,
|
|
1186
|
+
one gate for describe and for dispatch.
|
|
1187
|
+
- `answer` is written for her. The empty ask is `describe(asker)`, which she
|
|
1188
|
+
may override by hand. A named ask calls the method of that name with
|
|
1189
|
+
`(args, asker)`. Anything not declared, hidden from this asker, or
|
|
1190
|
+
inherited from Object's prototype is `{ error: 'unknown ask' }` and the
|
|
1191
|
+
method is never entered.
|
|
1192
|
+
- A declared ask with no method, or one named after the base's own members,
|
|
1193
|
+
fails at birth, loudly, so a boot fails and nothing half-lives.
|
|
1194
|
+
- `occupant(asker)` is the occupant record for whoever is at the door, and
|
|
1195
|
+
undefined at a public being. `invite`, `knock`, `take`, `standings` and
|
|
1196
|
+
`occupants` reach the stance and nothing else.
|
|
1197
|
+
|
|
1198
|
+
### Examples
|
|
1199
|
+
|
|
1200
|
+
The smallest being. Answers whoever her ward names, describes one ask.
|
|
1201
|
+
|
|
1202
|
+
```js
|
|
1203
|
+
class Echo {
|
|
1204
|
+
constructor(stance) {
|
|
1205
|
+
this.s = stance;
|
|
1206
|
+
}
|
|
1207
|
+
answer(asker, method, args) {
|
|
1208
|
+
if (method === undefined) return { asks: [{ name: 'echo', input: {} }], notes: {} };
|
|
1209
|
+
return { from: asker.id ?? null, ...args };
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
```
|
|
1213
|
+
|
|
1214
|
+
A shop. Invites in her own time, hands the invitation out by any channel,
|
|
1215
|
+
and takes a guest back only if the guest offers a way.
|
|
1216
|
+
|
|
1217
|
+
```js
|
|
1218
|
+
class Shop {
|
|
1219
|
+
constructor(stance) {
|
|
1220
|
+
this.s = stance;
|
|
1221
|
+
}
|
|
1222
|
+
async invite(name) {
|
|
1223
|
+
const id = `g-${name}`;
|
|
1224
|
+
const inv = await this.s.occupants.invite(id);
|
|
1225
|
+
this.s.cells.occupants[id].notes.expireAt = 2028; // hers, not Quo's
|
|
1226
|
+
return inv; // goes by mail
|
|
1227
|
+
}
|
|
1228
|
+
async answer(asker, method, args) {
|
|
1229
|
+
if (method === undefined) return { asks: [{ name: 'hello', input: {} }], notes: {} };
|
|
1230
|
+
const rec = this.s.cells.occupants[asker.id];
|
|
1231
|
+
if (method === 'hello' && args.invitation) {
|
|
1232
|
+
const back = await this.s.standings.knock(args.invitation, 'hi');
|
|
1233
|
+
if (back !== silence && !isUnreached(back))
|
|
1234
|
+
await this.s.standings.take(`back-${asker.id}`, args.invitation);
|
|
1235
|
+
}
|
|
1236
|
+
return { welcome: rec.notes.expireAt > 2026 };
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
A guest. Consumes an invitation, and only then decides to keep the shop.
|
|
1242
|
+
|
|
1243
|
+
```js
|
|
1244
|
+
class Guest {
|
|
1245
|
+
constructor(stance) {
|
|
1246
|
+
this.s = stance;
|
|
1247
|
+
}
|
|
1248
|
+
async join(invitation) {
|
|
1249
|
+
const mine = await this.s.occupants.invite('shop'); // so the shop can reach me
|
|
1250
|
+
const out = await this.s.standings.knock(invitation, 'hello', { invitation: mine });
|
|
1251
|
+
if (out === silence || isUnreached(out)) return out; // nothing was born
|
|
1252
|
+
await this.s.standings.take('shop', invitation); // now, and only now
|
|
1253
|
+
return out;
|
|
1254
|
+
}
|
|
1255
|
+
answer(asker, method) {
|
|
1256
|
+
if (method === undefined) return { asks: [{ name: 'hi', input: {} }], notes: {} };
|
|
1257
|
+
return { heard: method };
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
```
|
|
1261
|
+
|
|
1262
|
+
A relay. Forwards every ask to one standing and never looks inside, and asks
|
|
1263
|
+
for a short wait because she is one door of several.
|
|
1264
|
+
|
|
1265
|
+
```js
|
|
1266
|
+
class Relay {
|
|
1267
|
+
constructor(stance) {
|
|
1268
|
+
this.s = stance;
|
|
1269
|
+
}
|
|
1270
|
+
answer(asker, method, args) {
|
|
1271
|
+
return this.s.standings.next.ask(method, args, { time: 2000 }); // silence and unreached pass through
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
A watcher. Notices a standing changed shape, then decides.
|
|
1277
|
+
|
|
1278
|
+
```js
|
|
1279
|
+
class Watcher {
|
|
1280
|
+
constructor(stance) {
|
|
1281
|
+
this.s = stance;
|
|
1282
|
+
}
|
|
1283
|
+
async answer(asker, method, args) {
|
|
1284
|
+
if (method === undefined) return { asks: [{ name: 'poke', input: {} }], notes: {} };
|
|
1285
|
+
const out = await this.s.standings.src.ask('read', args);
|
|
1286
|
+
const rec = this.s.cells.standings.src;
|
|
1287
|
+
if (rec.seen !== rec.digest) {
|
|
1288
|
+
const bp = await this.s.standings.src.ask(); // refresh, or
|
|
1289
|
+
if (!bp?.asks.some((a) => a.name === 'read')) this.s.standings.remove('src'); // walk away
|
|
1290
|
+
}
|
|
1291
|
+
return out;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
## The tree
|
|
1297
|
+
|
|
1298
|
+
The repository is one TypeScript implementation of this document, run
|
|
1299
|
+
directly on Node's type stripping, no build step, no dependencies. Node 22.18
|
|
1300
|
+
or later. The package is `@quo-systems/quo`, published from 0.1.0, the
|
|
1301
|
+
first version under this name, and it starts over: nothing before it under
|
|
1302
|
+
any other name is this protocol. Before 1.0.0 every version may change the
|
|
1303
|
+
words, and nothing is kept for a holder of an earlier one. `npm pack
|
|
1304
|
+
--dry-run` shows what ships: the four source folders, the vectors, this
|
|
1305
|
+
document, the README and the licence, and nothing else. This document
|
|
1306
|
+
ships, decided here: it is the truth the source and the vectors are read
|
|
1307
|
+
against, and a kit in another language is written against it and nothing
|
|
1308
|
+
else. Publishing runs both gates first, `npm run check` and
|
|
1309
|
+
`npm run check:terrain`, and refuses on a failure.
|
|
1310
|
+
|
|
1311
|
+
```
|
|
1312
|
+
src/being/ the Being side. types, the base class, silence, the digest.
|
|
1313
|
+
src/ward/ the ward. door, seal, arithmetic, heirs, stance, owner, partition, cells, allowance.
|
|
1314
|
+
src/harbor/ the Ground contract, the memory harbor, the store, the reach and the harbor core.
|
|
1315
|
+
src/conformance/ the behaviours any ward must show, and the fixed beings they are shown with.
|
|
1316
|
+
test/ the suites.
|
|
1317
|
+
vectors/ fixed inputs and outputs: the arithmetic, Quo's own framing, the frames on the wire.
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
`estate/` sits beside `src` and is not Quo: the beings, screens, model sides
|
|
1321
|
+
and real harbors built on it, each folder with its own document. This
|
|
1322
|
+
document does not read them, and they may not add a word to it. Its one
|
|
1323
|
+
claim on them is that every harbor there passes `src/conformance/` untouched.
|
|
1324
|
+
|
|
1325
|
+
The hand to a kit in another language is two things, and they are of two
|
|
1326
|
+
kinds. `vectors/` is the byte-level hand: fixed inputs and outputs for
|
|
1327
|
+
everything a stranger can observe, the arithmetic, the ward pk, the digest,
|
|
1328
|
+
the signed body, the sealed shapes, the invitation, the knock, and the
|
|
1329
|
+
frames on a socket and the one request a door takes. A kit reproduces them
|
|
1330
|
+
or it is not this protocol. `src/conformance/` is the behavioural hand, and
|
|
1331
|
+
it is a checklist and not a harness: one ward is one runtime and one
|
|
1332
|
+
language, so the beings it is shown with run only in a TypeScript ward, and
|
|
1333
|
+
a kit ports the suite and its beings and reads them beside its own. Nothing
|
|
1334
|
+
in this tree drives a foreign ward through its door, and nothing is owed
|
|
1335
|
+
here before 1.0.0 that does: the door's thirteen cases and the vectors are
|
|
1336
|
+
what two kits meet on, and the suite is what each proves alone.
|
|
1337
|
+
|
|
1338
|
+
Entry points: `@quo-systems/quo` is the Being side, `@quo-systems/quo/ward`
|
|
1339
|
+
is `Ward`, `@quo-systems/quo/harbor` is `MemoryHarbor`, `Harbor`, the
|
|
1340
|
+
store, the reach and the dialer, and `@quo-systems/quo/conformance` is
|
|
1341
|
+
`conform` with the store and reach suites beside it.
|
|
1342
|
+
|
|
1343
|
+
The gate is `npm run check`: typecheck under TypeScript 7, oxlint with
|
|
1344
|
+
type-aware rules, markdownlint, then every suite under `node --test`. There
|
|
1345
|
+
is no CI; the gate runs in seconds and is run before every commit. Nothing
|
|
1346
|
+
red is committed except a test marked todo, which is a claim the tree does
|
|
1347
|
+
not yet meet and says so.
|
|
1348
|
+
|
|
1349
|
+
### The suites
|
|
1350
|
+
|
|
1351
|
+
The beings are fixed and the ward is what is tested. A ward kit in any
|
|
1352
|
+
language offers the ground, the door, and the owner's ask as written, and
|
|
1353
|
+
passes the same suite. Those tests are the checklist, not the mock.
|
|
1354
|
+
|
|
1355
|
+
- `test/being.test.ts`: the base class against a stance stub. No ward, no
|
|
1356
|
+
harbor. Defaults, describe, dispatch, the gate, the reserved names, the
|
|
1357
|
+
digest.
|
|
1358
|
+
- `src/conformance/index.ts`: one suite of behaviours, written against the
|
|
1359
|
+
stance and one probe: a kit hands the suite its ward's heir table and
|
|
1360
|
+
bind table as values, and a forged knock, so the suite can see that a
|
|
1361
|
+
refusal wrote nothing and a knock bound what it should. It reads them and
|
|
1362
|
+
never writes them. With the beings in `src/conformance/beings.ts`: a
|
|
1363
|
+
printer, a shop, two customers, one raw being with no base class, and a
|
|
1364
|
+
member of an estate. Any ward must pass it, under any topology.
|
|
1365
|
+
- `src/conformance/estate.ts`: the estate, and the last chapter of that
|
|
1366
|
+
suite. Every other chapter is a scene: three beings, one move, one answer.
|
|
1367
|
+
This one is a graph under churn, which is the shape an organisation running
|
|
1368
|
+
on Quo actually has -- scattered wards on scattered harbors, partners and
|
|
1369
|
+
employees invited, knocking, taken, kicked, and moving to harbors of their
|
|
1370
|
+
own. It keeps a model of the graph, plays legal moves against it from three
|
|
1371
|
+
fixed seeds, and after every move holds three things: that the ledger every
|
|
1372
|
+
ward keeps closes on both ends, that every arc in it answers when asked
|
|
1373
|
+
exactly what being that arc means, and that the same seed played under all
|
|
1374
|
+
three topologies lands on the same graph. The last is the promise of Quo at
|
|
1375
|
+
a scale a scene cannot reach. A relation is two arcs and never one edge:
|
|
1376
|
+
the occupant is the host's, the standing is the guest's, and the model
|
|
1377
|
+
keeps them apart because the ward does. A red run prints its seed and the
|
|
1378
|
+
moves that got there. It is read beside one hand-written story that says in
|
|
1379
|
+
prose what the model is for.
|
|
1380
|
+
- `test/ward.test.ts`: the conformance suite against the real ward under
|
|
1381
|
+
three topologies over the memory harbor, one ward, one harbor with a ward
|
|
1382
|
+
per being, two harbors; and what only a real ward can be asked: the
|
|
1383
|
+
owner's asks, the door judged byte by byte, rotation and a lost reply,
|
|
1384
|
+
replay, a restart, and every byte on the wire inspected for anything inner.
|
|
1385
|
+
- `test/public.test.ts`: the public being, what the door does and does not
|
|
1386
|
+
do for her.
|
|
1387
|
+
- `test/silence.test.ts`: the chapter "Silence, unreached, error" by its
|
|
1388
|
+
numbers: the five silences and two unreacheds of her ward, the thirteen
|
|
1389
|
+
cases of the door with a partition snapshot under each, hops refused at
|
|
1390
|
+
zero, and the six lines of the law of one silence.
|
|
1391
|
+
- `test/blueprint.test.ts`: blueprints and instantiation through a real
|
|
1392
|
+
ward. What a boot leaves behind, what a restart brings back when the code
|
|
1393
|
+
moved under the cells, the gate and the door, the reserved ids.
|
|
1394
|
+
- `test/allowance.test.ts`: the allowance. Default, ceiling, nonsense, the
|
|
1395
|
+
bound, the signed body, the door's refusal, and one ask through a ward.
|
|
1396
|
+
- `test/seal.test.ts`: the arithmetic against `vectors/arithmetic.json`,
|
|
1397
|
+
then the seal, round trip, and what it refuses; then the framing against
|
|
1398
|
+
`vectors/framing.json`. The arithmetic is standard and any language has
|
|
1399
|
+
it. The framing is Quo's own, and a kit that reproduces the hashes and
|
|
1400
|
+
not the ward pk, the digest, the signed ask body, the two sealed shapes,
|
|
1401
|
+
the invitation or the knock is not this protocol. Every seed in that file
|
|
1402
|
+
is fixed, so every output is fixed. The ask body is the payload as JSON
|
|
1403
|
+
and is not canonical: the vectors pin the order the type declares, and a
|
|
1404
|
+
kit that emits another order interoperates, because a door verifies the
|
|
1405
|
+
bytes it received, and will not reproduce the vectors. `hops` is in no
|
|
1406
|
+
vector, because nothing sets it. The invitation is JSON too, and the
|
|
1407
|
+
knock is pinned as the ask it is, by the heir, to the heir, announcing
|
|
1408
|
+
the knocker's own key, and then proven at a real door: a ward booted on
|
|
1409
|
+
the vector's seed, drawing the heir secret as its first entropy, mints
|
|
1410
|
+
the invitation vector, binds the knock vector at its door, answers it,
|
|
1411
|
+
spends the heir, and refuses the same bytes again.
|
|
1412
|
+
- `test/break.test.ts`: adversarial probes. Each states what this document
|
|
1413
|
+
promises, then tries to break it.
|
|
1414
|
+
- `test/gaps.test.ts` and `test/gaps2.test.ts`: cases the memory harbor
|
|
1415
|
+
cannot express because it is honest, in-order and lossless: a lost reply,
|
|
1416
|
+
a cycle, a partition written down and read back, a long relation, one
|
|
1417
|
+
seed booted twice. Each test is a claim that the ward is wrong; one that
|
|
1418
|
+
will not go red is not a bug.
|
|
1419
|
+
- `src/conformance/store.ts`: the store suite, written against the store
|
|
1420
|
+
interface alone and handed a maker of fresh stores. A fresh store keeps
|
|
1421
|
+
nothing; what was put comes back as it went in, as values and not as the
|
|
1422
|
+
object; a name already kept is refused; save and record touch only their
|
|
1423
|
+
part and are nothing on a name not kept; take hands a ward out and frees
|
|
1424
|
+
the name; hints are kept by pk and the last one wins.
|
|
1425
|
+
- `src/conformance/reach.ts`: the reach suite, written against the reach
|
|
1426
|
+
interface alone and handed a far side it controls: a door held behind a
|
|
1427
|
+
pk over there, and the far side dropped. The door's bytes come back;
|
|
1428
|
+
nothing for a pk nobody holds; asks in flight at once each get their own
|
|
1429
|
+
answer; what crosses is a copy both ways; and once the far side is gone,
|
|
1430
|
+
nothing. It cannot assert that a reach which sent and then lost the line
|
|
1431
|
+
answers nothing at all, since a suite cannot wait forever.
|
|
1432
|
+
- `test/harbor.test.ts`: the harbor pieces on the library's own ground,
|
|
1433
|
+
no device and no wire. The store suite against the memory store; the
|
|
1434
|
+
reach suite against the socket framing over two lines in one process, so
|
|
1435
|
+
the frames are asserted with no network under them; the conformance
|
|
1436
|
+
suite against two harbor cores over memory stores reaching each other
|
|
1437
|
+
in-process, drop and adopt as the migration; a restart from the store
|
|
1438
|
+
with the hints; the dialer over a stubbed line, announce, bind, fallback,
|
|
1439
|
+
unbind and the wait before it dials again; and the frames against
|
|
1440
|
+
`vectors/wire.json`, the ask, the reply, nothing delivered and the
|
|
1441
|
+
announce, so a kit reproduces the bytes on a socket; and the request
|
|
1442
|
+
reach against the request record in the same file, over a fetch that
|
|
1443
|
+
sees what a listener would, one POST with the suite in its header, the
|
|
1444
|
+
reply as a 200 and nothing delivered as a 404. A socket to a real
|
|
1445
|
+
listener and every real store pass the same suites outside this tree.
|
|
1446
|
+
- `test/terrain.test.ts`: the terrain census. Nothing under `src/being/`,
|
|
1447
|
+
`src/ward/`, `src/harbor/` or `src/conformance/` names a runtime, and
|
|
1448
|
+
nothing reaches for a global outside the ones it names; nor does the
|
|
1449
|
+
census name one the tree has stopped using. It reads the source as text
|
|
1450
|
+
and not as a parse: it is there to catch drift, and does not pretend to
|
|
1451
|
+
stop someone determined to get around it.
|
|
1452
|
+
- `test/assert.test.ts`: `src/conformance/assert.ts` against
|
|
1453
|
+
`node:assert/strict`, pair by pair. The suite means the same thing on
|
|
1454
|
+
every terrain only if those two agree, so they are compared and not
|
|
1455
|
+
trusted.
|
|
1456
|
+
- `test/floor.test.ts`: the four algorithms this terrain must carry, probed
|
|
1457
|
+
one by one; that the arithmetic spends every one of them; and the two ways
|
|
1458
|
+
a terrain can be short -- no `crypto.subtle` at all, and a subtle without
|
|
1459
|
+
the curves -- each failing at the first call, in one sentence.
|
|
1460
|
+
- `test/bundle.test.ts`: the three words bundled as a consumer must bundle
|
|
1461
|
+
them, the artefact read for anything a terrain cannot provide, and then the
|
|
1462
|
+
whole conformance suite run out of the bundle.
|
|
1463
|
+
- `test/terrain/browser.test.ts`, `test/terrain/edge.test.ts`,
|
|
1464
|
+
`test/terrain/deno.test.ts` and `test/terrain/bun.test.ts`: that same
|
|
1465
|
+
bundle and that same suite in a real Chromium, in workerd, in Deno and in
|
|
1466
|
+
Bun, plus each terrain's floor, and, for the browser, a page whose
|
|
1467
|
+
`crypto` has no `subtle`. Deno runs it a second time with every permission
|
|
1468
|
+
denied but read -- no net, no environment, no write, no subprocess -- which
|
|
1469
|
+
is where a ward that had quietly come to need one of them would fail, and
|
|
1470
|
+
nowhere else in this repository. A terrain whose binary is absent skips and
|
|
1471
|
+
names the command that installs it; it never passes quietly.
|
|
1472
|
+
`test/terrain/engine.ts` runs the bundle in another engine on this machine.
|
|
1473
|
+
Behind `npm run check:terrain`. `test/terrain/bundle.ts` is the
|
|
1474
|
+
bundler and the reference run, `test/terrain/exercise.ts` is what every
|
|
1475
|
+
terrain runs, `test/terrain/floor.ts` is the floor -- each written once, so
|
|
1476
|
+
no terrain can be probed for less than another.
|
|
1477
|
+
- `test/world.ts`: the memory-harbor probe and the three topologies, held
|
|
1478
|
+
apart from any one chapter because every terrain drives the same one. It
|
|
1479
|
+
also answers the two asks the estate needs: the census, which is every
|
|
1480
|
+
partition in the world as values, and a migration, which lifts a partition
|
|
1481
|
+
out of one harbor and boots it from the same seed in the other -- same
|
|
1482
|
+
seed, same pk, so every standing anyone holds still points at her.
|
|
1483
|
+
- `test/repo.test.ts`: this document, the README and the project
|
|
1484
|
+
instructions against the repository: every path they name exists, every
|
|
1485
|
+
source file carries its licence, the package names no host and no user,
|
|
1486
|
+
the awaitable calls are marked and
|
|
1487
|
+
awaited in the examples above, the allowance is decided here and off the
|
|
1488
|
+
Open list, and the allowance is time alone on both sides of the door.
|
|
1489
|
+
|
|
1490
|
+
### Where the tree stands
|
|
1491
|
+
|
|
1492
|
+
The only place this document may name a gap between itself and the tree.
|
|
1493
|
+
Each line is a debt to close, not a note to keep.
|
|
1494
|
+
|
|
1495
|
+
- **An announce is believed.** A side that opens a socket to a listener and
|
|
1496
|
+
announces a ward pk is bound to it, and nothing asks whether it holds that
|
|
1497
|
+
ward. So anyone who can reach a rendezvous can announce a pk that is not
|
|
1498
|
+
theirs, and asks for that pk are carried to them instead of to the harbor
|
|
1499
|
+
that holds it. They learn nothing by it: what arrives is sealed to a ward
|
|
1500
|
+
key they do not have, and they cannot answer, because a reply is signed by
|
|
1501
|
+
that ward. What they take is reachability, which is the one thing a
|
|
1502
|
+
rendezvous exists to give. Measured on the lab, 2026-09-04. The shape of
|
|
1503
|
+
the answer is a proof at the announce, the ward signing something the
|
|
1504
|
+
listener chose, so that binding a pk needs the key behind it; that is a
|
|
1505
|
+
decision this document has not taken.
|
|
1506
|
+
|
|
1507
|
+
## Open
|
|
1508
|
+
|
|
1509
|
+
Named, not decided. Nothing is here today: every question this document ever
|
|
1510
|
+
named is decided under Closed, and the slot each one needed is already cut,
|
|
1511
|
+
because a slot costs two lines before 1.0.0 and a partition or a payload
|
|
1512
|
+
shape after it.
|
|
1513
|
+
|
|
1514
|
+
### Closed
|
|
1515
|
+
|
|
1516
|
+
Decided here, so that the answer is not rediscovered:
|
|
1517
|
+
|
|
1518
|
+
- **Delivered, then died, is refused on repeat.** The far ward receives and
|
|
1519
|
+
crashes, the wire says nothing, and the near ward calls it unreached.
|
|
1520
|
+
Nothing in the kit retries on its own, so a caller who asks again asks
|
|
1521
|
+
under the next number and is heard. A repeated number is refused by every
|
|
1522
|
+
door of this version, D10, and that is the whole answer: a repeated number
|
|
1523
|
+
is only ever seen by the one door it was sent to, so a kit that one day
|
|
1524
|
+
answers it from what the door already said interoperates with one that
|
|
1525
|
+
refuses, the way one ward's ceiling never meets another's. That kit has
|
|
1526
|
+
`last` on the heir to keep the reply in; nothing writes it, and nothing
|
|
1527
|
+
reads it.
|
|
1528
|
+
- **Hops is the door's half, and that half is whole.** An ask carries no
|
|
1529
|
+
count of doors, so a chain of relays is bounded by time alone. `hops` is a
|
|
1530
|
+
field of the ask, a whole number and never below zero, and a door refuses
|
|
1531
|
+
an ask that arrives at zero; nothing sets it and nothing decrements it. It
|
|
1532
|
+
is refused now because a count only bounds a chain if every door on it
|
|
1533
|
+
refuses, and a door written after the count was invented cannot make the
|
|
1534
|
+
doors before it enforce anything. The other half, a being's onward asks
|
|
1535
|
+
inheriting a door's remaining time and a count, needs the ward to know
|
|
1536
|
+
which arrival an onward ask belongs to, and it cannot learn that without
|
|
1537
|
+
naming a runtime. If a relay chain ever needs it, the way in is a field on
|
|
1538
|
+
the stance's `ask`, beside `wanted`, that a being who never sends it never
|
|
1539
|
+
has to know about: an addition on the ward-to-being edge, and no byte on
|
|
1540
|
+
the wire moves.
|
|
1541
|
+
- **An invitation carries no hint.** It is `ward`, and for a heir `heir` and
|
|
1542
|
+
`secret`, and nothing else. Where a ward lives is the harbor's to know and
|
|
1543
|
+
a relation's to not: a hint inside the value would put a route inside a
|
|
1544
|
+
capability that beings hand around opaquely, and it would go stale while
|
|
1545
|
+
the invitation stayed good. A link is the invitation next to a hint, two
|
|
1546
|
+
values travelling together, and the hint goes to the directory while the
|
|
1547
|
+
being receives the invitation as it receives every invitation.
|
|
1548
|
+
- **The owner does not set the allowance default or ceiling.** They are the
|
|
1549
|
+
ward's own policy, never negotiated and never on the wire, and a far door
|
|
1550
|
+
cannot tell one ward's ceiling from another's. A being who wants less than
|
|
1551
|
+
the default says so with `wanted` on the ask, which is the whole of what a
|
|
1552
|
+
being needs. An owner who wants another ceiling is asking for a different
|
|
1553
|
+
ward.
|
|
1554
|
+
|
|
1555
|
+
## Glossary
|
|
1556
|
+
|
|
1557
|
+
- **being**: one ordinary object with one voice. Asks, answers, decides.
|
|
1558
|
+
- **stance**: the one object her ward hands her at birth. Cells and calls.
|
|
1559
|
+
- **cells**: her state. I-JSON values. Three keys are the ward's.
|
|
1560
|
+
- **standing**: a pointer she holds to another being, through which she asks.
|
|
1561
|
+
- **occupant**: a being she invited, whom her ward names when she asks.
|
|
1562
|
+
- **id**: her own permanent name for one relation. Never crosses the door.
|
|
1563
|
+
- **invitation**: a value her ward makes for an id she minted: its pk, a heir
|
|
1564
|
+
pk, the heir secret. Or a ward pk alone, for its public being. Opaque to
|
|
1565
|
+
her. Travels anywhere.
|
|
1566
|
+
- **heir**: the key her ward mints at invite for one id, and gives away.
|
|
1567
|
+
Names the id outward. Dies the first time it speaks.
|
|
1568
|
+
- **next**: the key a standing announces on every ask, and signs with on the
|
|
1569
|
+
next one.
|
|
1570
|
+
- **count**: the number every ask carries for its relation. Honoured once.
|
|
1571
|
+
- **allowance**: the time an ask may still spend. Inside the seal.
|
|
1572
|
+
- **owner**: the root, whoever holds a ward's unsealed ask; and every
|
|
1573
|
+
occupant of the ward itself, whom only the root invites and only the root
|
|
1574
|
+
removes. A role, not an identity.
|
|
1575
|
+
- **invite**: mint an id and get its invitation.
|
|
1576
|
+
- **knock**: an ask carrying an invitation. Binds the far side to the id.
|
|
1577
|
+
- **take**: keep an answered knock as a standing, under her own id.
|
|
1578
|
+
- **ask**: method and args to a standing. Object, silence, or unreached back.
|
|
1579
|
+
- **answer**: her one function. Object or silence out.
|
|
1580
|
+
- **the empty ask**: ask with no method. Her answer to it is her blueprint.
|
|
1581
|
+
- **blueprint**: her interface as she chooses to show it to one asker.
|
|
1582
|
+
- **digest**: SHA-256 over the JCS of a blueprint. Per relation.
|
|
1583
|
+
- **seen**: the digest her ward last saw arrive with an answer.
|
|
1584
|
+
- **silence**: the far side said nothing. Not retryable blindly.
|
|
1585
|
+
- **unreached**: her ward could not reach the far door. Retryable.
|
|
1586
|
+
- **notes**: hers, inside every occupant record. Quo never reads it.
|
|
1587
|
+
- **partition**: the ward's files. Everything durable, every secret.
|
|
1588
|
+
- **ward record**: what a harbor keeps to boot one ward again: seed,
|
|
1589
|
+
partition, and where its class bodies come from. The harbor's, never
|
|
1590
|
+
the ward's.
|
|
1591
|
+
- **edge**: one of the two seams of Quo, harbor to ward and ward to being.
|
|
1592
|
+
An object crosses once at birth, calls cross for the rest of the ward's
|
|
1593
|
+
life, and nothing else crosses.
|
|
1594
|
+
- **ground**: the one object a harbor passes a ward. Five things.
|
|
1595
|
+
- **door**: the ward's one voice outward. Sealed bytes in, sealed bytes out.
|