nervur 0.22.2-6 → 0.22.2-7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/AUTHORING.md +74 -44
  2. package/COMMAND.md +25 -10
  3. package/FACES.md +20 -9
  4. package/FACULTIES.md +163 -92
  5. package/GROUNDS.md +120 -67
  6. package/KIT-SPEC.md +10 -7
  7. package/README.md +16 -14
  8. package/dist/app/app-ground.d.ts +1 -1
  9. package/dist/app/app-ground.js +5 -5
  10. package/dist/app/index.d.ts +1 -1
  11. package/dist/app/index.js +1 -1
  12. package/dist/app/native.d.ts +3 -3
  13. package/dist/app/native.js +3 -3
  14. package/dist/bench/bench-ground.d.ts +18 -14
  15. package/dist/bench/bench-ground.js +34 -33
  16. package/dist/bench/fake-unlock.d.ts +6 -0
  17. package/dist/bench/fake-unlock.js +10 -0
  18. package/dist/bodies/kept.d.ts +5 -16
  19. package/dist/bodies/kept.js +17 -36
  20. package/dist/browser/browser-ground.d.ts +8 -16
  21. package/dist/browser/browser-ground.js +11 -22
  22. package/dist/browser/index.d.ts +1 -1
  23. package/dist/browser/index.js +1 -1
  24. package/dist/browser/locked-unlock.d.ts +24 -0
  25. package/dist/browser/locked-unlock.js +85 -0
  26. package/dist/edge/edge-ground.d.ts +8 -17
  27. package/dist/edge/edge-ground.js +14 -26
  28. package/dist/edge/index.d.ts +2 -2
  29. package/dist/edge/index.js +1 -1
  30. package/dist/edge/secret-unlock.d.ts +7 -0
  31. package/dist/edge/secret-unlock.js +13 -0
  32. package/dist/ground/ground.d.ts +155 -103
  33. package/dist/ground/ground.js +548 -314
  34. package/dist/ground/views.d.ts +59 -0
  35. package/dist/ground/views.js +188 -0
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.js +2 -1
  38. package/dist/node/bridge.d.ts +3 -0
  39. package/dist/node/bridge.js +66 -5
  40. package/dist/node/cli.js +32 -19
  41. package/dist/node/index.d.ts +2 -4
  42. package/dist/node/index.js +1 -3
  43. package/dist/node/node-ground.d.ts +6 -16
  44. package/dist/node/node-ground.js +59 -53
  45. package/dist/node/unlock.d.ts +17 -0
  46. package/dist/node/unlock.js +86 -0
  47. package/dist/serve/index.d.ts +20 -7
  48. package/dist/serve/index.js +21 -9
  49. package/package.json +1 -1
  50. package/dist/bench/fake-custody.d.ts +0 -15
  51. package/dist/bench/fake-custody.js +0 -39
  52. package/dist/bench/fake-keys.d.ts +0 -4
  53. package/dist/bench/fake-keys.js +0 -9
  54. package/dist/browser/locked-custody.d.ts +0 -36
  55. package/dist/browser/locked-custody.js +0 -114
  56. package/dist/edge/secret-custody.d.ts +0 -6
  57. package/dist/edge/secret-custody.js +0 -40
  58. package/dist/node/custody.d.ts +0 -34
  59. package/dist/node/custody.js +0 -58
  60. package/dist/node/file-keys.d.ts +0 -11
  61. package/dist/node/file-keys.js +0 -44
  62. package/dist/node/held-keys.d.ts +0 -14
  63. package/dist/node/held-keys.js +0 -36
  64. package/dist/node/keychain-keys.d.ts +0 -12
  65. package/dist/node/keychain-keys.js +0 -55
package/AUTHORING.md CHANGED
@@ -11,7 +11,7 @@ The shop has three classes and one faculty.
11
11
  - `Order` is one order, from its first item to its shipping.
12
12
  - `Shop` is the steward, the being that runs the house.
13
13
  - `Lobby` is the public being, which strangers may ask.
14
- - `Payments` is a faculty that charges money, which the ground's
14
+ - `Payments` is a faculty that charges money, which a maker in
15
15
  `recipe.ts` makes.
16
16
 
17
17
  ## Words
@@ -31,7 +31,8 @@ The shop has three classes and one faculty.
31
31
  | role | a named test over the asker and her cells |
32
32
  | state | a name read from her cells that decides which asks exist |
