@nervur-org/dock 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +17 -15
  2. package/api/route.ts +4 -72
  3. package/beings/avatar.ts +11 -7
  4. package/beings/carry.ts +129 -116
  5. package/beings/clock.ts +11 -7
  6. package/beings/desk.ts +7 -6
  7. package/beings/doorbell.ts +36 -7
  8. package/beings/index.ts +17 -4
  9. package/beings/join.ts +7 -4
  10. package/beings/link.ts +1 -1
  11. package/beings/porter.ts +5 -1
  12. package/beings/presence.ts +15 -5
  13. package/beings/setup.ts +27 -21
  14. package/beings/user.ts +26 -17
  15. package/beings/way.ts +1 -1
  16. package/{dock → box}/agents.ts +37 -44
  17. package/{dock → box}/apns.ts +4 -4
  18. package/box/box.ts +31 -0
  19. package/box/contract.ts +41 -0
  20. package/box/dialers.ts +88 -0
  21. package/box/dock.ts +148 -0
  22. package/box/envoy.ts +96 -0
  23. package/box/faculty.ts +111 -0
  24. package/{dock → box}/fcm.ts +1 -1
  25. package/box/grants.ts +90 -0
  26. package/box/http.ts +93 -0
  27. package/{dock → box}/http2.ts +7 -7
  28. package/box/index.ts +201 -0
  29. package/box/join.ts +78 -0
  30. package/box/local.ts +37 -0
  31. package/{dock → box}/location.ts +15 -3
  32. package/{dock → box}/notifications.ts +17 -5
  33. package/box/push.ts +85 -0
  34. package/box/ring.ts +85 -0
  35. package/{dock → box}/service.ts +2 -2
  36. package/box/timer.ts +150 -0
  37. package/box/tree.ts +22 -0
  38. package/{dock → box}/webpush.ts +2 -2
  39. package/cli/client.ts +1 -1
  40. package/cli/daemon.ts +101 -103
  41. package/cli/estate.ts +2 -3
  42. package/cli/nervur.ts +44 -25
  43. package/cli/stand.ts +1 -1
  44. package/dist/api/route.d.ts +1 -2
  45. package/dist/api/route.js +4 -76
  46. package/dist/beings/avatar.d.ts +4 -3
  47. package/dist/beings/avatar.js +9 -6
  48. package/dist/beings/carry.d.ts +10 -12
  49. package/dist/beings/carry.js +119 -127
  50. package/dist/beings/clock.js +10 -7
  51. package/dist/beings/desk.js +8 -7
  52. package/dist/beings/doorbell.d.ts +3 -1
  53. package/dist/beings/doorbell.js +37 -4
  54. package/dist/beings/index.d.ts +24 -4
  55. package/dist/beings/index.js +15 -4
  56. package/dist/beings/join.d.ts +1 -0
  57. package/dist/beings/join.js +6 -4
  58. package/dist/beings/link.d.ts +0 -1
  59. package/dist/beings/link.js +1 -1
  60. package/dist/beings/porter.js +5 -1
  61. package/dist/beings/presence.d.ts +1 -0
  62. package/dist/beings/presence.js +12 -3
  63. package/dist/beings/setup.d.ts +5 -2
  64. package/dist/beings/setup.js +16 -15
  65. package/dist/beings/user.d.ts +4 -3
  66. package/dist/beings/user.js +18 -11
  67. package/dist/{dock → box}/agents.d.ts +7 -4
  68. package/dist/{dock → box}/agents.js +30 -37
  69. package/dist/{dock → box}/apns.d.ts +1 -1
  70. package/dist/{dock → box}/apns.js +3 -3
  71. package/dist/box/box.d.ts +9 -0
  72. package/dist/box/box.js +18 -0
  73. package/dist/box/contract.d.ts +6 -0
  74. package/dist/box/contract.js +35 -0
  75. package/dist/{dock → box}/dialers.d.ts +7 -4
  76. package/dist/box/dialers.js +76 -0
  77. package/dist/box/dock.d.ts +182 -0
  78. package/dist/box/dock.js +128 -0
  79. package/dist/box/envoy.d.ts +44 -0
  80. package/dist/box/envoy.js +75 -0
  81. package/dist/box/faculty.d.ts +68 -0
  82. package/dist/box/faculty.js +97 -0
  83. package/dist/{dock → box}/fcm.d.ts +1 -1
  84. package/dist/{dock → box}/grants.d.ts +4 -3
  85. package/dist/box/grants.js +78 -0
  86. package/dist/box/http.d.ts +26 -0
  87. package/dist/box/http.js +89 -0
  88. package/dist/{dock → box}/http2.d.ts +1 -1
  89. package/dist/{dock → box}/http2.js +6 -6
  90. package/dist/box/index.d.ts +46 -0
  91. package/dist/box/index.js +179 -0
  92. package/dist/box/join.d.ts +19 -0
  93. package/dist/box/join.js +67 -0
  94. package/dist/{dock → box}/local.d.ts +3 -2
  95. package/dist/box/local.js +27 -0
  96. package/dist/{dock → box}/location.d.ts +3 -0
  97. package/dist/box/location.js +21 -0
  98. package/dist/{dock → box}/notifications.d.ts +3 -0
  99. package/dist/{dock → box}/notifications.js +14 -4
  100. package/dist/{dock → box}/push.d.ts +12 -18
  101. package/dist/box/push.js +83 -0
  102. package/dist/box/ring.d.ts +18 -0
  103. package/dist/box/ring.js +55 -0
  104. package/dist/{dock → box}/service.d.ts +1 -1
  105. package/dist/{dock → box}/service.js +1 -1
  106. package/dist/{dock → box}/timer.d.ts +2 -0
  107. package/dist/box/timer.js +127 -0
  108. package/dist/box/tree.d.ts +5 -0
  109. package/dist/box/tree.js +14 -0
  110. package/dist/{dock → box}/webpush.d.ts +1 -1
  111. package/dist/cli/client.d.ts +1 -0
  112. package/dist/cli/daemon.d.ts +2 -8
  113. package/dist/cli/daemon.js +102 -107
  114. package/dist/cli/estate.js +2 -3
  115. package/dist/cli/nervur.js +44 -24
  116. package/dist/cli/stand.d.ts +0 -14
  117. package/dist/cli/stand.js +1 -1
  118. package/dist/harbor/browser.d.ts +8 -5
  119. package/dist/harbor/browser.js +20 -21
  120. package/dist/harbor/capacitor.d.ts +3 -3
  121. package/dist/harbor/capacitor.js +9 -8
  122. package/dist/harbor/disk.d.ts +9 -9
  123. package/dist/harbor/disk.js +56 -51
  124. package/dist/harbor/edge/alarm.d.ts +4 -2
  125. package/dist/harbor/edge/alarm.js +9 -19
  126. package/dist/harbor/edge/edge.d.ts +7 -6
  127. package/dist/harbor/edge/edge.js +58 -53
  128. package/dist/harbor/edge/exercise.js +4 -4
  129. package/dist/harbor/edge/given.d.ts +0 -1
  130. package/dist/harbor/edge/given.js +0 -29
  131. package/dist/harbor/edge/storage.d.ts +0 -2
  132. package/dist/harbor/edge/storage.js +2 -2
  133. package/dist/harbor/http.js +4 -2
  134. package/dist/harbor/sealed.d.ts +0 -5
  135. package/dist/harbor/sealed.js +0 -39
  136. package/dist/harbor/tauri.d.ts +2 -2
  137. package/dist/harbor/tauri.js +13 -10
  138. package/dist/human/app.d.ts +1 -1
  139. package/dist/human/app.js +10 -12
  140. package/dist/human/door.d.ts +0 -1
  141. package/dist/human/door.js +1 -1
  142. package/dist/human/html.js +1 -1
  143. package/dist/human/move.js +1 -1
  144. package/dist/human/tab.bundle.txt +3629 -2824
  145. package/dist/human/wake.d.ts +5 -2
  146. package/dist/human/wake.js +23 -14
  147. package/dist/human/web.d.ts +0 -1
  148. package/dist/human/web.js +3 -3
  149. package/dist/human/worker.bundle.txt +2945 -2118
  150. package/dist/human/worlds.js +3 -3
  151. package/dist/mcp/agent.d.ts +0 -1
  152. package/dist/mcp/agent.js +1 -1
  153. package/dist/mcp/allow.js +3 -2
  154. package/dist/mcp/http.d.ts +0 -1
  155. package/dist/mcp/http.js +1 -1
  156. package/dist/mcp/oauth.d.ts +3 -4
  157. package/dist/mcp/oauth.js +4 -4
  158. package/dist/mcp/route.d.ts +1 -1
  159. package/dist/mcp/runner.d.ts +0 -1
  160. package/dist/mcp/runner.js +1 -1
  161. package/dist/mcp/server.d.ts +0 -1
  162. package/dist/mcp/server.js +1 -1
  163. package/harbor/browser.ts +22 -24
  164. package/harbor/capacitor.ts +9 -8
  165. package/harbor/disk.ts +53 -48
  166. package/harbor/edge/alarm.ts +15 -12
  167. package/harbor/edge/edge.ts +61 -55
  168. package/harbor/edge/exercise.ts +4 -4
  169. package/harbor/edge/given.ts +3 -33
  170. package/harbor/edge/storage.ts +2 -2
  171. package/harbor/http.ts +5 -3
  172. package/harbor/sealed.ts +0 -35
  173. package/harbor/tauri.ts +13 -10
  174. package/human/app.ts +12 -13
  175. package/human/door.ts +1 -1
  176. package/human/html.ts +1 -1
  177. package/human/local.ts +1 -1
  178. package/human/move.ts +1 -1
  179. package/human/tab.bundle.txt +3629 -2824
  180. package/human/wake.ts +36 -16
  181. package/human/web.ts +4 -4
  182. package/human/worker.bundle.txt +2945 -2118
  183. package/human/worlds.ts +3 -3
  184. package/mcp/agent.ts +1 -1
  185. package/mcp/allow.ts +2 -1
  186. package/mcp/http.ts +1 -1
  187. package/mcp/oauth.ts +4 -4
  188. package/mcp/route.ts +1 -1
  189. package/mcp/runner.ts +1 -1
  190. package/mcp/server.ts +1 -1
  191. package/package.json +10 -18
  192. package/GETTING_STARTED.md +0 -138
  193. package/GLOSSARY.md +0 -118
  194. package/WORLDS.md +0 -569
  195. package/beings/envoy.ts +0 -218
  196. package/dist/beings/envoy.d.ts +0 -54
  197. package/dist/beings/envoy.js +0 -211
  198. package/dist/dock/console.d.ts +0 -6
  199. package/dist/dock/console.js +0 -20
  200. package/dist/dock/dialers.js +0 -75
  201. package/dist/dock/faculty.d.ts +0 -24
  202. package/dist/dock/faculty.js +0 -142
  203. package/dist/dock/grants.js +0 -99
  204. package/dist/dock/index.d.ts +0 -41
  205. package/dist/dock/index.js +0 -113
  206. package/dist/dock/local.js +0 -44
  207. package/dist/dock/location.js +0 -11
  208. package/dist/dock/push.js +0 -132
  209. package/dist/dock/timer.js +0 -149
  210. package/dock/console.ts +0 -23
  211. package/dock/dialers.ts +0 -85
  212. package/dock/faculty.ts +0 -148
  213. package/dock/grants.ts +0 -111
  214. package/dock/index.ts +0 -133
  215. package/dock/local.ts +0 -48
  216. package/dock/push.ts +0 -161
  217. package/dock/timer.ts +0 -174
  218. package/quo-api.md +0 -196
  219. package/quo-app-bells.md +0 -280
  220. package/quo-dock.md +0 -1945
  221. package/quo-harbor.md +0 -874
  222. package/quo-human.md +0 -1042
  223. package/quo-mcp.md +0 -343
  224. /package/dist/{dock → box}/fcm.js +0 -0
  225. /package/dist/{dock → box}/webpush.js +0 -0
