nervur 0.22.2-5 → 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 (116) hide show
  1. package/AUTHORING.md +193 -102
  2. package/COMMAND.md +128 -0
  3. package/FACES.md +275 -0
  4. package/FACULTIES.md +558 -0
  5. package/GROUNDS.md +212 -0
  6. package/KIT-SPEC.md +26 -8
  7. package/README.md +30 -21
  8. package/WORLD.md +195 -0
  9. package/dist/app/app-ground.d.ts +2 -2
  10. package/dist/app/app-ground.js +7 -9
  11. package/dist/app/index.d.ts +2 -2
  12. package/dist/app/index.js +1 -1
  13. package/dist/app/native.d.ts +9 -44
  14. package/dist/app/native.js +7 -95
  15. package/dist/being/being.d.ts +84 -7
  16. package/dist/being/being.js +1 -1
  17. package/dist/being/index.d.ts +2 -1
  18. package/dist/being/index.js +1 -0
  19. package/dist/being/table.d.ts +6 -0
  20. package/dist/being/table.js +72 -16
  21. package/dist/bench/bench-ground.d.ts +35 -17
  22. package/dist/bench/bench-ground.js +52 -28
  23. package/dist/bench/bench.d.ts +43 -29
  24. package/dist/bench/bench.js +240 -132
  25. package/dist/bench/fake-clock.d.ts +1 -1
  26. package/dist/bench/fake-memory.d.ts +3 -1
  27. package/dist/bench/fake-network.d.ts +16 -7
  28. package/dist/bench/fake-network.js +67 -14
  29. package/dist/bench/fake-unlock.d.ts +6 -0
  30. package/dist/bench/fake-unlock.js +10 -0
  31. package/dist/bench/index.d.ts +3 -11
  32. package/dist/bench/index.js +2 -10
  33. package/dist/bench/seeded.d.ts +1 -1
  34. package/dist/bench/seeded.js +1 -1
  35. package/dist/bench/stewards.d.ts +157 -5
  36. package/dist/bench/stewards.js +115 -7
  37. package/dist/bodies/joined-carry.d.ts +7 -1
  38. package/dist/bodies/joined-carry.js +9 -3
  39. package/dist/bodies/kept.d.ts +44 -0
  40. package/dist/bodies/kept.js +91 -0
  41. package/dist/bodies/vouch.d.ts +20 -0
  42. package/dist/bodies/vouch.js +71 -0
  43. package/dist/bodies/web-carry.d.ts +9 -2
  44. package/dist/bodies/web-carry.js +30 -9
  45. package/dist/browser/browser-ground.d.ts +16 -9
  46. package/dist/browser/browser-ground.js +29 -28
  47. package/dist/browser/index.d.ts +3 -3
  48. package/dist/browser/index.js +1 -1
  49. package/dist/browser/{locked-custody.d.ts → locked-unlock.d.ts} +6 -9
  50. package/dist/browser/{locked-custody.js → locked-unlock.js} +29 -37
  51. package/dist/edge/durable-clock.d.ts +19 -0
  52. package/dist/edge/durable-clock.js +61 -0
  53. package/dist/edge/durable-memory.d.ts +5 -0
  54. package/dist/edge/durable-memory.js +11 -0
  55. package/dist/edge/durable.d.ts +18 -0
  56. package/dist/edge/durable.js +24 -0
  57. package/dist/edge/edge-ground.d.ts +49 -0
  58. package/dist/edge/edge-ground.js +199 -0
  59. package/dist/edge/index.d.ts +7 -0
  60. package/dist/edge/index.js +9 -0
  61. package/dist/edge/native-crypto.d.ts +8 -0
  62. package/dist/edge/native-crypto.js +102 -0
  63. package/dist/edge/secret-unlock.d.ts +7 -0
  64. package/dist/edge/secret-unlock.js +13 -0
  65. package/dist/edge/socket-carry.d.ts +39 -0
  66. package/dist/edge/socket-carry.js +111 -0
  67. package/dist/foundation.d.ts +12 -0
  68. package/dist/ground/ground.d.ts +199 -40
  69. package/dist/ground/ground.js +678 -203
  70. package/dist/ground/views.d.ts +59 -0
  71. package/dist/ground/views.js +188 -0
  72. package/dist/house/crossing.d.ts +18 -6
  73. package/dist/house/crossing.js +35 -14
  74. package/dist/house/house.d.ts +26 -28
  75. package/dist/house/house.js +405 -167
  76. package/dist/house/rows-shape.d.ts +8 -1
  77. package/dist/index.d.ts +9 -6
  78. package/dist/index.js +3 -1
  79. package/dist/node/bridge.d.ts +3 -0
  80. package/dist/node/bridge.js +113 -17
  81. package/dist/node/cli.js +43 -21
  82. package/dist/node/hand.d.ts +2 -0
  83. package/dist/node/hand.js +4 -2
  84. package/dist/node/index.d.ts +6 -8
  85. package/dist/node/index.js +1 -3
  86. package/dist/node/node-ground.d.ts +6 -12
  87. package/dist/node/node-ground.js +71 -56
  88. package/dist/node/tcp-carry.d.ts +8 -7
  89. package/dist/node/tcp-carry.js +21 -53
  90. package/dist/node/unlock.d.ts +17 -0
  91. package/dist/node/unlock.js +86 -0
  92. package/dist/node/websocket.d.ts +8 -2
  93. package/dist/node/websocket.js +20 -4
  94. package/dist/quo/frame.d.ts +13 -0
  95. package/dist/quo/frame.js +44 -0
  96. package/dist/quo/index.d.ts +1 -0
  97. package/dist/quo/index.js +5 -0
  98. package/dist/serve/index.d.ts +27 -9
  99. package/dist/serve/index.js +24 -8
  100. package/package.json +12 -2
  101. package/dist/bench/fake-carry.d.ts +0 -32
  102. package/dist/bench/fake-carry.js +0 -56
  103. package/dist/bench/fake-custody.d.ts +0 -9
  104. package/dist/bench/fake-custody.js +0 -10
  105. package/dist/bench/fake-faculty.d.ts +0 -25
  106. package/dist/bench/fake-faculty.js +0 -48
  107. package/dist/bench/fake-keys.d.ts +0 -5
  108. package/dist/bench/fake-keys.js +0 -9
  109. package/dist/node/custody.d.ts +0 -20
  110. package/dist/node/custody.js +0 -34
  111. package/dist/node/file-keys.d.ts +0 -7
  112. package/dist/node/file-keys.js +0 -30
  113. package/dist/node/held-keys.d.ts +0 -14
  114. package/dist/node/held-keys.js +0 -36
  115. package/dist/node/keychain-keys.d.ts +0 -10
  116. package/dist/node/keychain-keys.js +0 -51