33
33
  | house | what holds beings, keeps their cells, and seals every ask |
34
- | ground | the process houses run in, holding their seeds, faculties and hands |
34
+ | ground | the process houses run in, holding one key and sealing their seeds, faculties and secrets under it |
35
+ | registry | code that makes faculties and bodies by name, for the ground to stand |
35
36
 
36
37
  ## A being
37
38
 
@@ -587,7 +588,8 @@ type Answer = { result: { pending: boolean } } | { error: { message: string } };
587
588
  /**
588
589
  * A payment provider in the ground's process. It answers a call id it has
589
590
  * seen with the answer it gave, so an effect sent twice charges once. A
590
- * provider that changes the world keeps these where a restart keeps them.
591
+ * provider that changes the world keeps these in the memory its maker
592
+ * receives, where a restart and a move keep them.
591
593
  */
592
594
  export class Payments {
593
595
  readonly #answered = new Map<string, Answer>();
@@ -615,28 +617,31 @@ export class Payments {
615
617
  export const paymentsOffer = (payments: Payments): Offer => ({ blueprint: PaymentsBlueprint, object: payments, window: 7 * 86_400_000 });
616
618
  ```
617
619
 
618
- An offer is `{ blueprint, object, kinds?, window?, handler?, stop? }`.
619
- The object answers each call with its call id, and one that changes the
620
- world answers a call id it has seen with the answer it gave. The ground's
621
- recipe makes each faculty by name, and `kinds` grants it to the classes
622
- it names. [Writing a faculty](FACULTIES.md) teaches the craft whole, with a
623
- faculty written in Python.
620
+ An offer is `{ blueprint, object, kinds?, window? }`. The object answers
621
+ each call with its call id, and one that changes the world answers a
622
+ call id it has seen with the answer it gave. A registry holds a maker
623
+ for each faculty by name. The ground stands a faculty when an entry names
624
+ its maker, and the entry's `kinds` grants it to the classes it names.
625
+ [Writing a faculty](FACULTIES.md) teaches the craft whole, with a faculty
626
+ written in Python.
624
627
 
625
628
  ```ts
626
629
  // recipe.ts
627
630
  import { Payments, paymentsOffer } from './payments.ts';
628
631
 
629
- export const faculties = () => ({
630
- payments: paymentsOffer(new Payments()),
631
- });
632
+ // A registry: the ground makes each faculty an entry names, by its maker here.
633
+ export const faculties = {
634
+ payments: () => paymentsOffer(new Payments()),
635
+ };
632
636
  ```
633
637
 
634
638
  ## A ground
635
639
 
636
640
  A ground is the process houses run in, and you write none. `nervur up`
637
- runs one on a folder: the recipe of its faculties, and a folder of code
638
- for each house. It keeps a record of its houses, and opens each on the
639
- bodies the record names.
641
+ runs one on a folder of code: a folder for each house, and each module
642
+ your entries name. It holds one key in `state/`, and keeps every entry
643
+ sealed in its drawer: which faculties stand, and which houses open on
644
+ which bodies.
640
645
 
641
646
  ### The shop's folder
642
647
 
@@ -676,11 +681,14 @@ The ground boots in one order, and a stop is that order reversed, on an
676
681
  interrupt and on `SIGTERM`.
677
682
 
678
683
  1. **Lock.** One ground to its state.
679
- 2. **Its own.** Its seeds and its record open.
680
- 3. **Faculties.** The recipe makes each, in its order.
681
- 4. **Houses.** Each house of the record opens on its bodies.
682
- 5. **Hook.** The listeners, then the hand on its socket.
683
- 6. **Ready.** It tells systemd it is up, where systemd waits.
684
+ 2. **Key.** Its key is read from `state/key`, drawn there on the first
685
+ start.
686
+ 3. **Drawer.** Its ledger opens, and the key opens its drawer.
687
+ 4. **Ladder.** Each faculty its drawer names stands on its registry. One
688
+ that fails stays down, and says why.
689
+ 5. **Houses.** Each house of the drawer opens on its bodies.
690
+ 6. **Hook.** The listeners, then the hand on its socket.
691
+ 7. **Ready.** It tells systemd it is up, where systemd waits.
684
692
 
685
693
  It is set by its environment.
686
694
 
@@ -693,26 +701,45 @@ It is set by its environment.
693
701
  | `NERVUR_ALLOW_PRIVATE` | `1` to dial private and loopback addresses, as two grounds on one machine do |
694
702
  | `NERVUR_BIND` | the address it listens on, `0.0.0.0` where unset |
695
703
  | `NERVUR_STATE` | its state, `state/` in its folder where unset |
696
- | `NERVUR_KEYCHAIN` | a keychain service holding its seeds on macOS, in place of files |
704
+ | `NERVUR_UNLOCK` | `keychain:<account>` keeps its key in the macOS keychain, in place of `state/key` |
697
705
  | `NERVUR_WAIT` | its bound on every ask, in milliseconds |
698
706
 
699
- The state holds each house's seed in a file its owner alone reads, and
700
- each house's ledger. A ledger appends every write and never rewrites
701
- one. Its witness keeps where it last stood, and a ledger behind its
702
- witness is refused. So a ledger restored alone cannot replay what a
703
- house already answered, though a whole state folder restored with its
704
- witnesses is not seen. A lost seed is a lost house.
707
+ The state holds the key in a file its owner alone reads, and the
708
+ ground's ledger. Every house keeps its rows in that ledger, sealed. A
709
+ ledger appends every write and never rewrites one. Its witness keeps
710
+ where it last stood, and a ledger behind its witness is refused. So a
711
+ ledger restored alone cannot replay what a house already answered,
712
+ though a whole state folder restored with its witness is not seen. A
713
+ lost key is a lost ground.
714
+
715
+ The ladder stands once, and the ground stands it again at every start.
716
+ The first entry stands the folder's `recipe.ts` as a registry, by the
717
+ ground's own maker `module`. The second makes the payments from it.
718
+
719
+ ```bash
720
+ npx nervur faculties add name=recipe make=module args='{"at":"recipe.ts"}'
721
+ ```
722
+
723
+ ```bash
724
+ npx nervur faculties add name=payments from=recipe make=payments
725
+ ```
705
726
 
706
727
  A house is added once, and the ground opens it again at every start.
707
728
 
708
729
  ```bash
709
- npx nervur houses add name=shop memory='{"body":"ledger"}' classes='{"body":"folder","at":"classes"}' faculties='["payments"]'
730
+ npx nervur houses add name=shop classes='{"body":"folder","at":"classes"}' faculties='["payments"]'
710
731
  ```
711
732
 
712
- `memory` and `classes` name the bodies the house is handed. `ledger` and
713
- `folder` are the ground's own, and a recipe may add more, a git registry
714
- or another store among them. `faculties` names what the house receives,
715
- and within it each offer's `kinds` names the classes that hold it.
733
+ `classes` names the body of the house's code. `folder` is the ground's
734
+ own, and a registry may add more, a git repository among them. A house
735
+ keeps its rows in the ground's ledger unless its entry names a `memory`
736
+ body. `faculties` names what the house receives, and within it each
737
+ faculty's entry names in `kinds` the classes that hold it.
738
+
739
+ A secret reaches a faculty the same way, and never through the
740
+ environment. `npx nervur secrets set -` reads `{ name, value }` from
741
+ standard input into the ground's sealed drawer. A faculty's entry names
742
+ the secrets its maker receives in `secrets`.
716
743
 
717
744
  ### The hand
718
745
 
@@ -774,7 +801,7 @@ with `loginctl enable-linger`. On macOS, the agent goes to
774
801
 
775
802
  ### In a page
776
803
 
777
- `BrowserGround.open({ recipe })` from `nervur/browser` runs the same
804
+ `BrowserGround.open({ registry })` from `nervur/browser` runs the same
778
805
  houses in a page. Every tab and the service worker of one origin share
779
806
  one ground: the one holding the Web Lock runs it, and the others reach
780
807
  its hand over a `BroadcastChannel`. When it closes, the next opens the
@@ -782,12 +809,12 @@ ground from the same storage. `hand` answers the same three requests
782
809
  the command sends: `describe`, a faculty's method, and an ask of a being
783
810
  in a named house.
784
811
 
785
- Its bodies are the browser's. Seeds are sealed under an AES key that
786
- IndexedDB holds unextractable, and memory is IndexedDB, named
787
- `indexeddb` in an entry. Classes load from the page's own origin through
788
- the `origin` body, `{ body: 'origin', at: '/house/index.js' }`, and a
789
- path off the origin is refused. The recipe is the object you pass: its
790
- faculties, made by a function, and any custom body.
812
+ Its bodies are the browser's. The ground's key is sealed under an AES
813
+ key that IndexedDB holds unextractable, and its memory is IndexedDB.
814
+ Classes load from the page's own origin through the `origin` body, `{
815
+ body: 'origin', at: '/house/index.js' }`, and a path off the origin is
816
+ refused. The registry is the object you pass: makers of faculties, and
817
+ of any custom body, beside the terrain's own.
791
818
 
792
819
  Any script on the origin can use the ground's keys, so the ground is
793
820
  whoever serves the origin's script. Give it an origin of its own, serve
@@ -811,10 +838,10 @@ app's web view, on two interfaces the shell fills in native code:
811
838
  which lands every write only where each key in `expect` still holds
812
839
  what it names, and answers whether it landed.
813
840
 
814
- The library holds the rest. Each house's seed goes into the secret
815
- store, and each memory into the native store, which the system never
816
- evicts as it may a web view's storage. A push token reaches the ground
817
- through a faculty your recipe makes.
841
+ The library holds the rest. The ground's key goes into the secret store,
842
+ and its memory into the native store, which the system never evicts as
843
+ it may a web view's storage. A push token reaches the ground through a
844
+ faculty your registry makes.
818
845
 
819
846
  ## What the house guarantees
820
847
 
@@ -871,5 +898,8 @@ at her call, or where an ask arrives.
871
898
  - A send to a private address, unless its ground allows it.
872
899
  - A call on the house beside `House.open`, `door` and `ask`.
873
900
  - A faculty in a house's code. Faculties are the ground's.
874
- - A custom body for a ground's own custody or memory.
901
+ - A drawer opened with a key that did not seal it.
902
+ - A secret read by a being, a describe or anyone but a maker its entry
903
+ names.
904
+ - A faculty entry for `secrets` or `moves`, which the hand alone reaches.
875
905
  - A ground an owner must write. Each terrain's ships.
package/COMMAND.md CHANGED
@@ -7,17 +7,21 @@ have read [the package's start](README.md), which runs a first ground.
7
7
 
8
8
  ## Starting a ground
9
9
 
10
- `nervur up` runs a NodeGround on a folder until it is stopped. The folder
11
- holds the ground's recipe, a folder of code for each house, and `state/`,
12
- where the ground keeps its seeds, its record and its hand.
10
+ `nervur up` runs a NodeGround on a folder until it is stopped. The
11
+ folder holds your code: a folder for each house, and each module or
12
+ program your entries name. `state/` holds the ground's key, its ledger
13
+ and its hand. The key is drawn on the first start, into a file your user
14
+ alone reads.
13
15
 
14
16
  ```bash
15
17
  npx nervur up .
16
18
  ```
17
19
 
18
20
  The ground stops cleanly on an interrupt and on `SIGTERM`. It takes a
19
- lock on its folder, so a second `nervur up` on the same folder refuses to
20
- start.
21
+ lock on its state, so a second `nervur up` on the same folder refuses to
22
+ start. Keep `state/` as you keep an ssh key: whoever holds its key and
23
+ its ledger holds the ground, and without the key the ledger opens
24
+ nothing.
21
25
 
22
26
  `nervur service` prints what keeps the ground running across reboots: a
23
27
  systemd unit on Linux, a launchd job on macOS. Nothing is installed; you
@@ -40,7 +44,7 @@ A ground reads its settings from the environment.
40
44
  | `NERVUR_ADDRESSES` | the public addresses written into invitations, by commas |
41
45
  | `NERVUR_ORIGINS` | the page origins the web listener answers, by commas |
42
46
  | `NERVUR_ALLOW_PRIVATE` | `1` to let the ground dial a private or loopback address |
43
- | `NERVUR_KEYCHAIN` | on macOS, a keychain service that keeps the seeds in place of files |
47
+ | `NERVUR_UNLOCK` | on macOS, `keychain:<account>` keeps the key in the keychain in place of `state/key` |
44
48
  | `NERVUR_HAND` | where the hand's socket is, `state/hand` where unset; a relative path is read from where the command runs |
45
49
  | `NERVUR_WAIT` | the longest any ask may run, in milliseconds |
46
50
 
@@ -58,18 +62,22 @@ Every other word goes to the ground's hand, a socket in `state/` that
58
62
  your user alone may open. Run the command in the ground's folder, or
59
63
  name the socket with `--at <socket>` or `NERVUR_HAND`.
60
64
 
61
- `help` prints what the ground holds: each faculty with its methods, and
62
- each house.
65
+ `help` prints what the ground holds: each faculty with its methods, or
66
+ why it is down, and each house.
63
67
 
64
68
  ```bash
65
69
  npx nervur help
66
70
  ```
67
71
 
68
72
  A faculty is called by its name and a method. Named alone, it prints its
69
- methods. The ground's own `houses` faculty adds, removes and lists houses.
73
+ methods. Four faculties are the ground's own. `secrets` keeps a secret
74
+ in the ground's sealed drawer. `faculties` stands a faculty on the maker
75
+ its entry names, and `houses` opens a house on its entry.
70
76
 
71
77
  ```bash
72
- npx nervur houses add name=main memory='{"body":"ledger"}' classes='{"body":"folder","at":"house"}'
78
+ npx nervur faculties add name=recipe make=module args='{"at":"recipe.ts"}'
79
+ npx nervur faculties add name=payments from=recipe make=payments secrets='["stripe-key"]'
80
+ npx nervur houses add name=main classes='{"body":"folder","at":"house"}' faculties='["payments"]'
73
81
  ```
74
82
 
75
83
  `ask` asks a being of a house, as the house's owner. `--id <being>` names
@@ -101,6 +109,13 @@ JSON where it reads as JSON, and as text where it does not, so
101
109
  npx nervur ask main hello '{"name":"Ada"}'
102
110
  ```
103
111
 
112
+ A lone `-` reads the one JSON object from standard input instead. So a
113
+ secret never stands on a command line or in a shell's history.
114
+
115
+ ```bash
116
+ npx nervur secrets set - < stripe-key.json
117
+ ```
118
+
104
119
  Each word prints one JSON value, the answer, and its exit code says what
105
120
  came back.
106
121
 
package/FACES.md CHANGED
@@ -188,25 +188,35 @@ method the tool's name, its args schema the tool's input schema, and its
188
188
  hints the tool's annotations. A moved describe is a changed list of
189
189
  tools.
190
190
 
191
- ## The recipe
191
+ ## The registry
192
192
 
193
- The ground makes the face from its recipe, as it makes any faculty. The
194
- house's entry names `face` among its faculties, and the steward's `arm`
195
- hands the face its signup.
193
+ The ground makes the face from a registry, as it makes any faculty. A
194
+ NodeGround stands the module below with the maker `module`, and the
195
+ face from it by an entry that names `from`. The house's entry names
196
+ `face` among its faculties, and the steward's `arm` hands the face its
197
+ signup.
196
198
 
197
199
  ```ts
198
200
  // recipe.ts
199
201
  import { Api, apiOffer } from './api.ts';
200
202
 
201
- export const faculties = () => ({
202
- face: apiOffer(new Api()),
203
- });
203
+ // A registry: the ground makes the face when an entry names its maker.
204
+ export const faculties = {
205
+ face: () => apiOffer(new Api()),
206
+ };
207
+ ```
208
+
209
+ ```bash
210
+ npx nervur faculties add name=recipe make=module args='{"at":"recipe.ts"}'
211
+ npx nervur faculties add name=face from=recipe make=face
204
212
  ```
205
213
 
206
214
  ## Testing it
207
215
 
208
216
  A BenchGround answers a request with its faculties' handlers in turn, as
209
- a ground's one listener does, so the face is tested with no socket.
217
+ a ground's one listener does, so the face is tested with no socket. The
218
+ test hands the bench the registry, and stands the face through the
219
+ ground's hand, as an owner does.
210
220
 
211
221
  ```ts
212
222
  // api.test.ts
@@ -217,8 +227,9 @@ import * as desk from './desk.ts';
217
227
  import { faculties } from './recipe.ts';
218
228
 
219
229
  test('A person signs up at the face, and reaches their member as an API and as tools', async (t) => {
220
- const ground = await BenchGround.open({ network: new FakeNetwork(), host: 'desk', modules: { desk }, recipe: faculties });
230
+ const ground = await BenchGround.open({ network: new FakeNetwork(), host: 'desk', modules: { desk }, registry: { faculties } });
221
231
  t.after(() => ground.down());
232
+ await ground.hand({ faculty: 'faculties', method: 'add', args: { name: 'face', make: 'face' } });
222
233
  await ground.add('desk', 'desk', { faculties: ['face'] });
223
234
  await ground.ask({ house: 'desk', method: 'arm' });
224
235
  const web = async (path: string, body?: unknown, token?: string) =>