nervur 0.22.1 → 0.22.2-3

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 (396) hide show
  1. package/AUTHORING.md +741 -0
  2. package/KIT-SPEC.md +286 -0
  3. package/README.md +97 -436
  4. package/dist/app/app-ground.d.ts +18 -0
  5. package/dist/app/app-ground.js +25 -0
  6. package/dist/app/index.d.ts +2 -0
  7. package/dist/app/index.js +5 -0
  8. package/dist/app/native.d.ts +46 -0
  9. package/dist/app/native.js +101 -0
  10. package/dist/being/being.d.ts +229 -0
  11. package/dist/being/being.js +20 -0
  12. package/dist/being/covers.d.ts +9 -0
  13. package/dist/being/covers.js +21 -0
  14. package/dist/being/index.d.ts +3 -0
  15. package/dist/being/index.js +5 -0
  16. package/dist/being/need.d.ts +49 -0
  17. package/dist/being/need.js +33 -0
  18. package/dist/being/schema.d.ts +100 -0
  19. package/dist/being/schema.js +132 -0
  20. package/dist/being/table.d.ts +42 -0
  21. package/dist/being/table.js +288 -0
  22. package/dist/bench/bench-ground.d.ts +55 -0
  23. package/dist/bench/bench-ground.js +101 -0
  24. package/dist/bench/bench.d.ts +87 -0
  25. package/dist/bench/bench.js +260 -0
  26. package/dist/bench/fake-carry.d.ts +32 -0
  27. package/dist/bench/fake-carry.js +56 -0
  28. package/dist/bench/fake-clock.d.ts +17 -0
  29. package/dist/bench/fake-clock.js +34 -0
  30. package/dist/bench/fake-custody.d.ts +9 -0
  31. package/dist/bench/fake-custody.js +10 -0
  32. package/dist/bench/fake-faculty.d.ts +25 -0
  33. package/dist/bench/fake-faculty.js +48 -0
  34. package/dist/bench/fake-keys.d.ts +5 -0
  35. package/dist/bench/fake-keys.js +9 -0
  36. package/dist/bench/fake-memory.d.ts +14 -0
  37. package/dist/bench/fake-memory.js +46 -0
  38. package/dist/bench/fake-network.d.ts +57 -0
  39. package/dist/bench/fake-network.js +169 -0
  40. package/dist/bench/index.d.ts +11 -0
  41. package/dist/bench/index.js +13 -0
  42. package/dist/bench/seeded.d.ts +11 -0
  43. package/dist/bench/seeded.js +37 -0
  44. package/dist/bench/settle.d.ts +2 -0
  45. package/dist/bench/settle.js +49 -0
  46. package/dist/bench/stewards.d.ts +17 -0
  47. package/dist/bench/stewards.js +20 -0
  48. package/dist/bodies/class-list.d.ts +14 -0
  49. package/dist/bodies/class-list.js +33 -0
  50. package/dist/bodies/joined-carry.d.ts +23 -0
  51. package/dist/bodies/joined-carry.js +42 -0
  52. package/dist/bodies/noble-crypto.d.ts +22 -0
  53. package/dist/bodies/noble-crypto.js +88 -0
  54. package/dist/bodies/seed-keys.d.ts +10 -0
  55. package/dist/bodies/seed-keys.js +42 -0
  56. package/dist/bodies/strict-tools.d.ts +10 -0
  57. package/dist/bodies/strict-tools.js +306 -0
  58. package/dist/bodies/web-carry.d.ts +65 -0
  59. package/dist/bodies/web-carry.js +224 -0
  60. package/dist/bodies/web-clock.d.ts +12 -0
  61. package/dist/bodies/web-clock.js +33 -0
  62. package/dist/browser/browser-ground.d.ts +69 -0
  63. package/dist/browser/browser-ground.js +219 -0
  64. package/dist/browser/index.d.ts +4 -0
  65. package/dist/browser/index.js +7 -0
  66. package/dist/browser/indexeddb-memory.d.ts +17 -0
  67. package/dist/browser/indexeddb-memory.js +97 -0
  68. package/dist/browser/locked-custody.d.ts +25 -0
  69. package/dist/browser/locked-custody.js +89 -0
  70. package/dist/browser/origin-classes.d.ts +7 -0
  71. package/dist/browser/origin-classes.js +31 -0
  72. package/dist/foundation.d.ts +150 -0
  73. package/dist/foundation.js +4 -0
  74. package/dist/ground/ground.d.ts +177 -0
  75. package/dist/ground/ground.js +363 -0
  76. package/dist/house/crossing.d.ts +37 -0
  77. package/dist/house/crossing.js +131 -0
  78. package/dist/house/house.d.ts +80 -0
  79. package/dist/house/house.js +1753 -0
  80. package/dist/house/rows-shape.d.ts +103 -0
  81. package/dist/house/rows-shape.js +12 -0
  82. package/dist/house/rows.d.ts +27 -0
  83. package/dist/house/rows.js +168 -0
  84. package/dist/index.d.ts +29 -1
  85. package/dist/index.js +25 -14
  86. package/dist/node/bridge.d.ts +17 -0
  87. package/dist/node/bridge.js +188 -0
  88. package/dist/node/cli.js +170 -0
  89. package/dist/node/custody.d.ts +20 -0
  90. package/dist/node/custody.js +34 -0
  91. package/dist/node/file-keys.d.ts +7 -0
  92. package/dist/node/file-keys.js +30 -0
  93. package/dist/node/file-memory.d.ts +13 -0
  94. package/dist/node/file-memory.js +91 -0
  95. package/dist/node/folder-classes.d.ts +5 -0
  96. package/dist/node/folder-classes.js +40 -0
  97. package/dist/node/gone.d.ts +2 -0
  98. package/dist/node/gone.js +7 -0
  99. package/dist/node/hand.d.ts +29 -0
  100. package/dist/node/hand.js +85 -0
  101. package/dist/node/held-keys.d.ts +14 -0
  102. package/dist/node/held-keys.js +36 -0
  103. package/dist/node/http.d.ts +11 -0
  104. package/dist/node/http.js +90 -0
  105. package/dist/node/index.d.ts +13 -0
  106. package/dist/node/index.js +15 -0
  107. package/dist/node/keychain-keys.d.ts +10 -0
  108. package/dist/node/keychain-keys.js +51 -0
  109. package/dist/node/ledger-memory.d.ts +17 -0
  110. package/dist/node/ledger-memory.js +151 -0
  111. package/dist/node/lock.d.ts +10 -0
  112. package/dist/node/lock.js +78 -0
  113. package/dist/node/node-ground.d.ts +33 -0
  114. package/dist/node/node-ground.js +147 -0
  115. package/dist/node/notify.d.ts +1 -0
  116. package/dist/node/notify.js +18 -0
  117. package/dist/node/tcp-carry.d.ts +39 -0
  118. package/dist/node/tcp-carry.js +216 -0
  119. package/dist/node/websocket.d.ts +6 -0
  120. package/dist/node/websocket.js +118 -0
  121. package/dist/quo/frame.d.ts +20 -0
  122. package/dist/quo/frame.js +48 -0
  123. package/dist/quo/room.d.ts +154 -0
  124. package/dist/quo/room.js +362 -0
  125. package/dist/serve/index.d.ts +27 -0
  126. package/dist/serve/index.js +55 -0
  127. package/package.json +40 -56
  128. package/dist/browser.d.ts +0 -1
  129. package/dist/browser.js +0 -8
  130. package/dist/cli.js +0 -16
  131. package/dist/core/being/being.d.ts +0 -17
  132. package/dist/core/being/being.js +0 -72
  133. package/dist/core/being/digest.d.ts +0 -3
  134. package/dist/core/being/digest.js +0 -17
  135. package/dist/core/being/faculty.d.ts +0 -10
  136. package/dist/core/being/faculty.js +0 -77
  137. package/dist/core/being/index.d.ts +0 -6
  138. package/dist/core/being/index.js +0 -8
  139. package/dist/core/being/kind.d.ts +0 -6
  140. package/dist/core/being/kind.js +0 -34
  141. package/dist/core/being/types.d.ts +0 -61
  142. package/dist/core/being/types.js +0 -4
  143. package/dist/core/being/words.d.ts +0 -13
  144. package/dist/core/being/words.js +0 -14
  145. package/dist/core/browser/index.d.ts +0 -37
  146. package/dist/core/browser/index.js +0 -196
  147. package/dist/core/browser/worker.d.ts +0 -11
  148. package/dist/core/browser/worker.js +0 -37
  149. package/dist/core/cli/command.d.ts +0 -13
  150. package/dist/core/cli/command.js +0 -330
  151. package/dist/core/cli/edge.d.ts +0 -7
  152. package/dist/core/cli/edge.js +0 -79
  153. package/dist/core/cli/harbor.d.ts +0 -25
  154. package/dist/core/cli/harbor.js +0 -115
  155. package/dist/core/contract/index.d.ts +0 -42
  156. package/dist/core/contract/index.js +0 -34
  157. package/dist/core/contract/link.d.ts +0 -4
  158. package/dist/core/contract/link.js +0 -45
  159. package/dist/core/crypto/aes.d.ts +0 -3
  160. package/dist/core/crypto/aes.js +0 -24
  161. package/dist/core/crypto/bytes.d.ts +0 -6
  162. package/dist/core/crypto/bytes.js +0 -33
  163. package/dist/core/crypto/ed25519.d.ts +0 -3
  164. package/dist/core/crypto/ed25519.js +0 -93
  165. package/dist/core/crypto/hash.d.ts +0 -2
  166. package/dist/core/crypto/hash.js +0 -11
  167. package/dist/core/crypto/index.d.ts +0 -7
  168. package/dist/core/crypto/index.js +0 -10
  169. package/dist/core/crypto/json.d.ts +0 -8
  170. package/dist/core/crypto/json.js +0 -250
  171. package/dist/core/crypto/mlkem.d.ts +0 -12
  172. package/dist/core/crypto/mlkem.js +0 -36
  173. package/dist/core/crypto/subtle.d.ts +0 -3
  174. package/dist/core/crypto/subtle.js +0 -11
  175. package/dist/core/crypto/x25519.d.ts +0 -2
  176. package/dist/core/crypto/x25519.js +0 -24
  177. package/dist/core/edge/index.d.ts +0 -164
  178. package/dist/core/edge/index.js +0 -739
  179. package/dist/core/edge/kit.d.ts +0 -2
  180. package/dist/core/edge/kit.js +0 -2
  181. package/dist/core/folder/index.d.ts +0 -18
  182. package/dist/core/folder/index.js +0 -130
  183. package/dist/core/git/http.d.ts +0 -6
  184. package/dist/core/git/http.js +0 -102
  185. package/dist/core/git/index.d.ts +0 -5
  186. package/dist/core/git/index.js +0 -10
  187. package/dist/core/git/object.d.ts +0 -40
  188. package/dist/core/git/object.js +0 -103
  189. package/dist/core/git/pack.d.ts +0 -9
  190. package/dist/core/git/pack.js +0 -171
  191. package/dist/core/git/store.d.ts +0 -23
  192. package/dist/core/git/store.js +0 -120
  193. package/dist/core/git/zlib.d.ts +0 -6
  194. package/dist/core/git/zlib.js +0 -210
  195. package/dist/core/harbor/carrying.d.ts +0 -50
  196. package/dist/core/harbor/carrying.js +0 -66
  197. package/dist/core/harbor/catalogue.d.ts +0 -33
  198. package/dist/core/harbor/catalogue.js +0 -365
  199. package/dist/core/harbor/dna.d.ts +0 -13
  200. package/dist/core/harbor/dna.js +0 -120
  201. package/dist/core/harbor/dock.d.ts +0 -62
  202. package/dist/core/harbor/dock.js +0 -381
  203. package/dist/core/harbor/harbor.d.ts +0 -16
  204. package/dist/core/harbor/harbor.js +0 -448
  205. package/dist/core/harbor/index.d.ts +0 -11
  206. package/dist/core/harbor/index.js +0 -14
  207. package/dist/core/harbor/memory.d.ts +0 -10
  208. package/dist/core/harbor/memory.js +0 -25
  209. package/dist/core/harbor/package.d.ts +0 -20
  210. package/dist/core/harbor/package.js +0 -172
  211. package/dist/core/harbor/probe.d.ts +0 -22
  212. package/dist/core/harbor/probe.js +0 -15
  213. package/dist/core/harbor/registry.d.ts +0 -15
  214. package/dist/core/harbor/registry.js +0 -110
  215. package/dist/core/harbor/root-line.d.ts +0 -7
  216. package/dist/core/harbor/root-line.js +0 -9
  217. package/dist/core/harbor/terrain.d.ts +0 -25
  218. package/dist/core/harbor/terrain.js +0 -28
  219. package/dist/core/http/index.d.ts +0 -2
  220. package/dist/core/http/index.js +0 -154
  221. package/dist/core/http/websocket.d.ts +0 -29
  222. package/dist/core/http/websocket.js +0 -133
  223. package/dist/core/line/answer.d.ts +0 -8
  224. package/dist/core/line/answer.js +0 -60
  225. package/dist/core/line/frame.d.ts +0 -29
  226. package/dist/core/line/frame.js +0 -77
  227. package/dist/core/line/ground.d.ts +0 -3
  228. package/dist/core/line/ground.js +0 -7
  229. package/dist/core/line/index.d.ts +0 -4
  230. package/dist/core/line/index.js +0 -8
  231. package/dist/core/line/web.d.ts +0 -12
  232. package/dist/core/line/web.js +0 -117
  233. package/dist/core/node/index.d.ts +0 -23
  234. package/dist/core/node/index.js +0 -124
  235. package/dist/core/pointer/bodies.d.ts +0 -31
  236. package/dist/core/pointer/bodies.js +0 -118
  237. package/dist/core/pointer/index.d.ts +0 -2
  238. package/dist/core/pointer/index.js +0 -4
  239. package/dist/core/pointer/world.d.ts +0 -45
  240. package/dist/core/pointer/world.js +0 -106
  241. package/dist/core/proof/contracts.d.ts +0 -19
  242. package/dist/core/proof/contracts.js +0 -116
  243. package/dist/core/proof/expect.d.ts +0 -6
  244. package/dist/core/proof/expect.js +0 -18
  245. package/dist/core/proof/index.d.ts +0 -3
  246. package/dist/core/proof/index.js +0 -14
  247. package/dist/core/proof/law.d.ts +0 -25
  248. package/dist/core/proof/law.js +0 -715
  249. package/dist/core/quo/address.d.ts +0 -19
  250. package/dist/core/quo/address.js +0 -84
  251. package/dist/core/quo/door.d.ts +0 -34
  252. package/dist/core/quo/door.js +0 -174
  253. package/dist/core/quo/index.d.ts +0 -9
  254. package/dist/core/quo/index.js +0 -12
  255. package/dist/core/quo/invitation.d.ts +0 -8
  256. package/dist/core/quo/invitation.js +0 -19
  257. package/dist/core/quo/keys.d.ts +0 -40
  258. package/dist/core/quo/keys.js +0 -79
  259. package/dist/core/quo/payload.d.ts +0 -15
  260. package/dist/core/quo/payload.js +0 -55
  261. package/dist/core/quo/relations.d.ts +0 -40
  262. package/dist/core/quo/relations.js +0 -33
  263. package/dist/core/quo/reply.d.ts +0 -13
  264. package/dist/core/quo/reply.js +0 -35
  265. package/dist/core/quo/seal.d.ts +0 -42
  266. package/dist/core/quo/seal.js +0 -78
  267. package/dist/core/quo/standing.d.ts +0 -39
  268. package/dist/core/quo/standing.js +0 -90
  269. package/dist/core/tcp/index.d.ts +0 -27
  270. package/dist/core/tcp/index.js +0 -209
  271. package/dist/core/ward/allowance.d.ts +0 -14
  272. package/dist/core/ward/allowance.js +0 -32
  273. package/dist/core/ward/cells.d.ts +0 -7
  274. package/dist/core/ward/cells.js +0 -74
  275. package/dist/core/ward/house.d.ts +0 -70
  276. package/dist/core/ward/house.js +0 -499
  277. package/dist/core/ward/index.d.ts +0 -5
  278. package/dist/core/ward/index.js +0 -7
  279. package/dist/core/ward/partition.d.ts +0 -51
  280. package/dist/core/ward/partition.js +0 -66
  281. package/dist/core/ward/stance.d.ts +0 -31
  282. package/dist/core/ward/stance.js +0 -193
  283. package/dist/core/ward/ward.d.ts +0 -44
  284. package/dist/core/ward/ward.js +0 -140
  285. package/dist/core.d.ts +0 -10
  286. package/dist/core.js +0 -19
  287. package/dist/defaults/dock/index.d.ts +0 -4
  288. package/dist/defaults/dock/index.js +0 -11
  289. package/dist/defaults/index.d.ts +0 -5
  290. package/dist/defaults/index.js +0 -10
  291. package/dist/defaults/memory/index.d.ts +0 -12
  292. package/dist/defaults/memory/index.js +0 -39
  293. package/dist/defaults/tcp/index.d.ts +0 -16
  294. package/dist/defaults/tcp/index.js +0 -82
  295. package/dist/defaults/ward/index.d.ts +0 -4
  296. package/dist/defaults/ward/index.js +0 -11
  297. package/dist/defaults/web/index.d.ts +0 -15
  298. package/dist/defaults/web/index.js +0 -90
  299. package/dist/edge.d.ts +0 -1
  300. package/dist/edge.js +0 -9
  301. package/dist/kit.d.ts +0 -4
  302. package/dist/kit.js +0 -12
  303. package/dist/node.d.ts +0 -1
  304. package/dist/node.js +0 -8
  305. package/src/browser.ts +0 -10
  306. package/src/cli.ts +0 -15
  307. package/src/core/being/being.ts +0 -84
  308. package/src/core/being/digest.ts +0 -18
  309. package/src/core/being/faculty.ts +0 -76
  310. package/src/core/being/index.ts +0 -8
  311. package/src/core/being/kind.ts +0 -37
  312. package/src/core/being/types.ts +0 -74
  313. package/src/core/being/words.ts +0 -31
  314. package/src/core/browser/index.ts +0 -219
  315. package/src/core/browser/worker.ts +0 -63
  316. package/src/core/cli/command.ts +0 -311
  317. package/src/core/cli/edge.ts +0 -86
  318. package/src/core/cli/harbor.ts +0 -120
  319. package/src/core/contract/index.ts +0 -82
  320. package/src/core/contract/link.ts +0 -58
  321. package/src/core/crypto/aes.ts +0 -26
  322. package/src/core/crypto/bytes.ts +0 -37
  323. package/src/core/crypto/ed25519.ts +0 -92
  324. package/src/core/crypto/hash.ts +0 -14
  325. package/src/core/crypto/index.ts +0 -10
  326. package/src/core/crypto/json.ts +0 -241
  327. package/src/core/crypto/mlkem.ts +0 -38
  328. package/src/core/crypto/subtle.ts +0 -13
  329. package/src/core/crypto/x25519.ts +0 -23
  330. package/src/core/edge/index.ts +0 -865
  331. package/src/core/folder/index.ts +0 -137
  332. package/src/core/git/http.ts +0 -103
  333. package/src/core/git/index.ts +0 -10
  334. package/src/core/git/object.ts +0 -116
  335. package/src/core/git/pack.ts +0 -147
  336. package/src/core/git/store.ts +0 -122
  337. package/src/core/git/zlib.ts +0 -197
  338. package/src/core/harbor/carrying.ts +0 -121
  339. package/src/core/harbor/catalogue.ts +0 -386
  340. package/src/core/harbor/dna.ts +0 -116
  341. package/src/core/harbor/dock.ts +0 -420
  342. package/src/core/harbor/harbor.ts +0 -441
  343. package/src/core/harbor/index.ts +0 -14
  344. package/src/core/harbor/memory.ts +0 -31
  345. package/src/core/harbor/package.ts +0 -195
  346. package/src/core/harbor/probe.ts +0 -39
  347. package/src/core/harbor/registry.ts +0 -107
  348. package/src/core/harbor/root-line.ts +0 -19
  349. package/src/core/harbor/terrain.ts +0 -54
  350. package/src/core/http/index.ts +0 -147
  351. package/src/core/http/websocket.ts +0 -124
  352. package/src/core/line/answer.ts +0 -60
  353. package/src/core/line/frame.ts +0 -83
  354. package/src/core/line/ground.ts +0 -18
  355. package/src/core/line/index.ts +0 -8
  356. package/src/core/line/web.ts +0 -123
  357. package/src/core/node/index.ts +0 -139
  358. package/src/core/pointer/bodies.ts +0 -122
  359. package/src/core/pointer/index.ts +0 -4
  360. package/src/core/pointer/world.ts +0 -134
  361. package/src/core/proof/contracts.ts +0 -134
  362. package/src/core/proof/expect.ts +0 -25
  363. package/src/core/proof/index.ts +0 -14
  364. package/src/core/proof/law.ts +0 -742
  365. package/src/core/quo/address.ts +0 -87
  366. package/src/core/quo/door.ts +0 -180
  367. package/src/core/quo/index.ts +0 -12
  368. package/src/core/quo/invitation.ts +0 -19
  369. package/src/core/quo/keys.ts +0 -91
  370. package/src/core/quo/payload.ts +0 -61
  371. package/src/core/quo/relations.ts +0 -62
  372. package/src/core/quo/reply.ts +0 -38
  373. package/src/core/quo/seal.ts +0 -101
  374. package/src/core/quo/standing.ts +0 -111
  375. package/src/core/stand/main.ts +0 -20
  376. package/src/core/stand/stand.ts +0 -259
  377. package/src/core/tcp/index.ts +0 -227
  378. package/src/core/ward/allowance.ts +0 -47
  379. package/src/core/ward/cells.ts +0 -77
  380. package/src/core/ward/house.ts +0 -550
  381. package/src/core/ward/index.ts +0 -7
  382. package/src/core/ward/partition.ts +0 -120
  383. package/src/core/ward/stance.ts +0 -215
  384. package/src/core/ward/ward.ts +0 -166
  385. package/src/core.ts +0 -55
  386. package/src/defaults/dock/index.ts +0 -12
  387. package/src/defaults/index.ts +0 -10
  388. package/src/defaults/memory/index.ts +0 -50
  389. package/src/defaults/tcp/index.ts +0 -89
  390. package/src/defaults/ward/index.ts +0 -12
  391. package/src/defaults/web/index.ts +0 -97
  392. package/src/edge.ts +0 -11
  393. package/src/index.ts +0 -18
  394. package/src/kit.ts +0 -15
  395. package/src/node.ts +0 -10
  396. /package/dist/{cli.d.ts → node/cli.d.ts} +0 -0