package/AUTHORING.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # Writing for nervur
2
2
 
3
3
  This guide teaches the two things you write with `nervur`. A **being**
4
- holds logic and state. A **faculty** reaches the world outside. A
5
- **ground** runs them on a machine, and the library ships it. The
6
- examples build one small shop, and every file here is a file the
7
- package's own tests run.
4
+ holds logic and state. A **faculty** reaches the world outside, and
5
+ [Writing a faculty](FACULTIES.md) teaches it whole. A **ground** runs them on
6
+ a machine, and the library ships it. The examples build one small shop,
7
+ and every file here is a file the package's own tests run.
8
8
 
9
9
  The shop has three classes and one faculty.
10
10
 
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
 
@@ -169,7 +170,7 @@ JavaScript writes nothing.
169
170
  | `args` | the schema of what it takes; omitted, the empty object alone |
170
171
  | `result` | the schema of what it answers; omitted, nothing |
171
172
  | `hints` | `readOnly`, `idempotent`, `destructive` |
172
- | `examples` | cells, a role, args, fakes, and what it gives |
173
+ | `examples` | a history of her asks, a role, args, fakes, and what it gives |
173
174
  | `description` | one line for readers and agents |
174
175
  | `wait` | milliseconds she may run; omitted, thirty seconds |
175
176
 
@@ -202,6 +203,9 @@ nothing lands.
202
203
  | `this.house.cancelAlarm({ key })` | an alarm removed |