package/human/worlds.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  // `tab.ts` is the shell over this, and this is what the memory harbor proves.
12
12
  import type { Harbor, Hosted } from 'nervur/harbor';
13
13
  import { Avatar, USER } from '../beings/avatar.ts';
14
- import { settled } from '../dock/faculty.ts';
14
+ import { settled } from '../box/join.ts';
15
15
 
16
16
  // Which worlds a device has joined, and where each one lives. A tab learns
17
17
  // this from the page it is on, one world per origin, and needs it only to
@@ -113,14 +113,14 @@ export async function fresh(ward: Hosted): Promise<Relation> {
113
113
  // is a tab that hangs.
114
114
  for (let n = 1; n <= 64; n++) {
115
115
  const key = `r${n}`;
116
- const out = (await ward.ask('boot', { key, class: 'Presence' })) as { error?: string };
116
+ const out = (await ward.ask('boot', { key, class: 'beings/Presence' })) as { error?: string };
117
117
  if (out.error) continue;
118
118
  // She takes what the box lends at her birth, and birth is synchronous
119
119
  // while taking a standing is not. She is handed back when they have
120
120
  // landed, not before: a screen painted from her describe a moment early
121
121
  // is a page missing every faculty of the box, and nothing paints it
122
122
  // again until the person navigates.
123
- await settled();
123
+ await settled(ward);
124
124
  return { key, avatar: ward.being(key) as Avatar };
125
125
  }
126
126
  throw new Error('the tab could not boot an avatar: every key it tries is taken');
package/mcp/agent.ts CHANGED
@@ -21,7 +21,7 @@ export type Run = (event: JsonObject) => Promise<Json>;
21
21
 
22
22
  // The ask on the user being that takes the result. Gated to devices; the
23
23
  // agent's client identity is what the gate reads.
24
- export const REPORT = 'report';
24
+ const REPORT = 'report';
25
25
 
26
26
  // `claude -p` or any CLI agent: the command runs in the agent's folder with
27
27
  // the event, as JSON, as its last argument. The result is what it printed.
package/mcp/allow.ts CHANGED
@@ -92,7 +92,8 @@ export class Exchange {
92
92
  const world = worlds.find((w) => w.pk === inv.ward);
93
93
  if (!world) return again('That invitation is for a world that does not live here.');
94
94
  const identity = word(f.get('identity'));
95
- if (identity === null || identity === world.user || identity === 'desk') return again('An identity is one word, and not the user or the desk.');
95
+ // the ward judges the rest: an identity that is one of its beings is refused at the join
96
+ if (identity === null) return again('An identity is one word.');
96
97
  const joined = await this.o.join(identity, inv, world.ward);
97
98
  if (joined.error) return again(`Not in: ${joined.error}.`);
98
99
  const out = await this.o.oauth.complete(request, identity, world.ward);
package/mcp/http.ts CHANGED
@@ -14,7 +14,7 @@ import type { Serving } from '../beings/side.ts';
14
14
  import { mcpSide } from './server.ts';
15
15
 
16
16
  export type Session = { identity: string; ward: string; transport: StreamableHTTPServerTransport; serving: Serving; touched: number };
17
- export const SESSION_IDLE = 60 * 60 * 1000;
17
+ const SESSION_IDLE =60 * 60 * 1000;
18
18
  // An identity in a world: one harbor holds many, and the grant names which.
19
19
  export type Resolve = (identity: string, ward: string) => Promise<{ avatar?: Avatar; error?: string }>;
20
20
 
package/mcp/oauth.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  // S256, the code grant, refresh, opaque tokens. It is a route, not Quo. Its
6
6
  // one job is to end with a client identity, which the front desk trades for
7
7
  // an invitation and the avatar knocks with; the grant it issues is the
8
- // box's, `dock/grants.ts`, honoured on every route, and what this file
8
+ // box's, `box/grants.ts`, honoured on every route, and what this file
9
9
  // keeps for itself is only the dance: clients, pending requests and codes.
10
10
  // No secret of Quo's is ever here.
11
11
  //
@@ -18,9 +18,9 @@
18
18
  // The web route finishes a request by calling `complete(request, identity)`
19
19
  // after its allow page, where the human hands the invitation the root minted;
20
20
  // that returns the redirect that carries the code back to the client.
21
- import { REFRESH_TTL, type Grants } from '../dock/grants.ts';
21
+ import { REFRESH_TTL, type Bearers as Grants } from '../box/grants.ts';
22
22
 
23
- export { ACCESS_TTL, REFRESH_TTL } from '../dock/grants.ts';
23
+ export { ACCESS_TTL, REFRESH_TTL } from '../box/grants.ts';
24
24
 
25
25
  // A code, a client id: every key here is a string the far side chose, and a
26
26
  // bare lookup would find `__proto__` and hand back an object with no
@@ -244,7 +244,7 @@ function withQuery(base: string, params: Record<string, string | null>): string
244
244
  return u.toString();
245
245
  }
246
246
 
247
- export async function readBody(req: Request, limit = 64 * 1024): Promise<string> {
247
+ async function readBody(req: Request, limit = 64 * 1024): Promise<string> {
248
248
  const s = await req.text();
249
249
  if (s.length > limit) throw new Error('too large');
250
250
  return s;
package/mcp/route.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  import { OAuth, type Store } from './oauth.ts';
10
10
  import type { McpHttp } from './http.ts';
11
11
  import type { Handler } from '../harbor/http.ts';
12
- import type { Grants } from '../dock/grants.ts';
12
+ import type { Bearers as Grants } from '../box/grants.ts';
13
13
 
14
14
  // The routes' public faces, as the exchange and the metadata name them.
15
15
  export type Routes = { mcp: string; web: string; quo?: string; api?: string };
package/mcp/runner.ts CHANGED
@@ -19,7 +19,7 @@ import { presentation } from '../beings/look.ts';
19
19
  // from the device and never from cells. `turns` is the ceiling of model
20
20
  // calls in one turn of the conversation; ten by default.
21
21
  export type Model = { url: string; model: string; key?: string; turns?: number };
22
- export const TURNS = 10;
22
+ const TURNS = 10;
23
23
 
24
24
  // The wire shapes, only as much as the loop reads and writes.
25
25
  export type Tool = { type: 'function'; function: { name: string; description?: string; parameters: JsonObject } };
package/mcp/server.ts CHANGED
@@ -22,7 +22,7 @@ export const VERSION = '0.0.0';
22
22
  export type { Subject } from '../beings/side.ts';
23
23
 
24
24
  // The empty ask as a tool: her describe whole, asks and notes.
25
- export const DESCRIBE: Tool = { name: 'describe', description: 'the empty ask: her describe, the asks and the notes', inputSchema: { type: 'object' } };
25
+ const DESCRIBE: Tool ={ name: 'describe', description: 'the empty ask: her describe, the asks and the notes', inputSchema: { type: 'object' } };
26
26
 
27
27
  // Her describe, spoken as tools, the empty ask first. Name, description and
28
28
  // input are verbatim; an output schema crosses when she declared one. Her
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nervur-org/dock",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "The dock: what every estate on Quo needs and nobody writes twice. A daemon and the nervur command, the front desk, the user being and the avatar, harbors on disk, in a tab and on the edge, the model sides and the screen.",
5
5
  "keywords": [
6
6
  "quo",
@@ -28,7 +28,6 @@
28
28
  "types": "./dist/harbor/disk.d.ts",
29
29
  "default": "./dist/harbor/disk.js"
30
30
  },
31
- "./cli": "./dist/cli/nervur.js",
32
31
  "./stand": {
33
32
  "types": "./dist/cli/stand.d.ts",
34
33
  "default": "./dist/cli/stand.js"
@@ -79,10 +78,11 @@
79
78
  }
80
79
  },
81
80
  "scripts": {
82
- "build": "rm -rf dist && tsc -p tsconfig.build.json && node build/bundle.ts && chmod +x dist/cli/nervur.js && cp harbor/edge/platform.d.ts dist/harbor/edge/ && cp -R cli/estate dist/cli/ && cp ../../papers/quo-dock.md ../../papers/quo-app-bells.md ../../papers/quo-human.md ../../papers/quo-mcp.md ../../papers/quo-api.md ../../papers/quo-harbor.md ../../papers/GLOSSARY.md ../../papers/WORLDS.md ../../papers/GETTING_STARTED.md .",
83
- "check": "node --test \"test/*.test.ts\"",
84
- "deep": "node --test \"test/terrain/*.test.ts\"",
85
- "prepublishOnly": "test \"$NERVUR_GATED\" = 1 || { echo 'publish from the root, gated once: npm run release:dock' >&2; exit 1; }"
81
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && node build/bundle.ts && chmod +x dist/cli/nervur.js && cp harbor/edge/platform.d.ts dist/harbor/edge/ && cp -R cli/estate dist/cli/",
82
+ "check": "node ../../test.mjs \"test/*.test.ts\"",
83
+ "deep": "node --test --test-timeout=900000 --test-concurrency=1 \"test/terrain/*.test.ts\"",
84
+ "cover": "node --test --test-timeout=300000 --test-concurrency=1 --experimental-test-coverage --test-coverage-exclude=\"**/dist/**\" --test-coverage-exclude=\"**/test/**\" --test-coverage-exclude=\"**/harbor/idb.ts\" --test-coverage-lines=97 --test-coverage-branches=85 --test-coverage-functions=91 \"test/*.test.ts\"",
85
+ "prepublishOnly": "test \"$NERVUR_GATED\" = 1 || { echo 'a publish is /release, from the root, on the human'\"'\"'s word' >&2; exit 1; }"
86
86
  },
87
87
  "dependencies": {
88
88
  "@aparajita/capacitor-secure-storage": "^8.0.0",
@@ -91,9 +91,10 @@
91
91
  "@capacitor/filesystem": "^8.1.3",
92
92
  "@capacitor/geolocation": "^8.2.2",
93
93
  "@capacitor/local-notifications": "^8.3.1",
94
+ "@capacitor/push-notifications": "^8.1.2",
94
95
  "@modelcontextprotocol/sdk": "^1.30.0",
95
- "nervur": "^0.17.0",
96
- "@nervur-org/ui": "^0.3.0",
96
+ "nervur": "^0.19.0",
97
+ "@nervur-org/ui": "^0.5.0",
97
98
  "ws": "^8.21.3"
98
99
  },
99
100
  "devDependencies": {
@@ -107,21 +108,12 @@
107
108
  "files": [
108
109
  "dist",
109
110
  "beings",
110
- "dock",
111
+ "box",
111
112
  "cli",
112
113
  "harbor",
113
114
  "mcp",
114
115
  "human",
115
116
  "api",
116
- "quo-dock.md",
117
- "quo-app-bells.md",
118
- "quo-human.md",
119
- "quo-mcp.md",
120
- "quo-api.md",
121
- "quo-harbor.md",
122
- "GLOSSARY.md",
123
- "WORLDS.md",
124
- "GETTING_STARTED.md",
125
117
  "README.md",
126
118
  "LICENSE",
127
119
  "NOTICE"
@@ -1,138 +0,0 @@
1
- # Getting started
2
-
3
- Quo lets an object ask another object and get an answer, without knowing
4
- whether that other object is in the same process, on the same device, or
5
- on another planet. Three words: a harbor boots wards, a ward keeps beings
6
- and judges its door, and a being is one ordinary object with one voice.
7
- This is the shortest road from nothing to each of the three, for a
8
- stranger with a terminal. `SPEC.md` is the truth behind every sentence
9
- here and assumes nothing; `quo-dock.md` is the dock, the part a box runs.
10
-
11
- Quo is the protocol and Nervur is this kit of it. Every name you install
12
- and every command you type is Nervur's; `quo` stays on the wire, as the
13
- `quo.` route a ward's door answers at.
14
-
15
- Two packages, and you start with the one that fits what you have:
16
-
17
- - `nervur`, the library. A harbor, a ward and a being in one
18
- process, no wire, no files. For a program that wants Quo inside it.
19
- - `@nervur-org/dock`, the dock. A daemon and one command, `nervur`, that
20
- stand a box up: a person's world with a page, a model's side, an api, a
21
- door on the wire. For a box that receives people and models.
22
-
23
- The command is in the dock and not in the library, so `npm install nervur`
24
- gives you something to import and no command, and the command arrives with
25
- `npm install -g @nervur-org/dock`. That is how a kit of this shape is
26
- always named: one unscoped headline name for the library, the parts under
27
- the scope, and the CLI in the package that owns it.
28
-
29
- ## A being, in one process
30
-
31
- ```bash
32
- npm install nervur
33
- ```
34
-
35
- A being is a class with `asks`, the methods anyone may reach, each with
36
- the JSON schema of its input. Everything else on the class is hers alone.
37
-
38
- ```js
39
- import { Being } from 'nervur';
40
- import { Ward } from 'nervur/ward';
41
- import { MemoryHarbor } from 'nervur/harbor';
42
-
43
- class Shop extends Being {
44
- static asks = { price: { input: { type: 'object', properties: { item: { type: 'string' } } } } };
45
- price({ item }) {
46
- return { item, eur: 12 };
47
- }
48
- }
49
- class Customer extends Being {
50
- static asks = {};
51
- }
52
-
53
- const harbor = new MemoryHarbor();
54
- const ward = await harbor.boot('acme', Ward, { Shop, Customer });
55
- await ward.ask('boot', { key: 'shop', class: 'Shop' });
56
- await ward.ask('boot', { key: 'ana', class: 'Customer' });
57
- ```
58
-
59
- The harbor booted a ward named `acme` with two classes it may make beings
60
- of, and the ward's owner, the process itself, booted one of each by key.
61
- The ward's own asks are seven, `boot`, `public`, `invite`, `knock`,
62
- `remove`, `unboot` and `ask`, and `ward.ask()` with no method is her
63
- describe: those asks and her `notes`, the ward's `pk` and her beings.
64
-
65
- Nobody reaches a being she has not invited. An invitation is minted on a
66
- being for one id, the shop's for `ana`, and the customer knocks with it;
67
- from then she holds a standing at the shop, under the name she took, and
68
- the shop holds her as an occupant.
69
-
70
- ```js
71
- const invitation = await ward.ask('invite', { being: 'shop', id: 'ana' });
72
- const ana = harbor.objects.get(harbor.partitions.get('acme').beings.ana);
73
- await ana.knock(invitation);
74
- await ana.take('shop', invitation);
75
- console.log(await ana.standings.shop.ask('price', { item: 'bread' }));
76
- // { item: 'bread', eur: 12 }
77
- ```
78
-
79
- That is the whole protocol: a standing on one side, an occupant on the
80
- other, an ask that rides the relation and an answer that rides it back.
81
- The two beings here share a process; the same lines hold when the shop is
82
- on a box across the sea, because a standing is an address and a key, and
83
- the harbor owns the wire. `harbor.objects` is the memory harbor's hand for
84
- a test and a first program; a being on a real box is reached through her
85
- ward, never held.
86
-
87
- ## A box
88
-
89
- ```bash
90
- npm install @nervur-org/dock
91
- npx nervur init --dir ~/.nervur --ward acme --user ana --domain acme.com --default --show
92
- npx nervur serve --dir ~/.nervur --http 8787
93
- ```
94
-
95
- `init` mints the ward `acme`, boots ana's user being, her doorbell and
96
- the desk in it, marks it the default ward and shows it at the web route,
97
- and writes `routes.json`, the four routes of a box, `mcp.`, `web.`, `quo.`
98
- and `api.` under the domain, for a proxy to map onto the one loopback
99
- port. Without `--domain`, write it yourself; on a Mac the four are paths
100
- at one loopback address:
101
-
102
- ```json
103
- { "mcp": "http://127.0.0.1:8787/mcp", "web": "http://127.0.0.1:8787/web", "quo": "http://127.0.0.1:8787/quo", "api": "http://127.0.0.1:8787/api" }
104
- ```
105
-
106
- `serve` is the daemon, the one process over that folder, and every other
107
- command is its client. From a second terminal, a phone:
108
-
109
- ```bash
110
- npx nervur invite --dir ~/.nervur '{"being":"ana","id":"phone"}'
111
- ```
112
-
113
- The answer carries `link`, the ward's page with the invitation in its
114
- fragment. Opened on the phone, the tab boots a harbor of its own, joins as
115
- that device, and is in: no account, nothing typed. What a model gets is
116
- the same world as tools at `mcp.`, what a program gets is the same asks as
117
- JSON at `api.`, and what another box gets is the door at `quo.`; a being
118
- answers each the same, because each is an ask at her door.
119
-
120
- The box's own doings are rows on the faculties of its dock ward, placed by
121
- the root with `nervur ask --ward dock`: a socket held to another box, an
122
- agent run for a ward, a schedule on the clock. A second box owns this one
123
- across the wire with an invitation on the ward's own pk, knocked with
124
- from there, and every owner command with `--via` from then on. Each of
125
- those, the edge, and what an estate stands beyond the init, is the
126
- "Getting started" chapter of `quo-dock.md`, proven cold on a Mac by
127
- somebody with nothing else to read.
128
-
129
- ## Your own beings on a box
130
-
131
- A box holds the dock's classes and yours. `classes/index.ts` beside the
132
- wards exports each of yours by name, and `nervur boot '{"key":"shop",
133
- "class":"Shop"}'` boots one; `nervur init --class Shop` makes the ward's home
134
- being one of yours, an organisation's ward with the org as its being. A
135
- being's asks may name who may reach them, `for`, over the record of the
136
- occupant asking, and her `cells` are what she keeps between boots: the
137
- whole of what a being is, in `SPEC.md`, and the words above the spec, a
138
- world, a home, a membership, in `WORLDS.md` and `GLOSSARY.md`.
package/GLOSSARY.md DELETED
@@ -1,118 +0,0 @@
1
- # GLOSSARY
2
-
3
- Every word the papers above the spec use, in one place. The protocol's own
4
- words, being, ward, harbor, standing, occupant, invitation, knock and the
5
- rest, are in the glossary of `quo/SPEC.md`, which assumes nothing and
6
- is not repeated here; what the library spells for itself is in
7
- `papers/quo-kit.md`. A word here is a convention of the dock or of an
8
- estate, and changes no word of the spec. `papers/WORLDS.md` is where the
9
- first seven are reasoned; `papers/quo-dock.md` and the papers of its sides
10
- are where the rest are built.
11
-
12
- ## A person and a world
13
-
14
- - **org**: the unit every "you" names: a company, a family, a club, a
15
- service, and at the smallest one human alone, an org of one. An org owns
16
- wards, holds standings, trusts boxes and is trusted. Never an avatar, an
17
- account or a user id; each of those is a thing an org holds.
18
- - **world**: a ward, as a human sees it: one seed, one partition, one body
19
- of code, one owner.
20
- - **home**: the one ward an org owns on a harbor it trusts; the org's own
21
- being is its being. Also that being's standing at the ward's own door as
22
- an owner, `home`. One per org, and an org may have none yet.
23
- - **org being**: home's own being, one per org, running day and night,
24
- holding the org's standings and its members as occupants. An estate's
25
- own class for an organisation; the user being for an org of one.
26
- - **presence**: a ward a person owns on a harbor in her hand, one per world
27
- reached from that device. The tab, the phone and the desk are presences
28
- of the same shape, and nothing in a hand is a home.
29
- - **presence being**: the being of a world who lives on a device, booted in
30
- the presence ward when the world is joined. She lends every faculty the
31
- box has at her birth and carries them, so a world reaches the device only
32
- by asking her.
33
- - **app**: a browser of worlds, one harbor and a presence ward per world,
34
- the same shell on every terrain. It has no account and no person of its
35
- own; which app a person uses is settled at home, and an adopter's app is
36
- only the first one its member meets.
37
- - **membership**: an id in a ward someone else owns. An employee id, a
38
- member id, a customer id. Never a ward of hers.
39
- - **hub**: home, as the one place every relation of hers meets.
40
- - **rule**: home answering one standing by asking another.
41
- - **device**: a harbor in her hand, with a ward of its own holding what that
42
- box can do. One OS user is one harbor, and the app has no people of its
43
- own.
44
-
45
- ## The dock
46
-
47
- - **dock**: what every estate needs and nobody writes twice,
48
- `packages/dock/`, published as `@nervur-org/dock`.
49
- - **dock ward**: the ward a harbor boots and roots for itself, holding the
50
- faculties that box lends. Never a home, and never migrated.
51
- - **estate**: all of one owner's harbors, with what is theirs alone, in a
52
- tree of the owner's own and never published.
53
- - **daemon**: `nervur serve`, the one process on a device that holds a harbor.
54
- - **route**: a hostname a reverse proxy sends to one process: `web.`,
55
- `quo.`, `mcp.`, `api.`.
56
- - **user being**: the org being of an org of one: a human's own, holding
57
- all of her standings. The dock's `User` class, which also serves a
58
- membership the desk makes in another org's ward.
59
- - **front desk**: the being every fresh client meets; hands out invitations
60
- minted by the ward's being, and is what `nervur init` marks public in a
61
- fresh ward. Any ward may mark one being of any class public instead.
62
- - **avatar**: a being that is someone's voice from outside Quo, one per
63
- client identity.
64
- - **side**: the interface an avatar speaks through to someone outside Quo:
65
- a screen, an MCP server, a runner, an agent.
66
- - **client identity**: what a credential exchange establishes; one avatar,
67
- for life.
68
- - **proof**: a vouch the front desk trades once for an invitation: the
69
- daemon's nonce for a local side, or the link a world mailed to an address
70
- it wants proof of. Everyone else arrives with an invitation.
71
- - **way in**: a being who says who a proof of one kind is for, and who sends
72
- the proof out to an address where the kind has one. The desk holds a
73
- standing at each, under `way:<kind>`: the box's, lent by the dock ward,
74
- and the world's own, in the world's ward.
75
- - **carrier**: a being who shows her standings' asks as her own and
76
- forwards. The user being and the device are two.
77
- - **envoy**: a being of the org's standing in for a ward nobody here runs,
78
- one being per vendor and one relation per account, on the base
79
- `beings/envoy.ts`. Her asks out are the vendor's surface, narrowed; her
80
- one ask in, `deliver`, is the owner's, the vendor's bytes reaching her
81
- own body through the api route and never a door; her credential is in
82
- her cells, placed by the owner and shown to nobody; and a delivery is one
83
- ask on the way back her occupant handed her at the knock.
84
- - **faculty**: a being of the dock ward, standing for one thing the box can
85
- do: a timer, a line, a store, a vendor's API. Reached by `lend`, held as
86
- an ordinary standing, and she wakes whoever invited her. She holds what
87
- the device holds and begins holding it at birth.
88
- - **ring**: one platform's way of waking a device by its token, held by the
89
- push faculty: `web`, `apns`, `fcm`. It carries the banner sealed to that
90
- device's banner key, and nothing else.
91
- - **bell**: a ward that wakes devices, with a public being for a phone
92
- nobody knows and one doorbell per device. `papers/quo-app-bells.md`.
93
- - **doorbell**: one device's being at a bell. Its cells hold that device's
94
- platform token, and its occupants are the worlds that device allowed to
95
- wake it, one standing each.
96
- - **porter**: a bell's public being, where a phone nobody knows arrives as
97
- `{}` and comes away with a doorbell of its own.
98
- - **banner key**: the pair a device keeps in the cells of its notifications
99
- faculty. A world seals a notification to its public half; the secret half
100
- opens banners and nothing else, and never the world.
101
- - **timer**: the faculty that reads the box's clock, and the only thing on a
102
- device that may. She wakes whoever invited her, every minute.
103
- - **clock**: the being who wakes beings at a time, on standings the root
104
- placed. She holds the schedules and invited the timer.
105
- - **gate**: `for(occupant, asker)`; the whole permission model.
106
- - **look**: how a being is presented, as values; one optional ask.
107
- - **page**: a being's page as a tree of values in the screen's grammar; one
108
- optional ask, read by a screen alone.
109
- - **owner pilot**: `nervur pilot`, the model side over a ward's owner asks,
110
- reaching the daemon's socket; with `--via`, a ward elsewhere through a
111
- standing. Under cron, the estate's own agent.
112
-
113
- ## The model side
114
-
115
- - **constitution**: the folder and instructions an autonomous agent runs
116
- under; governance, never permission.
117
- - **runner**: an inline MCP client for a model that speaks function
118
- calling; it drives the loop the MCP client would, over one conversation.