@quo-systems/quo 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -10
- package/SPEC.md +161 -89
- package/dist/being/being.d.ts +2 -2
- package/dist/being/being.js +27 -11
- package/dist/being/digest.js +11 -7
- package/dist/being/types.d.ts +2 -2
- package/dist/conformance/estate.js +1 -3
- package/dist/conformance/index.js +1 -1
- package/dist/harbor/core.d.ts +2 -0
- package/dist/harbor/core.js +114 -25
- package/dist/harbor/dial.js +5 -3
- package/dist/harbor/memory.d.ts +1 -1
- package/dist/harbor/memory.js +3 -2
- package/dist/harbor/reach.js +32 -7
- package/dist/ward/allowance.js +8 -2
- package/dist/ward/arithmetic.js +11 -3
- package/dist/ward/cells.d.ts +3 -1
- package/dist/ward/cells.js +79 -21
- package/dist/ward/door.d.ts +1 -0
- package/dist/ward/door.js +24 -5
- package/dist/ward/ground.d.ts +1 -0
- package/dist/ward/heirs.d.ts +1 -1
- package/dist/ward/heirs.js +15 -8
- package/dist/ward/owner.d.ts +5 -26
- package/dist/ward/owner.js +24 -15
- package/dist/ward/seal.js +3 -0
- package/dist/ward/stance.d.ts +4 -1
- package/dist/ward/stance.js +116 -54
- package/dist/ward/ward.d.ts +10 -0
- package/dist/ward/ward.js +87 -36
- package/package.json +4 -2
- package/src/being/being.ts +26 -10
- package/src/being/digest.ts +13 -11
- package/src/being/types.ts +10 -4
- package/src/conformance/estate.ts +1 -3
- package/src/conformance/index.ts +1 -1
- package/src/harbor/core.ts +110 -26
- package/src/harbor/dial.ts +6 -4
- package/src/harbor/memory.ts +3 -2
- package/src/harbor/reach.ts +38 -11
- package/src/ward/allowance.ts +8 -2
- package/src/ward/arithmetic.ts +11 -3
- package/src/ward/cells.ts +76 -25
- package/src/ward/door.ts +22 -5
- package/src/ward/ground.ts +9 -2
- package/src/ward/heirs.ts +13 -6
- package/src/ward/owner.ts +24 -24
- package/src/ward/seal.ts +2 -0
- package/src/ward/stance.ts +117 -48
- package/src/ward/ward.ts +86 -35
- package/vectors/arithmetic.json +7 -0
- package/vectors/framing.json +14 -5
package/SPEC.md
CHANGED
|
@@ -86,9 +86,12 @@ ground
|
|
|
86
86
|
instantiate (class name, stance) -> object | null
|
|
87
87
|
carry (ward pk, bytes) -> bytes | undefined undefined: no door was reached. a throw is read the same.
|
|
88
88
|
random (n) -> n bytes of entropy. every key a ward mints is drawn from it.
|
|
89
|
+
wrote () -> nothing. the ward says it after every write to its partition, and nothing else:
|
|
90
|
+
a harbor that keeps the partition saves after it, when it likes and in the order
|
|
91
|
+
it was told. a harbor that keeps nothing leaves it out.
|
|
89
92
|
```
|
|
90
93
|
|
|
91
|
-
Nothing else is ever passed. A harbor that needs a
|
|
94
|
+
Nothing else is ever passed. A harbor that needs a seventh thing has found a
|
|
92
95
|
gap in Quo or a leak into the ward. The shape is `Ground` in
|
|
93
96
|
`src/ward/ground.ts`.
|
|
94
97
|
|
|
@@ -133,7 +136,7 @@ ever offered, and nothing here may be missing.
|
|
|
133
136
|
stance
|
|
134
137
|
cells her state. values only.
|
|
135
138
|
|
|
136
|
-
occupants.invite(id)
|
|
139
|
+
occupants.invite(id, notes?) -> invitation | null awaitable: a key is minted
|
|
137
140
|
occupants.remove(id) -> nothing
|
|
138
141
|
|
|
139
142
|
standings.knock(invitation, method?, args?, wanted?) -> object | silence | word
|
|
@@ -142,7 +145,8 @@ stance
|
|
|
142
145
|
standings[id].ask(method?, args?, wanted?) -> object | silence | word
|
|
143
146
|
standings.remove(id) -> nothing
|
|
144
147
|
|
|
145
|
-
boot(class, key)
|
|
148
|
+
boot(class, key, id?) -> key | null a new being of her ward, by class name.
|
|
149
|
+
with an id, her standing to her under it
|
|
146
150
|
|
|
147
151
|
wanted = { time? } what this one ask may spend, in milliseconds. optional,
|
|
148
152
|
and so is saying anything at all
|
|
@@ -152,7 +156,7 @@ Return table.
|
|
|
152
156
|
|
|
153
157
|
| call | returns | when |
|
|
154
158
|
| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
|
|
155
|
-
| invite(id)
|
|
159
|
+
| invite(id, notes?) | invitation | she minted a fresh id; the occupant record exists from now, with those notes on it |
|
|
156
160
|
| | null | the id already names a record, occupant or standing, or is a reserved word |
|
|
157
161
|
| remove(id) | nothing | always. removing what is not there is nothing |
|
|
158
162
|
| knock(inv, m, a, w) | object | the far being answered |
|
|
@@ -166,17 +170,25 @@ Return table.
|
|
|
166
170
|
| ask(m, a, w) | object | she answered |
|
|
167
171
|
| | silence | she chose to say nothing, or bytes came back that are not Quo's |
|
|
168
172
|
| | word | why not: removed, absent, repeated, threw, unreached, late, dropped |
|
|
169
|
-
| boot(
|
|
170
|
-
| | null | the key is already booted, the harbor holds no such class,
|
|
173
|
+
| boot(cls, key, id?) | key | she exists from now, with fresh cells, and a restart finds her; with an id, her maker holds her standing |
|
|
174
|
+
| | null | the key is already booted, the harbor holds no such class, the class threw at birth, the id is one the maker |
|
|
175
|
+
| | | already holds or a reserved word, or the relation was refused; nothing of the being made is left |
|
|
171
176
|
|
|
172
177
|
A being may make, and only the owner reaches into another. Boot touches
|
|
173
|
-
nobody: the being made has empty cells and no relation
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
nobody else: the being made has empty cells and no relation but the one her
|
|
179
|
+
maker named, and every other is invited and taken like any other. That one is
|
|
180
|
+
made the way all of them are, and it is the whole of what making gives: the
|
|
181
|
+
being made mints an occupant for her maker under the maker's own key, so she
|
|
182
|
+
knows who made her by that name and by nothing else, and her maker knocks
|
|
183
|
+
with it and takes the standing under the id she gave. Naming no id makes no
|
|
184
|
+
relation at all, and a being made that way is reachable by the owner alone. A
|
|
185
|
+
relation that could not be made is a boot that made nobody: the being made
|
|
186
|
+
goes out again, and nobody saw her, since she is a moment old and named to
|
|
187
|
+
no one yet. What the owner has that the maker does not is every ask that
|
|
188
|
+
reaches into a being from outside: public, invite and knock for her, remove,
|
|
189
|
+
unboot. Who may ask her to boot is her gate, as who may ask her anything is.
|
|
190
|
+
|
|
191
|
+
Ask, knock, invite, take and boot are awaitable. Async where the language has it,
|
|
180
192
|
blocking where it does not. Every ask she makes is a new call.
|
|
181
193
|
|
|
182
194
|
The asker has three shapes and no fourth, and nothing else ever reaches her.
|
|
@@ -281,8 +293,8 @@ S4 the reply is not one bytes came back that do not open, or are n
|
|
|
281
293
|
S5 the far door answered silence, or a word, exactly as the door said it.
|
|
282
294
|
```
|
|
283
295
|
|
|
284
|
-
And unreached in two: `U1` the args are not
|
|
285
|
-
sealed, so nothing left;
|
|
296
|
+
And unreached in two: `U1` the args are not one object of values, or could
|
|
297
|
+
not be sealed, so nothing left;
|
|
286
298
|
`U2` the harbor returned nothing, or threw, so no door was reached. The number is
|
|
287
299
|
taken all the same, and the gap it leaves in the count is harmless: the door
|
|
288
300
|
honours any number above its mark.
|
|
@@ -302,9 +314,9 @@ strangers: silence, nothing written, heard false
|
|
|
302
314
|
D1 the box does not open wrong padlock, garbage, too short. the reply is sealed to the lid if
|
|
303
315
|
the bytes carried one, and is noise otherwise. a lid that will not
|
|
304
316
|
take a seal, a small-order point, is no lid: noise.
|
|
305
|
-
D2 the payload is malformed by or next not 64 hex, method not a string, args
|
|
306
|
-
|
|
307
|
-
number or at zero.
|
|
317
|
+
D2 the payload is malformed by or next not 64 lowercase hex, method not a string, args present and
|
|
318
|
+
not one object of values, seq not a whole number from one, time not a
|
|
319
|
+
whole number above zero, hops not a whole number or at zero.
|
|
308
320
|
D3 for nobody, and nobody is home no public being on this ward.
|
|
309
321
|
D4 for nobody, signature fails the payload names a key it was not signed with.
|
|
310
322
|
D5 the heir is not held never minted here, and not one she removed either.
|
|
@@ -320,7 +332,7 @@ D10 the number is refused `repeated`: already honoured, or at or belo
|
|
|
320
332
|
choices: the number spent, the keys rotated, heard true
|
|
321
333
|
D11 she threw `threw`
|
|
322
334
|
D12 she answered silence on a named ask, or on the empty ask. nothing at all is silence too.
|
|
323
|
-
D13 she answered a
|
|
335
|
+
D13 she answered a non-value `threw`: a word, a Date, a cycle, a NaN, none is hers to make.
|
|
324
336
|
```
|
|
325
337
|
|
|
326
338
|
The signature is verified before anything is written and before any word is
|
|
@@ -380,7 +392,9 @@ Nothing else is a value: no dates, no references, no functions, no native
|
|
|
380
392
|
types. An id or a standing is never a value.
|
|
381
393
|
|
|
382
394
|
Args are held to the same rule before they are sealed: an ask whose args
|
|
383
|
-
|
|
395
|
+
are not one object of values is unreached, because nothing left. Her answer
|
|
396
|
+
is held to it at the door: a shape that is not a value is `threw` to a bound
|
|
397
|
+
key and silence to a stranger, so nothing crosses altered. Cells hold values and
|
|
384
398
|
refuse anything else at the moment of writing, in her own frame: a Map, a
|
|
385
399
|
Date, a NaN, a cyclic object, a function, an object that is not a plain one.
|
|
386
400
|
The refusal is a throw where she wrote it, which her ward turns into the
|
|
@@ -468,7 +482,12 @@ and take refuse them like the ward's words.
|
|
|
468
482
|
### Cells
|
|
469
483
|
|
|
470
484
|
Cells are I-JSON values. The ward may persist them. A restart is silent: she
|
|
471
|
-
is constructed again with the same cells.
|
|
485
|
+
is constructed again with the same cells. Three keys at their root are the
|
|
486
|
+
ward's, `standings`, `occupants` and `class`, and a write of hers to them is
|
|
487
|
+
refused where she wrote it, like a non-value. A value nested past sixty-four
|
|
488
|
+
levels, a hole in a list, an accessor, and a key named `__proto__` are
|
|
489
|
+
refused the same way: JSON writes each one way and a runtime reads it
|
|
490
|
+
another, so no harbor can keep them.
|
|
472
491
|
|
|
473
492
|
```
|
|
474
493
|
cells
|
|
@@ -483,7 +502,10 @@ cells
|
|
|
483
502
|
- `seen` is the digest her ward last saw arrive with an answer. A silent
|
|
484
503
|
refresh leaves it untouched.
|
|
485
504
|
- `notes` is hers. Quo never reads it. Tier, expiry, kinship between an
|
|
486
|
-
occupant and a standing that are the same far being: all hers.
|
|
505
|
+
occupant and a standing that are the same far being: all hers. `invite`
|
|
506
|
+
may seed it, which is how an inviter says the terms it mints under, and
|
|
507
|
+
after that it is written by nobody but her. The owner's `invite` passes
|
|
508
|
+
what it was given straight through; the ward reads none of it.
|
|
487
509
|
|
|
488
510
|
## Relations
|
|
489
511
|
|
|
@@ -543,7 +565,9 @@ and a knock on a taken invitation joins the standing's lane.
|
|
|
543
565
|
The keys an ask sends under are read when she calls, not when the lane
|
|
544
566
|
reaches her: an ask issued while the standing stood is answered even if she
|
|
545
567
|
drops it in the next line. An ask on a standing she has already dropped is
|
|
546
|
-
|
|
568
|
+
the word `dropped`, and nothing is sent. A knock on the invitation it was
|
|
569
|
+
born on, after take and a drop, is a knock as a heir the far door has already
|
|
570
|
+
spent, so bytes leave and the door's silence comes back: after take the
|
|
547
571
|
invitation names the standing and nothing else, so it is exactly as alive as
|
|
548
572
|
the standing is.
|
|
549
573
|
|
|
@@ -622,8 +646,8 @@ is sealed to. Beings never own a padlock.
|
|
|
622
646
|
- **Her own key** is what the knocker mints at knock. She signs the knock
|
|
623
647
|
with the heir and announces her own key in it. The door binds the id to
|
|
624
648
|
her key and the heir dies as it speaks. Rotation two: from now on she
|
|
625
|
-
signs with a key the inviter never held. A knock that announces nothing
|
|
626
|
-
binds nothing.
|
|
649
|
+
signs with a key the inviter never held. A knock that announces nothing,
|
|
650
|
+
or announces the heir itself, binds nothing.
|
|
627
651
|
- **Next.** Every ask she sends announces the key she will sign with next.
|
|
628
652
|
The door holds two pks for her: the one that may speak now, and the one
|
|
629
653
|
it vouched for. Whichever speaks first wins, and the other dies. There is
|
|
@@ -709,12 +733,12 @@ what makes three answers three. A wait that does not end is not an object,
|
|
|
709
733
|
not silence and not unreached, and before the allowance a cycle of legal asks
|
|
710
734
|
could produce one: A asks B, B answering asks A back, A answering asks B on
|
|
711
735
|
the relation the first ask still holds. Now the innermost wait gives up, the
|
|
712
|
-
|
|
736
|
+
word `late` unwinds outward, and the ask ends.
|
|
713
737
|
|
|
714
|
-
**A wait that ran out is
|
|
715
|
-
nothing was delivered and is safe to ask
|
|
716
|
-
such thing: the far door may have heard
|
|
717
|
-
back late is not read.
|
|
738
|
+
**A wait that ran out is the word `late`, never silence and never
|
|
739
|
+
unreached.** Unreached promises nothing was delivered and is safe to ask
|
|
740
|
+
again. A bound that expired knows no such thing: the far door may have heard
|
|
741
|
+
and be working still. What comes back late is not read.
|
|
718
742
|
|
|
719
743
|
The harbor keeps a patience of its own, and the two never read each other.
|
|
720
744
|
What a harbor hands back as nothing came back is unreached, and unreached
|
|
@@ -722,7 +746,7 @@ says nothing was delivered: a harbor may answer it only where it knows the
|
|
|
722
746
|
bytes never arrived, no reach for that pk, a socket that would not open, a
|
|
723
747
|
link that is down. A harbor that sent them and then gave up knows no such
|
|
724
748
|
thing, and must not answer at all; the ward's bound will ring, and that is
|
|
725
|
-
|
|
749
|
+
`late`, which promises nothing. A harbor that throws has answered nothing
|
|
726
750
|
in a louder voice, and the ward reads it as nothing: unreached. So a harbor
|
|
727
751
|
may hold a shorter patience than the ward for its own reasons, a socket it
|
|
728
752
|
wants back or a queue it will not grow, and whichever ends first ends the
|
|
@@ -747,7 +771,10 @@ Every arrival is judged by the door, and named by it or falls silent.
|
|
|
747
771
|
payload names, and dispatch as `{}`. Nothing is written.
|
|
748
772
|
3. For a heir: admit the signer if it is the key held for that heir or the
|
|
749
773
|
key that key announced, or the key held when she removed the id. Verify
|
|
750
|
-
the signature.
|
|
774
|
+
the signature. Read admission again, since the door judges arrivals
|
|
775
|
+
concurrently and a knock that raced this one may have spent the heir
|
|
776
|
+
while the signature was checked. Only then say anything or write
|
|
777
|
+
anything: a removed
|
|
751
778
|
relation is `removed`, a being not back this run is `absent`, a knock
|
|
752
779
|
with no key is `unannounced`, a number already honoured is `repeated`,
|
|
753
780
|
and none of those writes. Else spend the number and settle the keys, so
|
|
@@ -800,14 +827,17 @@ on the bytes.
|
|
|
800
827
|
```
|
|
801
828
|
ask on the wire box = ephemeral X25519 pk (32) || AES-GCM( payload || signature (64) )
|
|
802
829
|
sealed to the ward padlock. nothing rides outside it.
|
|
803
|
-
payload = JSON { to, by, next, seq, time, hops?, method?, args }
|
|
804
|
-
signed by `by`. to, by, next: 64 hex. to, next may be null.
|
|
805
|
-
to names the heir, or null for the public being.
|
|
830
|
+
payload = JSON { to, by, next, seq, time, hops?, method?, args? }
|
|
831
|
+
signed by `by`. to, by, next: 64 lowercase hex. to, next may be null.
|
|
832
|
+
to names the heir, or null for the public being. seq is a whole
|
|
833
|
+
number from one. args, when present, is one object of values.
|
|
806
834
|
hops is reserved: a whole number, never below zero,
|
|
807
835
|
refused at zero, and nothing sets it.
|
|
808
836
|
reply on the wire box = ephemeral X25519 pk (32) || AES-GCM( reply || signature (64) )
|
|
809
837
|
sealed to the ask's ephemeral pk, signed by the ward key
|
|
810
838
|
reply = JSON { object, seen } | { silence: true } | { quo: word }
|
|
839
|
+
seen is always present: the digest, 64 hex, on a named ask that
|
|
840
|
+
had one, and null otherwise, the empty ask included.
|
|
811
841
|
word: removed | absent | unannounced | repeated | threw. only to a key the door bound.
|
|
812
842
|
```
|
|
813
843
|
|
|
@@ -854,8 +884,9 @@ It owns nothing durable. It is three things, all in the harbor's hands:
|
|
|
854
884
|
its seed, its partition, and the classes its beings are made of. The
|
|
855
885
|
partition names each class and the harbor holds the bodies, so a ward is
|
|
856
886
|
whole only where all three are, and a harbor that keeps one remembers where
|
|
857
|
-
the other two came from. It writes as it runs
|
|
858
|
-
learns whether anything was kept. Between one
|
|
887
|
+
the other two came from. It writes as it runs, says so after every write,
|
|
888
|
+
and never decides, asks, or learns whether anything was kept. Between one
|
|
889
|
+
run and the next, a ward is
|
|
859
890
|
nothing at all.
|
|
860
891
|
|
|
861
892
|
One ward is one runtime and one language, and every being in it shares both.
|
|
@@ -871,8 +902,9 @@ list is a promise, not an accident: `test/terrain.test.ts` fails the build
|
|
|
871
902
|
both when a file names a platform and when it reaches for a global outside
|
|
872
903
|
the list, and when the list names one the tree has stopped using.
|
|
873
904
|
|
|
874
|
-
The package exports
|
|
875
|
-
|
|
905
|
+
The package exports the emitted `dist/`, JavaScript with a declaration
|
|
906
|
+
beside it, on every specifier, so plain Node imports it; the tree runs the
|
|
907
|
+
source directly, and every other terrain reaches a ward through a
|
|
876
908
|
bundler. `test/bundle.test.ts` bundles the three words and runs them, and
|
|
877
909
|
asserts the artefact carries nothing a terrain cannot provide.
|
|
878
910
|
|
|
@@ -887,8 +919,9 @@ of every terrain at once. Five run it: Node, a browser, workerd, Deno and
|
|
|
887
919
|
Bun. Bun is the one that is not V8, so "the language alone" is checked
|
|
888
920
|
against two implementations of the language and not one.
|
|
889
921
|
|
|
890
|
-
`npm run check` is Node alone, and stays fast. `npm run check:terrain` is
|
|
891
|
-
|
|
922
|
+
`npm run check` is Node alone, and stays fast. `npm run check:terrain` is
|
|
923
|
+
every other terrain, the browser, workerd, Deno and Bun, kept apart because
|
|
924
|
+
each is a binary of its own and the browser is a download and not a
|
|
892
925
|
package: a fresh clone needs `npx playwright install chromium` first, and is
|
|
893
926
|
told so in one sentence rather than a stack trace.
|
|
894
927
|
|
|
@@ -897,7 +930,9 @@ told so in one sentence rather than a stack trace.
|
|
|
897
930
|
Everything durable a ward has is here, every secret included, and nothing
|
|
898
931
|
here is ever in a being's cells. Values only, so the harbor may persist it as
|
|
899
932
|
it likes: as this process's objects, as a row, as a line of JSON on a disk. A
|
|
900
|
-
reboot from its JSON is the same ward.
|
|
933
|
+
reboot from its JSON is the same ward. A harbor that keeps it copies it
|
|
934
|
+
through JSON and never structured-clones it: what the ward hands out is
|
|
935
|
+
behind the cells guard. The shape is `src/ward/partition.ts`.
|
|
901
936
|
|
|
902
937
|
```
|
|
903
938
|
partition
|
|
@@ -949,7 +984,9 @@ A constructor that throws on a restart takes only herself down: she is
|
|
|
949
984
|
absent this run, no door, so `absent` at her door to the keys she bound and
|
|
950
985
|
silence to strangers, and her cells sit untouched waiting for the run that
|
|
951
986
|
can read them. The ward is up and so is everyone
|
|
952
|
-
else.
|
|
987
|
+
else. Her row is hers while she is absent: the owner sees her as absent, no
|
|
988
|
+
class boots under her key, and the owner may unboot her. A class the harbor
|
|
989
|
+
does not hold this run is the same absence, and so is a
|
|
953
990
|
class the harbor holds at a body the cells were not written for: which body
|
|
954
991
|
a ward reboots on is the harbor's decision, and a ward cannot tell an old
|
|
955
992
|
one from a new one. Loss of the partition is loss of every relation,
|
|
@@ -989,10 +1026,10 @@ renders anyone's and holds no list of its own.
|
|
|
989
1026
|
|
|
990
1027
|
```
|
|
991
1028
|
ask() -> { asks: [boot, public, invite, knock, remove, unboot],
|
|
992
|
-
notes: { pk, beings: { key: { class, public, digest } } } }
|
|
1029
|
+
notes: { pk, beings: { key: { class, public, digest, absent? } } } }
|
|
993
1030
|
ask('boot', { key, class }) -> { booted: key } | { error }
|
|
994
1031
|
ask('public', { key }) -> { public: key } | { error }
|
|
995
|
-
ask('invite', { being, id })
|
|
1032
|
+
ask('invite', { being, id, notes? }) -> invitation | null | { error }
|
|
996
1033
|
ask('knock', { being | { boot: class, key },
|
|
997
1034
|
id, invitation, method?, args?, wanted? })
|
|
998
1035
|
-> { taken: id | null, answer } | { error: 'silence' | 'unreached' | ... }
|
|
@@ -1004,16 +1041,20 @@ anything else -> { error: 'unknown ask' }
|
|
|
1004
1041
|
- The owner is a caller like any other: `wanted` says what its knock may
|
|
1005
1042
|
spend, and saying nothing is the ward's default, exactly as for a being.
|
|
1006
1043
|
- The describe runs every being's own describe as `OWNER` and hashes it; a
|
|
1007
|
-
being that throws or falls silent there shows a null digest.
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1044
|
+
being that throws or falls silent there shows a null digest. A being
|
|
1045
|
+
absent this run is listed with a null digest and `absent: true`.
|
|
1046
|
+
- Boot refuses a key that has a row in the partition, booted this run or
|
|
1047
|
+
absent, and a class the harbor does not know; a boot that made nobody
|
|
1048
|
+
leaves the partition as it found it. A throw at birth is that boot's
|
|
1049
|
+
error, `{ error: 'threw at birth' }`, and nothing half-lives; a being's
|
|
1050
|
+
own boot answers null for it.
|
|
1011
1051
|
- Public marks a being already booted as the ward's one public being. It
|
|
1012
1052
|
refuses a key not booted, the ward's own pk, and a second public being
|
|
1013
1053
|
while one stands, since marking a second would leave the first holding
|
|
1014
1054
|
every relation she had, reachable by nobody at the bare pk and told by
|
|
1015
1055
|
nobody she was replaced. Marking the one already public again is
|
|
1016
|
-
answered, and changes nothing.
|
|
1056
|
+
answered, and changes nothing. A public being absent this run is
|
|
1057
|
+
reachable by nobody already, so the mark may move to another.
|
|
1017
1058
|
- Invite on the ward's own pk mints an owner, and only the root may ask it:
|
|
1018
1059
|
from the door it is answered as an invite on nobody. The ward has
|
|
1019
1060
|
occupants, its owners, and no standings: a knock for the ward itself is a
|
|
@@ -1034,7 +1075,9 @@ anything else -> { error: 'unknown ask' }
|
|
|
1034
1075
|
heir with silence as one that never had a public being does. The ward
|
|
1035
1076
|
itself is refused, for the reason knock is: it would be a ward deleting
|
|
1036
1077
|
itself from inside its own map, leaving its owners bound to a door that is
|
|
1037
|
-
gone.
|
|
1078
|
+
gone. An absent being is unbooted the same way: she has no stance to speak
|
|
1079
|
+
for her, so the ward closes her heirs itself, and her occupants hear
|
|
1080
|
+
`removed`.
|
|
1038
1081
|
- The owner's remove is the mirror of its knock: a relation out of a being,
|
|
1039
1082
|
by id, and the id may be an occupant or a standing, since the two share
|
|
1040
1083
|
one namespace. The being's own remove says nothing; the owner hears
|
|
@@ -1104,7 +1147,12 @@ being, holds no id, reads no partition, and is nobody outside its device.
|
|
|
1104
1147
|
|
|
1105
1148
|
The harbor:
|
|
1106
1149
|
|
|
1107
|
-
- Boots wards as processes, one seed each, and keeps each ward's partition
|
|
1150
|
+
- Boots wards as processes, one seed each, and keeps each ward's partition:
|
|
1151
|
+
saves it when the ward says it wrote, one save at a time per ward and in
|
|
1152
|
+
the order it was told, so a being driven in process is kept the way one
|
|
1153
|
+
reached through a door is. A save that fails is the harbor's to count and
|
|
1154
|
+
never a door's to answer: the door answered bytes, and a full disk is not
|
|
1155
|
+
a reason the far side may hear.
|
|
1108
1156
|
- Holds the class bodies for its wards, and remembers for each ward where
|
|
1109
1157
|
its bodies came from, so that a restart is on the same bodies unless the
|
|
1110
1158
|
harbor decides otherwise. Constructs a being when a ward names a class
|
|
@@ -1154,12 +1202,13 @@ one hop: a pk that is not its own and not a direct peer's is nothing. It
|
|
|
1154
1202
|
keeps no lease: two of them may hold one seed and one partition, which is
|
|
1155
1203
|
the broken vouch every real harbor refuses, kept here so that the tests can
|
|
1156
1204
|
show what divergence looks like. Every ward and being assertion in the tree
|
|
1157
|
-
is made on it, so that the network never hides a fault in the words
|
|
1205
|
+
is made on it first, so that the network never hides a fault in the words;
|
|
1206
|
+
the harbor core then passes the same suite over memory stores.
|
|
1158
1207
|
|
|
1159
1208
|
What a harbor on a device keeps is a ward's three parts under one name: the
|
|
1160
1209
|
seed, the partition, and a **ward record**, which says where the class
|
|
1161
1210
|
bodies come from and which being is the user's. That is the **store**,
|
|
1162
|
-
`src/harbor/store.ts`: list, load, put, save the partition
|
|
1211
|
+
`src/harbor/store.ts`: list, load, put, save the partition when the ward wrote,
|
|
1163
1212
|
save the record after a boot learned the pk, take a ward out as the first
|
|
1164
1213
|
move of a migration, and keep the directory's hints, a pk to a URL, across
|
|
1165
1214
|
a restart. A name already kept is refused. A store reads nothing it keeps
|
|
@@ -1183,14 +1232,17 @@ WebSocket, and this tree holds either end of a line and never a listener:
|
|
|
1183
1232
|
who accepts a socket is the terrain's business. A reach reads nothing; a
|
|
1184
1233
|
harbor opens no box but its own probe's.
|
|
1185
1234
|
Nothing comes back only where the reach knows nothing was delivered: no such
|
|
1186
|
-
pk at the far end, a connection that would not open
|
|
1235
|
+
pk at the far end, a connection that would not open or closed before it
|
|
1236
|
+
opened, a line already gone, a status from a request that says the listener
|
|
1237
|
+
did not take the bytes, refused on their face or with nobody behind the
|
|
1238
|
+
gateway. A
|
|
1187
1239
|
reach that sent the bytes and lost the line after answers nothing at all,
|
|
1188
1240
|
and the ward's bound ends the ask. Every reach passes
|
|
1189
1241
|
`src/conformance/reach.ts` untouched, and the frames are pinned in
|
|
1190
1242
|
`vectors/wire.json`.
|
|
1191
1243
|
|
|
1192
1244
|
A reach also carries the **wire suite**: which frames these are, and which
|
|
1193
|
-
|
|
1245
|
+
five algorithms seal what they travel with. It is one number, it is not
|
|
1194
1246
|
negotiated, and today it is 1. It rides where a line is opened and never on
|
|
1195
1247
|
an ask: in the text frame a socket announces itself with, and as the header
|
|
1196
1248
|
`quo-suite` on a request. So an ask is still bytes from the first one, with
|
|
@@ -1209,7 +1261,7 @@ refused as it is written: the number is compared as it was sent, so a suite
|
|
|
1209
1261
|
is one spelling and not a family of them.
|
|
1210
1262
|
|
|
1211
1263
|
This is the only place a second suite could ever be told from the first.
|
|
1212
|
-
The
|
|
1264
|
+
The five algorithms are named once and never negotiated, which is right: a
|
|
1213
1265
|
choice offered on the wire is a choice a stranger can push. But named once
|
|
1214
1266
|
is not the same as unnameable, and a kit that must one day seal differently
|
|
1215
1267
|
has to be able to say so to a kit that cannot, or the day it arrives every
|
|
@@ -1221,9 +1273,14 @@ is: the store and the reach put together under the ground. It boots every
|
|
|
1221
1273
|
ward the store keeps, keeps the map of ward pk to door for its own wards
|
|
1222
1274
|
and the **directory**, pk to reach, for foreign ones, carries bytes to a
|
|
1223
1275
|
pk and delivers bytes from the wire to one door. The directory is filled
|
|
1224
|
-
|
|
1276
|
+
four ways, in this order: its own doors; a socket a dialer holds to it,
|
|
1225
1277
|
bound once the door behind the dialer's claim has proved it, and unbound
|
|
1226
|
-
when the line closes;
|
|
1278
|
+
when the line closes; the claims of a listener this harbor dialed, proven
|
|
1279
|
+
the same way and reached through that line; and a hint, a pk at a URL,
|
|
1280
|
+
kept in the store. A hint never displaces a reach proven at a door. An
|
|
1281
|
+
announce names at most sixty-four pks, proven a few at a time, and is the
|
|
1282
|
+
newest word: a proof still in flight from an earlier announce on the same
|
|
1283
|
+
line binds nothing when it lands.
|
|
1227
1284
|
|
|
1228
1285
|
An announce is a claim, and a claim binds nothing until proven: anyone who
|
|
1229
1286
|
can reach a listener could otherwise name a pk that is not theirs and take
|
|
@@ -1238,8 +1295,9 @@ listener proves the dialer's claims and the dialer the listener's. What a
|
|
|
1238
1295
|
relay that genuinely reaches a ward elsewhere can still do is forward the
|
|
1239
1296
|
probe and pass, and then it is a rendezvous for that ward like any other,
|
|
1240
1297
|
able to drop and nothing more. This is the one box a harbor ever opens,
|
|
1241
|
-
the one it sealed itself, and it reads nothing from it but
|
|
1242
|
-
signed. One rule makes a
|
|
1298
|
+
the one it sealed itself, and it reads nothing from it but that it is the
|
|
1299
|
+
silence a door owes such a box, signed by the claimed key. One rule makes a
|
|
1300
|
+
**rendezvous** of
|
|
1243
1301
|
any listener: bytes that arrive from the wire go to an own door or to a
|
|
1244
1302
|
socket this harbor holds for that pk, and never onward by request. So a
|
|
1245
1303
|
harbor that cannot be dialed is reached through the one it dialed, by
|
|
@@ -1285,8 +1343,7 @@ describes herself, per asker, by answering the empty ask.
|
|
|
1285
1343
|
|
|
1286
1344
|
Not stressed with: where anyone lives; crypto, keys, wire, storage, clocks,
|
|
1287
1345
|
or the harbor; strangers, because nobody her ward cannot name reaches her;
|
|
1288
|
-
whether an occupant is a person, a model or a program
|
|
1289
|
-
only the ward instantiates.
|
|
1346
|
+
whether an occupant is a person, a model or a program.
|
|
1290
1347
|
|
|
1291
1348
|
### Her obligations
|
|
1292
1349
|
|
|
@@ -1313,9 +1370,11 @@ only the ward instantiates.
|
|
|
1313
1370
|
asks is always a new call. The one exception is the public being, who is
|
|
1314
1371
|
reached with no heir and no count: the same bytes reach her twice, and
|
|
1315
1372
|
her answer must be safe to repeat.
|
|
1316
|
-
8.
|
|
1317
|
-
|
|
1318
|
-
|
|
1373
|
+
8. A being may make a being of her own ward, by class name and under a key
|
|
1374
|
+
she chooses, and holds no relation to her until one is invited and taken
|
|
1375
|
+
like any other; the ward builds the stance and the harbor holds the
|
|
1376
|
+
class, so she never touches either. Only the owner reaches into a being
|
|
1377
|
+
from outside.
|
|
1319
1378
|
9. Whatever her ward cannot name is not hers to worry about: what the ward
|
|
1320
1379
|
does with an arrival it cannot name is the ward's.
|
|
1321
1380
|
|
|
@@ -1338,10 +1397,13 @@ The raw shape needs no import from the kit. The kit also offers a base class,
|
|
|
1338
1397
|
inherited from Object's prototype is `{ error: 'unknown ask' }` and the
|
|
1339
1398
|
method is never entered.
|
|
1340
1399
|
- A declared ask with no method, or one named after the base's own members,
|
|
1341
|
-
fails at birth, loudly, so a boot fails and nothing half-lives.
|
|
1400
|
+
fails at birth, loudly, so a boot fails and nothing half-lives. The
|
|
1401
|
+
method is one on her prototype chain below Object's: a class field
|
|
1402
|
+
holding a function is not there yet when the base checks, and the
|
|
1403
|
+
refusal says so.
|
|
1342
1404
|
- `occupant(asker)` is the occupant record for whoever is at the door, and
|
|
1343
|
-
undefined at a public being. `invite`, `knock`, `take`, `
|
|
1344
|
-
`occupants` reach the stance and nothing else.
|
|
1405
|
+
undefined at a public being. `invite`, `knock`, `take`, `boot`, `cells`,
|
|
1406
|
+
`standings` and `occupants` reach the stance and nothing else.
|
|
1345
1407
|
|
|
1346
1408
|
### Examples
|
|
1347
1409
|
|
|
@@ -1375,6 +1437,7 @@ class Shop {
|
|
|
1375
1437
|
}
|
|
1376
1438
|
async answer(asker, method, args) {
|
|
1377
1439
|
if (method === undefined) return { asks: [{ name: 'hello', input: {} }], notes: {} };
|
|
1440
|
+
if (asker.id === undefined) return { welcome: false }; // a stranger at the public door has no record
|
|
1378
1441
|
const rec = this.s.cells.occupants[asker.id];
|
|
1379
1442
|
if (method === 'hello' && args.invitation) {
|
|
1380
1443
|
const back = await this.s.standings.knock(args.invitation, 'hi');
|
|
@@ -1408,15 +1471,18 @@ class Guest {
|
|
|
1408
1471
|
```
|
|
1409
1472
|
|
|
1410
1473
|
A relay. Forwards every ask to one standing and never looks inside, and asks
|
|
1411
|
-
for a short wait because she is one door of several.
|
|
1474
|
+
for a short wait because she is one door of several. Silence passes through
|
|
1475
|
+
her; a word does not, since a word out of a being is `threw` at the door,
|
|
1476
|
+
D13, so she says it as an error of her own.
|
|
1412
1477
|
|
|
1413
1478
|
```js
|
|
1414
1479
|
class Relay {
|
|
1415
1480
|
constructor(stance) {
|
|
1416
1481
|
this.s = stance;
|
|
1417
1482
|
}
|
|
1418
|
-
answer(asker, method, args) {
|
|
1419
|
-
|
|
1483
|
+
async answer(asker, method, args) {
|
|
1484
|
+
const out = await this.s.standings.next.ask(method, args, { time: 2000 });
|
|
1485
|
+
return isWord(out) ? { error: wordOf(out) } : out; // unreached and late reach her asker as an error, not as a throw
|
|
1420
1486
|
}
|
|
1421
1487
|
}
|
|
1422
1488
|
```
|
|
@@ -1452,8 +1518,7 @@ words, and nothing is kept for a holder of an earlier one.
|
|
|
1452
1518
|
|
|
1453
1519
|
The tree runs as TypeScript and the package ships as JavaScript. Node
|
|
1454
1520
|
strips types nowhere under `node_modules`, so a consumer cannot load the
|
|
1455
|
-
source
|
|
1456
|
-
version a stranger can import. `npm run build` emits `dist/` from `src`
|
|
1521
|
+
source. `npm run build` emits `dist/` from `src`
|
|
1457
1522
|
alone, one JavaScript file and one declaration file per source file with
|
|
1458
1523
|
every relative import rewritten, and the exports map points there, types
|
|
1459
1524
|
beside default. The
|
|
@@ -1463,11 +1528,12 @@ which is how a consumer does. `test/package.test.ts` is the one
|
|
|
1463
1528
|
suite that meets the package as a stranger: it packs the tarball,
|
|
1464
1529
|
installs it into an empty folder, and imports every entry point under
|
|
1465
1530
|
plain Node. `npm pack --dry-run` shows what ships: `dist/`, the four
|
|
1466
|
-
source folders, the vectors, this document, the README
|
|
1467
|
-
and nothing else. This document ships, decided here: it is the
|
|
1468
|
-
source and the vectors are read against, and a kit in another
|
|
1469
|
-
written against it and nothing else. Publishing runs both
|
|
1470
|
-
`npm run check` and `npm run check:terrain`, and refuses on a
|
|
1531
|
+
source folders, the vectors, this document, the README, the licence and
|
|
1532
|
+
the notice, and nothing else. This document ships, decided here: it is the
|
|
1533
|
+
truth the source and the vectors are read against, and a kit in another
|
|
1534
|
+
language is written against it and nothing else. Publishing runs both
|
|
1535
|
+
gates first, `npm run check` and `npm run check:terrain`, and refuses on a
|
|
1536
|
+
failure.
|
|
1471
1537
|
|
|
1472
1538
|
```
|
|
1473
1539
|
src/being/ the Being side. types, the base class, silence, the digest.
|
|
@@ -1514,8 +1580,9 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
1514
1580
|
- `src/conformance/index.ts`: one suite of behaviours, written against the
|
|
1515
1581
|
stance and one probe: a kit hands the suite its ward's heir table and
|
|
1516
1582
|
bind table as values, and a forged knock, so the suite can see that a
|
|
1517
|
-
refusal wrote nothing and a knock bound what it should. It reads
|
|
1518
|
-
never writes them
|
|
1583
|
+
refusal wrote nothing and a knock bound what it should. It reads the
|
|
1584
|
+
tables and never writes them; the forged knock is the one thing it sends
|
|
1585
|
+
through a door. With the beings in `src/conformance/beings.ts`: a
|
|
1519
1586
|
printer, a shop, two customers, one raw being with no base class, and a
|
|
1520
1587
|
member of an estate. Any ward must pass it, under any topology.
|
|
1521
1588
|
- `src/conformance/estate.ts`: the estate, and the last chapter of that
|
|
@@ -1594,8 +1661,11 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
1594
1661
|
reach suite against the socket framing over two lines in one process, so
|
|
1595
1662
|
the frames are asserted with no network under them; the conformance
|
|
1596
1663
|
suite against two harbor cores over memory stores reaching each other
|
|
1597
|
-
in-process, drop and adopt as the migration; a
|
|
1598
|
-
|
|
1664
|
+
in-process, drop and adopt as the migration; a ward that moves harbor
|
|
1665
|
+
keeping its pk and every relation, the rendezvous told on the line in
|
|
1666
|
+
hand; a ward booted or dropped after a line opened, announced on that
|
|
1667
|
+
line, and a claim on a pk nobody holds binding nothing; a restart from
|
|
1668
|
+
the store with the hints; the dialer over a stubbed line, announce, a claim proven
|
|
1599
1669
|
at a real door before it binds and a claim nobody there holds left
|
|
1600
1670
|
unbound, fallback, unbind and the wait before it dials again; and the frames against
|
|
1601
1671
|
`vectors/wire.json`, the ask, the reply, nothing delivered and the
|
|
@@ -1614,7 +1684,7 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
1614
1684
|
`node:assert/strict`, pair by pair. The suite means the same thing on
|
|
1615
1685
|
every terrain only if those two agree, so they are compared and not
|
|
1616
1686
|
trusted.
|
|
1617
|
-
- `test/floor.test.ts`: the
|
|
1687
|
+
- `test/floor.test.ts`: the five algorithms this terrain must carry, probed
|
|
1618
1688
|
one by one; that the arithmetic spends every one of them; and the two ways
|
|
1619
1689
|
a terrain can be short -- no `crypto.subtle` at all, and a subtle without
|
|
1620
1690
|
the curves -- each failing at the first call, in one sentence.
|
|
@@ -1745,7 +1815,7 @@ Decided here, so that the answer is not rediscovered:
|
|
|
1745
1815
|
- **invite**: mint an id and get its invitation.
|
|
1746
1816
|
- **knock**: an ask carrying an invitation. Binds the far side to the id.
|
|
1747
1817
|
- **take**: keep an answered knock as a standing, under her own id.
|
|
1748
|
-
- **ask**: method and args to a standing. Object, silence, or
|
|
1818
|
+
- **ask**: method and args to a standing. Object, silence, or a word back.
|
|
1749
1819
|
- **answer**: her one function. Object or silence out.
|
|
1750
1820
|
- **the empty ask**: ask with no method. Her answer to it is her blueprint.
|
|
1751
1821
|
- **blueprint**: her interface as she chooses to show it to one asker.
|
|
@@ -1759,11 +1829,13 @@ Decided here, so that the answer is not rediscovered:
|
|
|
1759
1829
|
- **unreached**: the word for no far door reached. Retryable.
|
|
1760
1830
|
- **notes**: hers, inside every occupant record. Quo never reads it.
|
|
1761
1831
|
- **partition**: the ward's files. Everything durable, every secret.
|
|
1762
|
-
- **ward record**: what a harbor keeps
|
|
1763
|
-
|
|
1764
|
-
the ward's.
|
|
1832
|
+
- **ward record**: what a harbor keeps beside a ward's seed and partition
|
|
1833
|
+
to boot her again: her pk, where the class bodies come from, and which
|
|
1834
|
+
being is the user's. The harbor's, never the ward's.
|
|
1835
|
+
- **lid**: the ephemeral X25519 pk an ask carries in front of its box. The
|
|
1836
|
+
reply is sealed to it and to nothing else. One per ask, never reused.
|
|
1765
1837
|
- **edge**: one of the two seams of Quo, harbor to ward and ward to being.
|
|
1766
1838
|
An object crosses once at birth, calls cross for the rest of the ward's
|
|
1767
1839
|
life, and nothing else crosses.
|
|
1768
|
-
- **ground**: the one object a harbor passes a ward.
|
|
1840
|
+
- **ground**: the one object a harbor passes a ward. Six things.
|
|
1769
1841
|
- **door**: the ward's one voice outward. Sealed bytes in, sealed bytes out.
|
package/dist/being/being.d.ts
CHANGED
|
@@ -13,10 +13,10 @@ export declare class Being {
|
|
|
13
13
|
get cells(): Cells;
|
|
14
14
|
get standings(): Standings;
|
|
15
15
|
get occupants(): Occupants;
|
|
16
|
-
invite(id: string): Promise<Invitation | null>;
|
|
16
|
+
invite(id: string, notes?: JsonObject): Promise<Invitation | null>;
|
|
17
17
|
knock(invitation: Invitation, method?: string, args?: JsonObject, wanted?: Wanted): Promise<Answer>;
|
|
18
18
|
take(id: string, invitation: Invitation): Promise<string | null>;
|
|
19
|
-
boot(className: string, key: string): string | null
|
|
19
|
+
boot(className: string, key: string, id?: string): Promise<string | null>;
|
|
20
20
|
occupant(asker: Asker): OccupantRecord | undefined;
|
|
21
21
|
describe(asker: Asker): Blueprint;
|
|
22
22
|
answer(asker: Asker, method?: string, args?: JsonObject): Promise<Reply>;
|