203
204
  | `this.<need>.<method>({…}, { reply? })` | an awaited call, or an effect |
204
205
  | `this.held(id, Need).<ask>({…}, { reply?, after? })` | the same, on a standing, and a watch where `after` is given |
206
+ | `this.held(id).describe()`, `.ask(method, {…}, options)` | a standing with no need: what it shows her, then any ask it showed |
207
+ | `this.stranger({ ward, at }, Need).<ask>({…})` | a far house's public being, asked as a stranger, every ask awaited |
208
+ | `this.stranger({ ward, at }).describe()`, `.ask(method, {…})` | the same with no need: what she shows a stranger, then any ask it showed |
205
209
  | `this.standings` | `list()`, `note(id, notes)` and `drop(id)` |
206
210
  | `this.handle(ask, { bind?, notes?, once?, expires? })` | a handle to one of her asks |
207
211
  | `this.invite(id, { notes?, expires? })` | a new occupant, as a handle |
@@ -261,6 +265,12 @@ hold: `{ result: [...] }`. From a being, pass the result she holds:
261
265
  `readOnly` ask is watched, and a watch on anything else is refused where
262
266
  she makes it.
263
267
 
268
+ A watch may land as a reply instead: `{ after: seen, reply: 'heard' }`.
269
+ It leaves once her ask lands, as an effect does, and its answer asks her
270
+ `heard`, where she writes what she heard and watches again. So a device
271
+ that only dials, a phone or a Pi behind a router, hears its station the
272
+ moment something changes there, and opens no port.
273
+
264
274
  A watch moves only on what its asker could read, since it runs as that
265
275
  asker. One asker holds one watch on one ask with the same args, and a
266
276
  second answers the first at once. A watch across a door holds its
@@ -277,7 +287,11 @@ An **occupant** is someone who may ask her. She mints one with
277
287
  A **standing** is someone she may ask. She receives one where an ask's
278
288
  args carry an invitation under `s.handle`, or where her steward
279
289
  introduces one. She asks through it with `this.held(id, Need)`, which
280
- checks the standing's describe covers the need.
290
+ checks the standing's describe covers the need. A standing from an
291
+ invitation is named `standing:` and sixteen hex digits, and the id is
292
+ what her ask receives. One her steward introduced is named by the being
293
+ it reaches. She asks a far standing from the ask after the one that took
294
+ it.
281
295
 
282
296
  Every relation carries two sets of notes. `notes` are hers alone.
283
297
  `steward` are her steward's, written when the steward made the relation,