package/AUTHORING.md ADDED
@@ -0,0 +1,741 @@
1
+ # Writing for nervur
2
+
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.
8
+
9
+ The shop has three classes and one faculty.
10
+
11
+ - `Order` is one order, from its first item to its shipping.
12
+ - `Shop` is the steward, the being that runs the house.
13
+ - `Lobby` is the public being, which strangers may ask.
14
+ - `Payments` is a faculty that charges money, which the ground's
15
+ `recipe.ts` makes.
16
+
17
+ ## Words
18
+
19
+ | Word | What it is |
20
+ | --- | --- |
21
+ | being | an instance of a class, born for one ask and dropped after it |
22
+ | cells | her state, JSON values, kept when an ask lands |
23
+ | ask | a method others may call on her, with its entry |
24
+ | asker | who calls her now: an id and notes |
25
+ | occupant | someone who may ask her, by id |
26
+ | standing | someone she may ask, by id |
27
+ | blueprint | the shape of what can be called: a name and its methods |
28
+ | need | the blueprint she calls, at its minimum, under a member of her own |
29
+ | faculty | anything outside the house that answers a blueprint |
30
+ | offer | a faculty the ground hands the house: its blueprint and its object |
31
+ | role | a named test over the asker and her cells |
32
+ | state | a name read from her cells that decides which asks exist |
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 |
35
+
36
+ ## A being
37
+
38
+ A being is pure logic over her cells. She never knows where she runs,
39
+ who carries her asks, or how her cells are kept. The house does all of
40
+ it, and she trusts it.
41
+
42
+ ```ts
43
+ // classes/order.ts
44
+ import { Being, s, need, type Args } from 'nervur/being';
45
+
46
+ export const Payments = need('payments', {
47
+ charge: {
48
+ args: s.object({ order: s.string(), amount: s.number(), notify: s.handle() }),
49
+ result: s.object({ pending: s.boolean() }),
50
+ },
51
+ });
52
+
53
+ export const Courier = need('courier', {
54
+ pickup: { args: s.object({ items: s.array(s.string()) }) },
55
+ });
56
+
57
+ export class Order extends Being.of({
58
+ kind: 'com.acme.order',
59
+ description: 'One order, from its first item to its shipping.',
60
+ needs: { pay: Payments },
61
+ cells: {
62
+ items: [] as string[],
63
+ total: 0,
64
+ state: 'open',
65
+ paidAt: 0,
66
+ courier: '',
67
+ },
68
+ roles: { owner: (asker) => asker.steward.owner === true },
69
+ state: (me) => me.cells.state,
70
+ asks: {
71
+ hire: {
72
+ for: 'steward',
73
+ hints: { idempotent: true },
74
+ args: s.object({ courier: s.handle() }),
75
+ result: s.string(),
76
+ },
77
+ add: {
78
+ in: 'open',
79
+ for: 'owner',
80
+ to: 'open',
81
+ args: s.object({ sku: s.string(), price: s.number() }),
82
+ result: s.object({ total: s.number() }),
83
+ },
84
+ checkout: { in: 'open', for: 'owner', to: 'paying' },
85
+ charged: { in: 'paying', args: s.reply(Payments.charge), to: ['paying', 'open'] },
86
+ settled: { in: 'paying', for: 'handle', to: 'paid' },
87
+ ship: { in: 'paid', for: 'owner', to: 'shipped' },
88
+ },
89
+ }) {
90
+ // The courier's invitation arrives as her standing. The same one twice is the same standing.
91
+ hire({ courier }: Args<Order, 'hire'>) {
92
+ this.cells.courier = courier;
93
+ return courier;
94
+ }
95
+
96
+ add({ sku, price }: Args<Order, 'add'>) {
97
+ this.cells.items = [...this.cells.items, sku];
98
+ this.cells.total += price;
99
+ return { total: this.cells.total };
100
+ }
101
+
102
+ checkout() {
103
+ if (this.cells.items.length === 0) this.fail('Add an item first.');
104
+ const notify = this.handle('settled', { once: true });
105
+ this.pay.charge({ order: this.id, amount: this.cells.total, notify }, { reply: 'charged' });
106
+ this.cells.state = 'paying';
107
+ }
108
+
109
+ charged({ error }: Args<Order, 'charged'>) {
110
+ if (error) this.cells.state = 'open';
111
+ }
112
+
113
+ settled() {
114
+ this.cells.state = 'paid';
115
+ this.cells.paidAt = this.house.now();
116
+ }
117
+
118
+ ship() {
119
+ if (this.cells.courier === '') this.fail('Hire a courier first.');
120
+ this.held(this.cells.courier, Courier).pickup({ items: this.cells.items });
121
+ this.cells.state = 'shipped';
122
+ }
123
+ }
124
+ ```
125
+
126
+ The order wrote no retry, no catch, no key and no address. `charge` is
127
+ an effect, so it leaves only once `checkout` has landed. Its answer comes
128
+ back to `charged`, and a refused charge opens the order again. The
129
+ provider calls `settled` through the handle once the money arrives.
130
+ `hire` takes a courier's invitation as her standing, and `ship` asks the
131
+ courier through it. `shipped` is a terminal state, and nothing leaves it.
132
+
133
+ ### The declaration
134
+
135
+ A class extends `Being.of({ … })`. That one object declares the class,
136
+ and TypeScript reads from it the types of her cells and her needs.
137
+
138
+ | Field | What it is |
139
+ | --- | --- |
140
+ | `kind` | the code's name: a reversed domain you own, then a name |
141
+ | `description` | one line for readers and agents |
142
+ | `cells` | the state and its defaults, written where a key is missing |
143
+ | `needs` | blueprints she calls, each under a member name she chooses |
144
+ | `roles` | named tests over the asker and her cells |
145
+ | `state` | a function of the being that names her current state |
146
+ | `asks` | one entry per method she answers |
147
+
148
+ Every field is optional but `kind` and `asks`. A class with no `state`
149
+ has one state, named `ready`. The kind is how the house finds her code
150
+ again, so a bundler that renames classes changes nothing.
151
+
152
+ A method in TypeScript names its args with `Args<Class, 'method'>`,
153
+ since a subclass's method takes no type from its base. A method in
154
+ JavaScript writes nothing.
155
+
156
+ ### An ask's entry
157
+
158
+ | Field | What it says |
159
+ | --- | --- |
160
+ | `in` | the states where the ask exists; omitted, every state |
161
+ | `for` | the roles that may call it; omitted, every occupant but handles |
162
+ | `to` | the states it may land in; omitted, the state it began in |
163
+ | `args` | the schema of what it takes; omitted, the empty object alone |
164
+ | `result` | the schema of what it answers; omitted, nothing |
165
+ | `hints` | `readOnly`, `idempotent`, `destructive` |
166
+ | `examples` | cells, a role, args, fakes, and what it gives |
167
+ | `description` | one line for readers and agents |
168
+ | `wait` | milliseconds she may run; omitted, thirty seconds |
169
+
170
+ `in`, `for` and `to` each take one name or a list of names. A method
171
+ with no entry is never reached. An entry with no method is refused when
172
+ the house first loads the class.
173
+
174
+ Five roles are the house's. `handle` is whoever holds a handle to this
175
+ ask. `stranger` is the asker of a public being. `steward` is her
176
+ steward. `being` is another being her steward introduced to her. `root`
177
+ is the owner, through the house's hand. Every other role is yours, a
178
+ function of `(asker, me)`.
179
+
180
+ The house checks the table when it first loads the class. It refuses a
181
+ state no ask reaches, an ask no role reaches, and a role no ask names.
182
+ After a method runs, a state outside the entry's `to` fails the ask, and
183
+ nothing lands.
184
+
185
+ ### What she reaches
186
+
187
+ | Member | What it is |
188
+ | --- | --- |
189
+ | `this.id` | her own id |
190
+ | `this.position` | `steward`, `public` or `normal` |
191
+ | `this.asker` | `{ id, notes, steward }` of who asks now; `signer` for a stranger |
192
+ | `this.cells` | her values |
193
+ | `this.house.now()` | the time, in milliseconds since the epoch |
194
+ | `this.house.random({ length })` | random bytes |
195
+ | `this.house.alarm({ at, ask, args, key })` | one of her asks, at that time |
196
+ | `this.house.cancelAlarm({ key })` | an alarm removed |
197
+ | `this.<need>.<method>({…}, { reply? })` | an awaited call, or an effect |
198
+ | `this.held(id, Need).<ask>({…}, { reply? })` | the same, on a standing |
199
+ | `this.standings` | `list()`, `note(id, notes)` and `drop(id)` |
200
+ | `this.handle(ask, { bind?, notes?, once?, expires? })` | a handle to one of her asks |
201
+ | `this.invite(id, { notes?, expires? })` | a new occupant, as a handle |
202
+ | `this.occupants` | `list()`, `note(id, notes)` and `dismiss(id)` |
203
+ | `this.steward` | her steward, where she is not one |
204
+ | `this.powers` | the house's powers, where she is the steward |
205
+ | `this.fail(message)` | an error the asker can act on |
206
+
207
+ An alarm lives in the house's rows and survives every restart. A second
208
+ alarm under one key replaces the first. When its time comes, the house
209
+ asks her named ask as her steward would.
210
+
211
+ ### Awaited calls and effects
212
+
213
+ The callee decides how it is called, with one flag. A method or ask
214
+ marked `idempotent` is safe to repeat, so the caller awaits it during
215
+ her ask. Everything else is an effect. `readOnly` is stricter: it writes
216
+ nothing, it implies `idempotent`, and the house holds it.
217
+
218
+ An awaited call answers during her ask. `await this.fx.rate({…})`
219
+ returns the answer, and a failure throws an error she may catch. It
220
+ waits thirty seconds unless its entry says otherwise, and never more
221
+ than five minutes.
222
+
223
+ An awaited call never comes back to a being in its own chain. While she
224
+ awaits, she holds her queue. A call back to her, other than a
225
+ `readOnly` one, waits behind the ask that waits for it, until the wait
226
+ runs out and the call fails as `answered nothing`. Call back with an
227
+ effect instead.
228
+
229
+ An effect leaves after her ask lands. She calls it and it returns
230
+ nothing. The house writes it in the same write as her cells, then sends
231
+ it with one call id until it is answered. So an effect acts at most
232
+ once. Its answer comes back to the ask `reply` names, as `{ result }` or
233
+ `{ error: { message } }`.
234
+
235
+ An effect gives up at its deadline, seven days for a standing and the
236
+ offer's window for a faculty. The reply then hears an error. Effects to
237
+ one receiver leave one at a time, in the order she called them.
238
+
239
+ Asks to one being run one at a time, in the order they arrive. A
240
+ `readOnly` ask runs beside that queue, on the cells last landed.
241
+
242
+ ### Relations
243
+
244
+ An **occupant** is someone who may ask her. She mints one with
245
+ `this.invite(id, { notes })`, which answers a handle, and lets one go with
246
+ `dismiss`.
247
+
248
+ A **standing** is someone she may ask. She receives one where an ask's
249
+ args carry an invitation under `s.handle`, or where her steward
250
+ introduces one. She asks through it with `this.held(id, Need)`, which
251
+ checks the standing's describe covers the need.
252
+
253
+ Every relation carries two sets of notes. `notes` are hers alone.
254
+ `steward` are her steward's, written when the steward made the relation,
255
+ and she reads them and never writes them. The order's `owner` role reads
256
+ the steward's notes, so only the steward decides who owns an order.
257
+
258
+ A **handle** is the only way a relation leaves her. `this.handle(ask)`
259
+ admits its holder to that one ask. `once` dismisses it after its first
260
+ ask lands, and `bind` fixes args the holder cannot change. The house
261
+ turns a handle into an invitation for a far house, or a token for a
262
+ faculty. A handle never enters her cells.
263
+
264
+ `expires`, on `this.handle`, `this.invite` and the steward's `invite`,
265
+ gives each invitation minted from the handle that many milliseconds to
266
+ be taken. Its first knock lands within that time, or it hears silence.
267
+ One taken in time never expires, and one without `expires` waits for
268
+ ever. Give it to every invitation a being mints on each answer, so the
269
+ unspent ones go.
270
+
271
+ ### The steward
272
+
273
+ Every house has one steward, with the id `steward`. The holder of the
274
+ house's hand asks her as the occupant `root`, which no sealed box can
275
+ forge. She alone holds `this.powers`.
276
+
277
+ ```ts
278
+ // classes/shop.ts
279
+ import { Being, s, type Args } from 'nervur/being';
280
+
281
+ export class Shop extends Being.of({
282
+ kind: 'com.acme.shop',
283
+ description: 'The shop’s steward: opens orders, and enrols whoever signs up.',
284
+ cells: { opened: 0 },
285
+ roles: { pilot: (asker) => asker.id === 'root' },
286
+ asks: {
287
+ open: {
288
+ for: 'pilot',
289
+ description: 'Opens an order, and hands back an invitation for its owner.',
290
+ args: s.object({ id: s.string() }),
291
+ result: s.object({ owner: s.invitation() }),
292
+ },
293
+ orders: { for: 'pilot', hints: { readOnly: true }, result: s.array(s.string()) },
294
+ hire: {
295
+ for: 'pilot',
296
+ description: 'Hands a courier’s invitation to an order, which takes it.',
297
+ hints: { idempotent: true },
298
+ args: s.object({ order: s.string(), courier: s.invitation() }),
299
+ result: s.string(),
300
+ },
301
+ enroll: {
302
+ for: 'being',
303
+ hints: { idempotent: true },
304
+ args: s.object({ signer: s.bytes() }),
305
+ result: s.object({ invitation: s.invitation() }),
306
+ },
307
+ },
308
+ }) {
309
+ open({ id }: Args<Shop, 'open'>) {
310
+ this.powers!.bear({ kind: 'com.acme.order', id });
311
+ this.cells.opened += 1;
312
+ return { owner: this.powers!.invite({ id, occupant: 'owner', notes: { owner: true } }) };
313
+ }
314
+
315
+ async orders() {
316
+ return (await this.powers!.list()).filter((being) => being.kind === 'com.acme.order').map((being) => being.id);
317
+ }
318
+
319
+ // She carries the invitation unopened, and the order she names takes it.
320
+ async hire({ order, courier }: Args<Shop, 'hire'>) {
321
+ return (await this.powers!.ask({ id: order, method: 'hire', args: { courier } })) as string;
322
+ }
323
+
324
+ // One order a signer: asked twice, the same id is borne once.
325
+ enroll({ signer }: Args<Shop, 'enroll'>) {
326
+ const id = `order-${Array.from(signer.subarray(0, 8), (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
327
+ this.powers!.bear({ kind: 'com.acme.order', id });
328
+ const occupant = `owner-${Array.from(this.house.random({ length: 4 }), (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
329
+ return { invitation: this.powers!.invite({ id, occupant, notes: { owner: true } }) };
330
+ }
331
+ }
332
+ ```
333
+
334
+ | Power | What it does |
335
+ | --- | --- |
336
+ | `bear({ kind, id, args })` | places a new being; the same id twice answers the first |
337
+ | `remove({ id })` | removes a being and everything of hers |
338
+ | `list()` | every being, her kind, whether she is absent, and her dead letters |
339
+ | `ask({ id, method, args }, { reply? })` | asks any being as her occupant `steward` |
340
+ | `introduce({ from, to, notes })` | gives `from` a standing on `to`, and `to` an occupant |
341
+ | `invite({ id, occupant, notes, expires? })` | gives a being a new occupant, and the steward its handle |
342
+
343
+ `bear`, `remove`, `introduce` and `invite` land in the steward's own
344
+ write. If her ask fails, none of them happened. A being borne runs her
345
+ `born` ask first, where her class declares one.
346
+
347
+ Every other being is placed as `normal`. She holds the standing
348
+ `steward` and the occupant `steward`, and can drop neither.
349
+
350
+ An invitation from outside lands where the owner says. The courier's
351
+ invitation reaches the owner by any road, a mail or a link. The owner
352
+ hands it to the steward's `hire` with the order it is for. `hire`
353
+ declares it `s.invitation`, so the steward carries it unopened and never
354
+ holds it. The order's `hire` declares it `s.handle`, so the order takes
355
+ it, and the standing is hers.
356
+
357
+ Taking an invitation is safe to repeat. The same invitation taken again
358
+ gives the same standing, so the order's `hire` is `idempotent`, and the
359
+ steward awaits it. The owner hears the standing, or why it was refused.
360
+ A steward that bears a being for an invitation bears her first, then
361
+ hands it to her the same way.
362
+
363
+ ### A public being
364
+
365
+ A house may name one public being, with the id `public`. She answers
366
+ strangers: every asker with no relation is the occupant `stranger`, and
367
+ `this.asker.signer` is the key the ask was signed with.
368
+
369
+ ```ts
370
+ // classes/lobby.ts
371
+ import { Being, need, s } from 'nervur/being';
372
+
373
+ /** What the lobby asks of her steward. */
374
+ const Signup = need('signup', {
375
+ enroll: {
376
+ hints: { idempotent: true },
377
+ args: s.object({ signer: s.bytes() }),
378
+ result: s.object({ invitation: s.invitation() }),
379
+ },
380
+ });
381
+
382
+ export class Lobby extends Being.of({
383
+ kind: 'com.acme.lobby',
384
+ description: 'The shop’s front door: a stranger signs up and receives an order of their own.',
385
+ asks: {
386
+ signup: { for: 'stranger', result: s.object({ invitation: s.invitation() }) },
387
+ },
388
+ }) {
389
+ signup() {
390
+ return this.held('steward', Signup).enroll({ signer: this.asker.signer! });
391
+ }
392
+ }
393
+ ```
394
+
395
+ A public being's asks are idempotent by default, since a stranger's box
396
+ may arrive twice. The house answers a replayed stranger's box from a
397
+ cache for ten minutes, and nothing runs twice. An ask marked
398
+ `hints: { idempotent: false }` is refused to strangers.
399
+
400
+ Signup awaits the steward. The lobby asks `enroll`, which is
401
+ `idempotent`, so the lobby awaits its answer and returns the invitation
402
+ unopened. A stranger who signs up twice holds one order.
403
+
404
+ Signup is this shop's choice, not the house's. A house is its owner's,
405
+ and a public being does only what its owner wrote. One may answer who
406
+ the house is and nothing more. A house with no public being answers
407
+ strangers silence.
408
+
409
+ ### Schemas
410
+
411
+ One builder gives the schema and the TypeScript type: `s.object`,
412
+ `s.string`, `s.number`, `s.integer`, `s.boolean`, `s.array`, `s.enum`,
413
+ `s.const`, `s.bytes`, `s.optional`, `s.handle`, `s.invitation` and
414
+ `s.reply`. They write JSON Schema 2020-12, in a subset the house checks
415
+ on every ask.
416
+
417
+ - `s.bytes` is a `Uint8Array` in the process and lowercase hex across a
418
+ door.
419
+ - `s.handle` carries a relation. A handle leaves as an invitation, and
420
+ an invitation arrives as a new standing id.
421
+ - `s.invitation` carries an invitation unopened, as the lobby does.
422
+ Passed on under `s.handle`, it is taken by the being that receives
423
+ it. Taking one is safe to repeat: the same invitation gives the same
424
+ standing.
425
+ - `s.reply(method)` is the args of an effect's reply: `{ result }` or
426
+ `{ error: { message } }`.
427
+
428
+ ### Needs
429
+
430
+ A need is a blueprint at its minimum: what she will call, never what a
431
+ faculty offers. `need(name, methods)` writes one, and she binds it to a
432
+ member of her own in `needs`. An offer covers a need when four things
433
+ hold.
434
+
435
+ 1. The blueprint's name is the same.
436
+ 2. Every method the need names is offered. More are allowed.
437
+ 3. The offer requires no property the need does not require.
438
+ 4. Each method is `idempotent` in both, or in neither.
439
+
440
+ Every need is covered, or she is absent. An absent being answers silence
441
+ and keeps her cells, and answers again once an offer covers her needs.
442
+
443
+ ### Testing on the bench
444
+
445
+ The bench opens two houses in one process, on fake memory, keys, clock
446
+ and carry. So every ask crosses a door as it would in production.
447
+
448
+ ```ts
449
+ // order.test.ts
450
+ import assert from 'node:assert/strict';
451
+ import { test } from 'node:test';
452
+ import { Bench } from 'nervur/bench';
453
+ import { Order } from './classes/order.ts';
454
+ import { Payments, paymentsOffer } from './payments.ts';
455
+
456
+ test('Order keeps her table: every state and role shows what it owes', async () => {
457
+ await Bench.check(Order);
458
+ });
459
+
460
+ test('An order is paid once the provider calls the handle it was given', async () => {
461
+ const payments = new Payments();
462
+ const bench = await Bench.open({ classes: [Order], offers: [paymentsOffer(payments)] });
463
+ const order = await bench.place(Order, { id: 'first' });
464
+
465
+ assert.deepEqual(await order.ask('checkout'), { error: { message: 'Add an item first.' } });
466
+ assert.deepEqual(await order.ask('add', { sku: 'tea', price: 4 }), { result: { total: 4 } });
467
+ assert.deepEqual(await order.ask('checkout'), { result: null });
468
+ await bench.settle();
469
+ assert.equal((await order.cells())!.state, 'paying', 'the charge left once checkout landed, and answered pending');
470
+
471
+ assert.deepEqual(await payments.settle('first'), { result: null });
472
+ await bench.settle();
473
+ assert.equal((await order.cells())!.state, 'paid');
474
+ assert.deepEqual(await order.ask('add', { sku: 'jam', price: 1 }), { error: { message: 'not in this state' } });
475
+ });
476
+ ```
477
+
478
+ `place(Class, { id, cells })` bears a being and starts her from those
479
+ cells. A placed being is asked with `ask(method, args, { role })`,
480
+ described with `describe({ role })`, and read with `cells()`. `settle()`
481
+ lets every effect and reply run, and `advance(ms)` moves the fake clock.
482
+
483
+ The bench plays a role with an occupant named for it, whose own notes and
484
+ steward notes both hold the role as `true`.
485
+
486
+ A steward and a public being are placed where the house places them.
487
+ `Bench.open({ steward: Shop, public: Lobby, classes: [Order] })` opens the
488
+ author's house with them there, and `place(Shop)` and `place(Lobby)` find
489
+ them. A role `root` holds is played through the hand, on any being. On
490
+ the public being, a role a stranger holds is played by a box with no
491
+ relation, signed with a key drawn from the bench's seed.
492
+ `Bench.check(Shop, { position: 'steward' })` and `Bench.check(Lobby, {
493
+ position: 'public', steward: Shop })` check them.
494
+
495
+ An entry's `examples` are tests the bench runs. Each is one ask on a
496
+ fresh bench: `cells` start her, `role` asks, `args` are the ask's,
497
+ `fakes` answer her needs, and `gives` is the answer owed. `Bench.check`
498
+ runs every example twice from one seed and flags a class that answers
499
+ differently. It then describes every state to every role, and names every
500
+ finding that failed.
501
+
502
+ ## A faculty
503
+
504
+ A faculty is anything a being may call that is not a being: a payment
505
+ provider, a mail sender, a model, a sensor. The ground hands it to the
506
+ house as an offer, and the house matches it to every need it covers.
507
+
508
+ ```ts
509
+ // payments.ts
510
+ import type { FacultyContext, Offer } from 'nervur';
511
+ import { need, s } from 'nervur/being';
512
+
513
+ /** What the faculty offers. An order's need is covered by it. */
514
+ export const PaymentsBlueprint = need('payments', {
515
+ charge: {
516
+ description: 'Charges an order, and calls notify once the money arrives.',
517
+ args: s.object({ order: s.string(), amount: s.number(), notify: s.handle() }),
518
+ result: s.object({ pending: s.boolean() }),
519
+ },
520
+ });
521
+
522
+ type Answer = { result: { pending: boolean } } | { error: { message: string } };
523
+
524
+ /**
525
+ * A payment provider in the ground's process. It answers a call id it has
526
+ * seen with the answer it gave, so an effect sent twice charges once. A
527
+ * provider that changes the world keeps these where a restart keeps them.
528
+ */
529
+ export class Payments {
530
+ readonly #answered = new Map<string, Answer>();
531
+ readonly #waiting = new Map<string, { notify: string; context: FacultyContext }>();
532
+
533
+ async charge({ order, amount, notify }: { order: string; amount: number; notify: string }, context: FacultyContext): Promise<Answer> {
534
+ const seen = this.#answered.get(context.id);
535
+ if (seen !== undefined) return seen;
536
+ const answer: Answer = amount > 0 ? { result: { pending: true } } : { error: { message: 'Nothing to charge.' } };
537
+ if (amount > 0) this.#waiting.set(order, { notify, context });
538
+ this.#answered.set(context.id, answer);
539
+ return answer;
540
+ }
541
+
542
+ /** The money for an order arrived: the provider calls the handle it was given. */
543
+ settle(order: string) {
544
+ const waiting = this.#waiting.get(order);
545
+ if (waiting === undefined) throw new Error(`no charge waits for ${order}`);
546
+ this.#waiting.delete(order);
547
+ return waiting.context.call({ token: waiting.notify, id: `settle:${order}` });
548
+ }
549
+ }
550
+
551
+ /** The offer a ground hands: the blueprint, the object, and a week's memory of call ids. */
552
+ export const paymentsOffer = (payments: Payments): Offer => ({ blueprint: PaymentsBlueprint, object: payments, window: 7 * 86_400_000 });
553
+ ```
554
+
555
+ An offer is `{ blueprint, object, kinds?, window?, handler?, stop? }`.
556
+
557
+ - **The object has the blueprint's methods.** Each takes one args object
558
+ and a context, and answers `{ result }` or `{ error: { message } }`. A
559
+ throw is a failure to answer, and the house tries again.
560
+ - **The context holds the call id.** An effect arrives again with the same
561
+ call id when an answer was lost. A faculty that changes the world
562
+ answers a call id it has seen with the answer it gave.
563
+ - **A handle arrives as a token.** The faculty calls it with
564
+ `context.call({ token, args, id })`. Its own `id` makes that call run
565
+ once, however often it is sent.
566
+ - **`kinds` is the ground's grant.** It lists the classes that may hold
567
+ the offer. Without it, every class whose need it covers holds it.
568
+ - **`window` is how long the faculty remembers a call id.** It is seven
569
+ days where omitted, and the house gives up on an effect at it.
570
+ - **`handler` answers HTTP on the ground's one listener.** It takes a
571
+ `Request` and answers a `Response`, or `null` where the request is not
572
+ its own. A site, an API or an MCP server is a faculty with a handler.
573
+ - **`stop` is called when the ground stops**, faculties in the reverse of
574
+ the order they were made.
575
+
576
+ The object arrives living. The ground makes and starts the faculty, and
577
+ the house never starts, stops or restarts it. Every being whose need it
578
+ covers holds the same object.
579
+
580
+ A faculty is the ground's, never a house's. The ground's recipe makes
581
+ each one by name, from the settings the ground hands it. `env` is the
582
+ ground's environment, so a secret stays on its machine and out of the
583
+ code. `dir(name)` answers a folder of the faculty's own.
584
+
585
+ ```ts
586
+ // recipe.ts
587
+ import { Payments, paymentsOffer } from './payments.ts';
588
+
589
+ export const faculties = () => ({
590
+ payments: paymentsOffer(new Payments()),
591
+ });
592
+ ```
593
+
594
+ Policy over a faculty is written as a being. Limits, approvals and
595
+ quotas are not the faculty's. One being holds the raw faculty through
596
+ `kinds`, and every other being reaches her through a standing.
597
+
598
+ ## A ground
599
+
600
+ A ground is the process houses run in, and you write none. `nervur up`
601
+ runs one on a folder: the recipe of its faculties, and a folder of code
602
+ for each house. It keeps a record of its houses, and opens each on the
603
+ bodies the record names.
604
+
605
+ ### The shop's folder
606
+
607
+ ```text
608
+ nervur-ground/
609
+ recipe.ts
610
+ payments.ts
611
+ classes/index.ts, order.ts, shop.ts, lobby.ts
612
+ state/ made by the ground, its owner's alone
613
+ ```
614
+
615
+ A house's folder names what the house holds.
616
+
617
+ ```ts
618
+ // classes/index.ts
619
+ export { Shop as steward } from './shop.ts';
620
+ export { Lobby as public } from './lobby.ts';
621
+ import { Order } from './order.ts';
622
+
623
+ export const beings = [Order];
624
+ ```
625
+
626
+ ### Running it
627
+
628
+ ```bash
629
+ npx nervur up .
630
+ ```
631
+
632
+ The ground boots in one order, and a stop is that order reversed, on an
633
+ interrupt and on `SIGTERM`.
634
+
635
+ 1. **Lock.** One ground to its state.
636
+ 2. **Its own.** Its seeds and its record open.
637
+ 3. **Faculties.** The recipe makes each, in its order.
638
+ 4. **Houses.** Each house of the record opens on its bodies.
639
+ 5. **Hook.** The listeners, then the hand on its socket.
640
+ 6. **Ready.** It tells systemd it is up, where systemd waits.
641
+
642
+ It is set by its environment.
643
+
644
+ | Setting | What it sets |
645
+ | --- | --- |
646
+ | `NERVUR_TCP_PORT` | its TCP port, 7300 where unset |
647
+ | `NERVUR_HTTP_PORT` | its HTTP port, for Quo over the web and every handler; no HTTP where unset |
648
+ | `NERVUR_ADDRESSES` | the public addresses it writes into invitations, by commas |
649
+ | `NERVUR_BIND` | the address it listens on, `0.0.0.0` where unset |
650
+ | `NERVUR_STATE` | its state, `state/` in its folder where unset |
651
+ | `NERVUR_KEYCHAIN` | a keychain service holding its seeds on macOS, in place of files |
652
+ | `NERVUR_WAIT` | its bound on every ask, in milliseconds |
653
+
654
+ The state holds each house's seed in a file its owner alone reads, and
655
+ each house's ledger. A ledger appends every write and never rewrites
656
+ one, and one behind its witness is refused, so a restored backup cannot
657
+ replay what a house already answered. A lost seed is a lost house.
658
+
659
+ A house is added once, and the ground opens it again at every start.
660
+
661
+ ```bash
662
+ npx nervur houses add name=shop memory='{"body":"ledger"}' classes='{"body":"folder","at":"classes"}' faculties='["payments"]'
663
+ ```
664
+
665
+ `memory` and `classes` name the bodies the house is handed. `ledger` and
666
+ `folder` are the ground's own, and a recipe may add more, a git registry
667
+ or another store among them. `faculties` names what the house receives,
668
+ and within it each offer's `kinds` names the classes that hold it.
669
+
670
+ ### The hand
671
+
672
+ The command is a face on the ground's hand. It holds no word of its own
673
+ beyond `up` and `service`, and reads every other from what the ground
674
+ describes.
675
+
676
+ ```bash
677
+ npx nervur help
678
+ ```
679
+
680
+ A faculty's method is called as its owner calls it, and a faculty named
681
+ alone shows its methods. Args are one JSON object, or words `key=value`.
682
+
683
+ ```bash
684
+ npx nervur houses list
685
+ ```
686
+
687
+ `ask` asks a being in a house as `root`: the steward, or the being
688
+ `--id` names. The owner lands a paper in a being with one ask, and her
689
+ `accept` names `root` in its `for`, or names no `for`.
690
+
691
+ ```bash
692
+ npx nervur ask shop open id=first
693
+ ```
694
+
695
+ ```bash
696
+ npx nervur ask shop --id alice accept invitation=7b22…
697
+ ```
698
+
699
+ Each prints one JSON value. It exits 0 on a result and 1 on an error
700
+ answered. It exits 2 where nothing was asked, so a script tells a
701
+ refusal from a ground that is down. The hand is `state/hand` in the
702
+ folder where the command runs, or the socket `--at` or `NERVUR_HAND`
703
+ names.
704
+
705
+ ### Running it as a service
706
+
707
+ `nervur service` writes the unit that runs a folder: a systemd user unit
708
+ on Linux, started again after any exit, and a launchd agent on macOS.
709
+
710
+ ```bash
711
+ npx nervur service . > ~/.config/systemd/user/nervur-ground.service
712
+ ```
713
+
714
+ ```bash
715
+ systemctl --user enable --now nervur-ground
716
+ ```
717
+
718
+ A user unit stops when its user logs out, unless lingering is enabled
719
+ with `loginctl enable-linger`. On macOS, the agent goes to
720
+ `~/Library/LaunchAgents/` and is started with `launchctl bootstrap`.
721
+
722
+ ## What the house guarantees
723
+
724
+ 1. **Every need is covered, or she is absent.** An absent being answers
725
+ silence and keeps her cells.
726
+ 2. **The asker's id is true.** Wherever the asker lives, the id she reads
727
+ is who asked.
728
+ 3. **Her notes are hers.** No one else writes them, and she never writes
729
+ her steward's.
730
+ 4. **She never holds an invitation's bytes.** Handles leave, standing ids
731
+ arrive, and `s.invitation` passes through unopened.
732
+ 5. **One ask at a time.** Asks to one being run in order, and `readOnly`
733
+ asks run beside them.
734
+ 6. **An ask lands whole or not at all.** Her cells, her relations, her
735
+ effects and her call ids land in one write.
736
+ 7. **An effect acts at most once, and its outcome is known.** Its answer,
737
+ or its giving up, reaches her through `reply`.
738
+ 8. **Her class is checked before her first ask.** A class that fails
739
+ leaves her absent, and the refusal says why.
740
+
741
+ A failed ask changed nothing she owns, so asking again is safe.