@@ -365,13 +379,14 @@ export class Shop extends Being.of({
365
379
  | `bear({ kind, id, args })` | places a new being; the same id twice answers the first |
366
380
  | `remove({ id })` | removes a being and everything of hers |
367
381
  | `list()` | every being, her kind, whether she is absent, and her dead letters |
368
- | `ask({ id, method, args }, { reply? })` | asks any being as her occupant `steward` |
382
+ | `ask({ id, method, args }, { reply? })` | asks any being as her occupant `steward`; with no method, reads what she shows the steward |
369
383
  | `introduce({ from, to, notes })` | gives `from` a standing on `to`, and `to` an occupant |
370
384
  | `invite({ id, occupant, notes, expires? })` | gives a being a new occupant, and the steward its handle |
371
385
 
372
386
  `bear`, `remove`, `introduce` and `invite` land in the steward's own
373
387
  write. If her ask fails, none of them happened. A being borne runs her
374
- `born` ask first, where her class declares one.
388
+ `born` ask first, where her class declares one, asked as the occupant
389
+ `steward`, so its entry says `for: 'steward'`.
375
390
 
376
391
  Every other being is placed as `normal`. She holds the standing
377
392
  `steward` and the occupant `steward`, and can drop neither.
@@ -469,10 +484,19 @@ hold.
469
484
  Every need is covered, or she is absent. An absent being answers silence
470
485
  and keeps her cells, and answers again once an offer covers her needs.
471
486
 
487
+ A standing is matched to a need by rules two to four alone. A describe
488
+ names no blueprint, so the need's name is hers to choose there.
489
+
490
+ A need she forgot to declare is a member she does not hold, and the call
491
+ throws inside her ask, which answers only `the ask failed`. So check your
492
+ classes with `npx tsc --noEmit` beside your tests, which run with
493
+ `node --test`.
494
+
472
495
  ### Testing on the bench
473
496
 
474
- The bench opens two houses in one process, on fake memory, keys, clock
475
- and carry. So every ask crosses a door as it would in production.
497
+ The bench opens two houses on one ground in memory, on fake memory,
498
+ keys and clock. A being of the bench's own house asks hers through a
499
+ door, so every ask crosses as it would in production.
476
500
 
477
501
  ```ts
478
502
  // order.test.ts
@@ -495,38 +519,49 @@ test('An order is paid once the provider calls the handle it was given', async (
495
519
  assert.deepEqual(await order.ask('add', { sku: 'tea', price: 4 }), { result: { total: 4 } });
496
520
  assert.deepEqual(await order.ask('checkout'), { result: null });
497
521
  await bench.settle();
498
- assert.equal((await order.cells())!.state, 'paying', 'the charge left once checkout landed, and answered pending');
522
+ assert.equal((await order.describe())?.state, 'paying', 'the charge left once checkout landed, and answered pending');
499
523
 
500
524
  assert.deepEqual(await payments.settle('first'), { result: null });
501
525
  await bench.settle();
502
- assert.equal((await order.cells())!.state, 'paid');
526
+ assert.equal((await order.describe())?.state, 'paid');
503
527
  assert.deepEqual(await order.ask('add', { sku: 'jam', price: 1 }), { error: { message: 'not in this state' } });
504
528
  });
505
529
  ```
506
530
 
507
- `place(Class, { id, cells })` bears a being and starts her from those
508
- cells. A placed being is asked with `ask(method, args, { role })`,
509
- described with `describe({ role })`, and read with `cells()`. `settle()`
510
- lets every effect and reply run, and `advance(ms)` moves the fake clock.
511
-
512
- The bench plays a role with an occupant named for it, whose own notes and
513
- steward notes both hold the role as `true`.
531
+ `place(Class, { id, born })` has the bench's steward bear a being. A
532
+ placed being is asked with `ask(method, args, { role })` and described
533
+ with `describe({ role })`. Test her through her asks first, as every
534
+ asker meets her. `cells()` reads her cells through the hand, as her owner
535
+ inspects them. An effect answers with the reply it brings once it lands.
536
+ `settle()` lets every effect and reply run, and `advance(ms)` moves the
537
+ fake clock.
538
+
539
+ The bench plays a role as an owner could, judged on her cells as they
540
+ stand. `root`, and a role root holds, is the hand. `steward` is the
541
+ bench's steward, and `being` a being it introduces to her. Any other
542
+ role is an occupant the bench's steward invites, with the role `true` in
543
+ its steward notes. A role read from her own notes is hers to grant, and
544
+ a handle only her own ask mints.
514
545
 
515
546
  A steward and a public being are placed where the house places them.
516
547
  `Bench.open({ steward: Shop, public: Lobby, classes: [Order] })` opens the
517
548
  author's house with them there, and `place(Shop)` and `place(Lobby)` find
518
- them. A role `root` holds is played through the hand, on any being. On
519
- the public being, a role a stranger holds is played by a box with no
520
- relation, signed with a key drawn from the bench's seed.
549
+ them. On the public being, a role a stranger holds is played by a being
550
+ of the bench's own house, asking as a stranger. Beside a steward the test
551
+ brings, the bench plays `root` and `stranger` alone, and places no other
552
+ being. A world of your steward and the beings she bears is tested on a
553
+ BenchGround, as [Faces](FACES.md) tests its desk.
521
554
  `Bench.check(Shop, { position: 'steward' })` and `Bench.check(Lobby, {
522
- position: 'public', steward: Shop })` check them.
555
+ position: 'public' })` check them.
523
556
 
524
- An entry's `examples` are tests the bench runs. Each is one ask on a
525
- fresh bench: `cells` start her, `role` asks, `args` are the ask's,
526
- `fakes` answer her needs, and `gives` is the answer owed. `Bench.check`
527
- runs every example twice from one seed and flags a class that answers
528
- differently. It then describes every state to every role, and names every
529
- finding that failed.
557
+ An entry's `examples` are tests the bench runs. Each is a history, then
558
+ one ask, on a fresh bench. `given` lists the asks of hers that bring her
559
+ from her `born` to where the example starts. `role` asks, `args` are the
560
+ ask's, `fakes` answer her needs, and `gives` is the answer owed.
561
+ `Bench.check` runs every example twice from one seed and flags a class
562
+ that answers differently, or leaves her cells differently. It then
563
+ describes every state to every role it plays, and names every finding
564
+ that failed.
530
565
 
531
566
  ## A faculty
532
567
 
@@ -553,7 +588,8 @@ type Answer = { result: { pending: boolean } } | { error: { message: string } };
553
588
  /**
554
589
  * A payment provider in the ground's process. It answers a call id it has
555
590
  * seen with the answer it gave, so an effect sent twice charges once. A
556
- * 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.
557
593
  */
558
594
  export class Payments {
559
595
  readonly #answered = new Map<string, Answer>();
@@ -581,55 +617,31 @@ export class Payments {
581
617
  export const paymentsOffer = (payments: Payments): Offer => ({ blueprint: PaymentsBlueprint, object: payments, window: 7 * 86_400_000 });
582
618
  ```
583
619
 
584
- An offer is `{ blueprint, object, kinds?, window?, handler?, stop? }`.
585
-
586
- - **The object has the blueprint's methods.** Each takes one args object
587
- and a context, and answers `{ result }` or `{ error: { message } }`. A
588
- throw is a failure to answer, and the house tries again.
589
- - **The context holds the call id.** An effect arrives again with the same
590
- call id when an answer was lost. A faculty that changes the world
591
- answers a call id it has seen with the answer it gave.
592
- - **A handle arrives as a token.** The faculty calls it with
593
- `context.call({ token, args, id })`. Its own `id` makes that call run
594
- once, however often it is sent.
595
- - **`kinds` is the ground's grant.** It lists the classes that may hold
596
- the offer. Without it, every class whose need it covers holds it.
597
- - **`window` is how long the faculty remembers a call id.** It is seven
598
- days where omitted, and the house gives up on an effect at it.
599
- - **`handler` answers HTTP on the ground's one listener.** It takes a
600
- `Request` and answers a `Response`, or `null` where the request is not
601
- its own. A site, an API or an MCP server is a faculty with a handler.
602
- - **`stop` is called when the ground stops**, faculties in the reverse of
603
- the order they were made.
604
-
605
- The object arrives living. The ground makes and starts the faculty, and
606
- the house never starts, stops or restarts it. Every being whose need it
607
- covers holds the same object.
608
-
609
- A faculty is the ground's, never a house's. The ground's recipe makes
610
- each one by name, from the settings the ground hands it. `env` is the
611
- ground's environment, so a secret stays on its machine and out of the
612
- code. `dir(name)` answers a folder of the faculty's own.
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.
613
627
 
614
628
  ```ts
615
629
  // recipe.ts
616
630
  import { Payments, paymentsOffer } from './payments.ts';
617
631
 
618
- export const faculties = () => ({
619
- payments: paymentsOffer(new Payments()),
620
- });
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
+ };
621
636
  ```
622
637
 
623
- Policy over a faculty is written as a being. Limits, approvals and
624
- quotas are not the faculty's. One being holds the raw faculty through
625
- `kinds`, and every other being reaches her through a standing.
626
-
627
638
  ## A ground
628
639
 
629
640
  A ground is the process houses run in, and you write none. `nervur up`
630
- runs one on a folder: the recipe of its faculties, and a folder of code
631
- for each house. It keeps a record of its houses, and opens each on the
632
- 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.
633
645
 
634
646
  ### The shop's folder
635
647
 
@@ -641,6 +653,13 @@ nervur-ground/
641
653
  state/ made by the ground, its owner's alone
642
654
  ```
643
655
 
656
+ The folder is a package of ECMAScript modules, so Node reads its
657
+ TypeScript as it is written, with no build step.
658
+
659
+ ```bash
660
+ npm init -y && npm pkg set type=module && npm install nervur
661
+ ```
662
+
644
663
  A house's folder names what the house holds.
645
664
 
646
665
  ```ts
@@ -662,39 +681,65 @@ The ground boots in one order, and a stop is that order reversed, on an
662
681
  interrupt and on `SIGTERM`.
663
682
 
664
683
  1. **Lock.** One ground to its state.
665
- 2. **Its own.** Its seeds and its record open.
666
- 3. **Faculties.** The recipe makes each, in its order.
667
- 4. **Houses.** Each house of the record opens on its bodies.
668
- 5. **Hook.** The listeners, then the hand on its socket.
669
- 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.
670
692
 
671
693
  It is set by its environment.
672
694
 
673
695
  | Setting | What it sets |
674
696
  | --- | --- |
675
- | `NERVUR_TCP_PORT` | its TCP port, 7300 where unset |
697
+ | `NERVUR_TCP_PORT` | its TCP port, 9110 where unset |
676
698
  | `NERVUR_HTTP_PORT` | its HTTP port, for Quo over the web and every handler; no HTTP where unset |
677
- | `NERVUR_ADDRESSES` | the public addresses it writes into invitations, by commas |
699
+ | `NERVUR_ADDRESSES` | the public addresses it writes into invitations, by commas: `tcp`, `https`, `http`, `wss` or `ws` |
700
+ | `NERVUR_ORIGINS` | the pages of other origins it answers on the web, by commas; a page of its own host needs none |
701
+ | `NERVUR_ALLOW_PRIVATE` | `1` to dial private and loopback addresses, as two grounds on one machine do |
678
702
  | `NERVUR_BIND` | the address it listens on, `0.0.0.0` where unset |
679
703
  | `NERVUR_STATE` | its state, `state/` in its folder where unset |
680
- | `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` |
681
705
  | `NERVUR_WAIT` | its bound on every ask, in milliseconds |
682
706
 
683
- The state holds each house's seed in a file its owner alone reads, and
684
- each house's ledger. A ledger appends every write and never rewrites
685
- one, and one behind its witness is refused, so a restored backup cannot
686
- replay what a house already answered. 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
+ ```
687
726
 
688
727
  A house is added once, and the ground opens it again at every start.
689
728
 
690
729
  ```bash
691
- 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"]'
692
731
  ```
693
732
 
694
- `memory` and `classes` name the bodies the house is handed. `ledger` and
695
- `folder` are the ground's own, and a recipe may add more, a git registry
696
- or another store among them. `faculties` names what the house receives,
697
- 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`.
698
743
 
699
744
  ### The hand
700
745
 
@@ -714,17 +759,23 @@ npx nervur houses list
714
759
  ```
715
760
 
716
761
  `ask` asks a being in a house as `root`: the steward, or the being
717
- `--id` names. The owner lands a paper in a being with one ask, and her
718
- `accept` names `root` in its `for`, or names no `for`.
762
+ `--id` names. The owner opens an order through the steward, and hands a
763
+ courier's paper to it the same way. The steward's `hire` carries the
764
+ paper unopened, and the order takes it.
719
765
 
720
766
  ```bash
721
767
  npx nervur ask shop open id=first
722
768
  ```
723
769
 
724
770
  ```bash
725
- npx nervur ask shop --id alice accept invitation=7b22…
771
+ npx nervur ask shop hire order=first courier=7b22…
726
772
  ```
727
773
 
774
+ `--id` names the being asked in the steward's place, as `npx nervur ask
775
+ shop --id first` shows the order's describe. `--cells` reads her cells
776
+ and asks nothing, as `npx nervur ask shop --id first --cells`. No one
777
+ but the owner reads them, and only her own asks write them.
778
+
728
779
  Each prints one JSON value. It exits 0 on a result and 1 on an error
729
780
  answered. It exits 2 where nothing was asked, so a script tells a
730
781
  refusal from a ground that is down. The hand is `state/hand` in the
@@ -750,7 +801,7 @@ with `loginctl enable-linger`. On macOS, the agent goes to
750
801
 
751
802
  ### In a page
752
803
 
753
- `BrowserGround.open({ recipe })` from `nervur/browser` runs the same
804
+ `BrowserGround.open({ registry })` from `nervur/browser` runs the same
754
805
  houses in a page. Every tab and the service worker of one origin share
755
806
  one ground: the one holding the Web Lock runs it, and the others reach
756
807
  its hand over a `BroadcastChannel`. When it closes, the next opens the
@@ -758,12 +809,12 @@ ground from the same storage. `hand` answers the same three requests
758
809
  the command sends: `describe`, a faculty's method, and an ask of a being
759
810
  in a named house.
760
811
 
761
- Its bodies are the browser's. Seeds are sealed under an AES key that
762
- IndexedDB holds unextractable, and memory is IndexedDB, named
763
- `indexeddb` in an entry. Classes load from the page's own origin through
764
- the `origin` body, `{ body: 'origin', at: '/house/index.js' }`, and a
765
- path off the origin is refused. The recipe is the object you pass: its
766
- 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.
767
818
 
768
819
  Any script on the origin can use the ground's keys, so the ground is
769
820
  whoever serves the origin's script. Give it an origin of its own, serve
@@ -787,10 +838,10 @@ app's web view, on two interfaces the shell fills in native code:
787
838
  which lands every write only where each key in `expect` still holds
788
839
  what it names, and answers whether it landed.
789
840
 
790
- The library holds the rest. Each house's seed goes into the secret
791
- store, and each memory into the native store, which the system never
792
- evicts as it may a web view's storage. A push token reaches the ground
793
- 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.
794
845
 
795
846
  ## What the house guarantees
796
847
 
@@ -812,3 +863,43 @@ through a faculty your recipe makes.
812
863
  leaves her absent, and the refusal says why.
813
864
 
814
865
  A failed ask changed nothing she owns, so asking again is safe.
866
+
867
+ ## What the house refuses
868
+
869
+ Each refusal answers why, where it is met: at her class's first resolve,
870
+ at her call, or where an ask arrives.
871
+
872
+ - A being reaching anything her position, her needs and `this.house` do
873
+ not give.
874
+ - A ground's own references handed to a being, or offered to her as a
875
+ faculty.
876
+ - A handle or an invitation in her cells.
877
+ - Her cells read by anyone but the owner's hand.
878
+ - A standing she mints herself. Standings arrive from the house.
879
+ - A far standing asked in the ask that took it. She asks it from her next
880
+ ask.
881
+ - A write by anyone else to her notes, and by her to her steward's.
882
+ - An occupant id the house reserves, and one she already holds.
883
+ - Two member names that clash.
884
+ - An ask with no entry.
885
+ - A state no ask reaches, an ask no role reaches, and a role unused.
886
+ - A method landing in a state its `to` does not name.
887
+ - A `readOnly` ask that writes.
888
+ - A need and an offer that disagree on `idempotent`.
889
+ - A schema keyword outside the subset `s` writes.
890
+ - Two offers covering one need for one kind, and a kind two sources
891
+ claim.
892
+ - An effect sent before its ask landed, or sent again while a call for
893
+ it is pending.
894
+ - An ask a stranger reaches that is not idempotent.
895
+ - An invite from a public being.
896
+ - A seed inside the house, and a seed that is not sixty-four hex digits.
897
+ - A memory opened with keys that derive another bound.
898
+ - A send to a private address, unless its ground allows it.
899
+ - A call on the house beside `House.open`, `door` and `ask`.
900
+ - A faculty in a house's code. Faculties are the ground's.
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.
905
+ - A ground an owner must write. Each terrain's ships.
package/COMMAND.md ADDED
@@ -0,0 +1,128 @@
1
+ # The command
2
+
3
+ `nervur` is the command the package installs. It holds no business of
4
+ its own. Two words start things on your machine, and every other word is
5
+ read from what the running ground says it holds. This guide assumes you
6
+ have read [the package's start](README.md), which runs a first ground.
7
+
8
+ ## Starting a ground
9
+
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.
15
+
16
+ ```bash
17
+ npx nervur up .
18
+ ```
19
+
20
+ The ground stops cleanly on an interrupt and on `SIGTERM`. It takes a
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.
25
+
26
+ `nervur service` prints what keeps the ground running across reboots: a
27
+ systemd unit on Linux, a launchd job on macOS. Nothing is installed; you
28
+ place what it prints.
29
+
30
+ ```bash
31
+ npx nervur service /srv/shop
32
+ ```
33
+
34
+ ## Settings
35
+
36
+ A ground reads its settings from the environment.
37
+
38
+ | Setting | What it sets |
39
+ | --- | --- |
40
+ | `NERVUR_STATE` | the state folder, `state/` in the ground's folder where unset |
41
+ | `NERVUR_TCP_PORT` | the TCP port Quo listens on, 9110 where unset |
42
+ | `NERVUR_HTTP_PORT` | the port of the web listener, which faces and Quo over the web share; none where unset |
43
+ | `NERVUR_BIND` | the address both listen on, every interface where unset |
44
+ | `NERVUR_ADDRESSES` | the public addresses written into invitations, by commas |
45
+ | `NERVUR_ORIGINS` | the page origins the web listener answers, by commas |
46
+ | `NERVUR_ALLOW_PRIVATE` | `1` to let the ground dial a private or loopback address |
47
+ | `NERVUR_UNLOCK` | on macOS, `keychain:<account>` keeps the key in the keychain in place of `state/key` |
48
+ | `NERVUR_HAND` | where the hand's socket is, `state/hand` where unset; a relative path is read from where the command runs |
49
+ | `NERVUR_WAIT` | the longest any ask may run, in milliseconds |
50
+
51
+ A ground that people reach names its public addresses. Without them, it
52
+ writes only the addresses it listens on, which a stranger cannot reach.
53
+
54
+ A socket's path fits 103 bytes on macOS and 107 on Linux, and a longer
55
+ one refuses to start by name. A ground in a deep folder names a shorter
56
+ path in `NERVUR_HAND`, such as `state/hand` run from the folder, and every
57
+ command that reaches it names the same.
58
+
59
+ ## Asking the ground
60
+
61
+ Every other word goes to the ground's hand, a socket in `state/` that
62
+ your user alone may open. Run the command in the ground's folder, or
63
+ name the socket with `--at <socket>` or `NERVUR_HAND`.
64
+
65
+ `help` prints what the ground holds: each faculty with its methods, or
66
+ why it is down, and each house.
67
+
68
+ ```bash
69
+ npx nervur help
70
+ ```
71
+
72
+ A faculty is called by its name and a method. Named alone, it prints its
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.
76
+
77
+ ```bash
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"]'
81
+ ```
82
+
83
+ `ask` asks a being of a house, as the house's owner. `--id <being>` names
84
+ her, and with none it asks the steward. With no method, it prints what
85
+ she shows.
86
+
87
+ ```bash
88
+ npx nervur ask main
89
+ ```
90
+
91
+ `--cells` reads a being's cells as they last landed, and asks nothing.
92
+ Nothing but the hand reads cells, which makes it the tool for a test, a
93
+ repair, or a look at what her asks do not show.
94
+
95
+ ```bash
96
+ npx nervur ask main --cells
97
+ ```
98
+
99
+ The command asks no watch. A watch is held by a being on her standing,
100
+ or by a page through a face, where something waits on its answer.
101
+
102
+ ## Arguments and answers
103
+
104
+ Arguments are one JSON object, or words `key=value`. A value is read as
105
+ JSON where it reads as JSON, and as text where it does not, so
106
+ `count=3` is a number and `name=Ada` is text.
107
+
108
+ ```bash
109
+ npx nervur ask main hello '{"name":"Ada"}'
110
+ ```
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
+
119
+ Each word prints one JSON value, the answer, and its exit code says what
120
+ came back.
121
+
122
+ | Exit | What it means |
123
+ | --- | --- |
124
+ | 0 | a result, or what she shows |
125
+ | 1 | an error the ground or the being answered |
126
+ | 2 | nothing was asked: a word the command does not know, or a hand that does not answer |
127
+
128
+ So a script tells a refusal from a ground that is down.