@nervur-org/dock 0.3.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 (285) hide show
  1. package/GETTING_STARTED.md +128 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +6 -0
  4. package/README.md +37 -0
  5. package/api/quo-api.md +196 -0
  6. package/api/route.ts +227 -0
  7. package/beings/GLOSSARY.md +118 -0
  8. package/beings/WORLDS.md +567 -0
  9. package/beings/avatar.ts +101 -0
  10. package/beings/carry.ts +159 -0
  11. package/beings/clock.ts +552 -0
  12. package/beings/courier.ts +107 -0
  13. package/beings/desk.ts +128 -0
  14. package/beings/doorbell.ts +129 -0
  15. package/beings/envoy.ts +218 -0
  16. package/beings/index.ts +16 -0
  17. package/beings/join.ts +51 -0
  18. package/beings/link.ts +104 -0
  19. package/beings/look.ts +139 -0
  20. package/beings/porter.ts +43 -0
  21. package/beings/presence.ts +39 -0
  22. package/beings/quo-app-bells.md +280 -0
  23. package/beings/quo-dock.md +1942 -0
  24. package/beings/setup.ts +45 -0
  25. package/beings/side.ts +60 -0
  26. package/beings/user.ts +459 -0
  27. package/beings/way.ts +65 -0
  28. package/cli/bundles.ts +20 -0
  29. package/cli/client.ts +73 -0
  30. package/cli/daemon.ts +581 -0
  31. package/cli/estate/Caddyfile +33 -0
  32. package/cli/estate/quo.service +35 -0
  33. package/cli/estate/systems.quo.plist +44 -0
  34. package/cli/estate.ts +48 -0
  35. package/cli/folder.ts +27 -0
  36. package/cli/oauth.ts +32 -0
  37. package/cli/pilot.ts +38 -0
  38. package/cli/quo-dock.md +1942 -0
  39. package/cli/quo.ts +203 -0
  40. package/cli/stand.ts +160 -0
  41. package/dist/api/route.d.ts +21 -0
  42. package/dist/api/route.js +193 -0
  43. package/dist/beings/avatar.d.ts +32 -0
  44. package/dist/beings/avatar.js +100 -0
  45. package/dist/beings/carry.d.ts +12 -0
  46. package/dist/beings/carry.js +174 -0
  47. package/dist/beings/clock.d.ts +161 -0
  48. package/dist/beings/clock.js +528 -0
  49. package/dist/beings/courier.d.ts +50 -0
  50. package/dist/beings/courier.js +96 -0
  51. package/dist/beings/desk.d.ts +53 -0
  52. package/dist/beings/desk.js +135 -0
  53. package/dist/beings/doorbell.d.ts +78 -0
  54. package/dist/beings/doorbell.js +133 -0
  55. package/dist/beings/envoy.d.ts +54 -0
  56. package/dist/beings/envoy.js +211 -0
  57. package/dist/beings/index.d.ts +14 -0
  58. package/dist/beings/index.js +16 -0
  59. package/dist/beings/join.d.ts +15 -0
  60. package/dist/beings/join.js +42 -0
  61. package/dist/beings/link.d.ts +14 -0
  62. package/dist/beings/link.js +91 -0
  63. package/dist/beings/look.d.ts +30 -0
  64. package/dist/beings/look.js +104 -0
  65. package/dist/beings/porter.d.ts +14 -0
  66. package/dist/beings/porter.js +41 -0
  67. package/dist/beings/presence.d.ts +8 -0
  68. package/dist/beings/presence.js +21 -0
  69. package/dist/beings/setup.d.ts +3 -0
  70. package/dist/beings/setup.js +39 -0
  71. package/dist/beings/side.d.ts +32 -0
  72. package/dist/beings/side.js +37 -0
  73. package/dist/beings/user.d.ts +315 -0
  74. package/dist/beings/user.js +462 -0
  75. package/dist/beings/way.d.ts +19 -0
  76. package/dist/beings/way.js +28 -0
  77. package/dist/cli/bundles.d.ts +5 -0
  78. package/dist/cli/bundles.js +17 -0
  79. package/dist/cli/client.d.ts +15 -0
  80. package/dist/cli/client.js +58 -0
  81. package/dist/cli/daemon.d.ts +50 -0
  82. package/dist/cli/daemon.js +584 -0
  83. package/dist/cli/estate/Caddyfile +33 -0
  84. package/dist/cli/estate/quo.service +35 -0
  85. package/dist/cli/estate/systems.quo.plist +44 -0
  86. package/dist/cli/estate.d.ts +5 -0
  87. package/dist/cli/estate.js +50 -0
  88. package/dist/cli/folder.d.ts +2 -0
  89. package/dist/cli/folder.js +25 -0
  90. package/dist/cli/oauth.d.ts +2 -0
  91. package/dist/cli/oauth.js +32 -0
  92. package/dist/cli/pilot.d.ts +5 -0
  93. package/dist/cli/pilot.js +22 -0
  94. package/dist/cli/quo.d.ts +2 -0
  95. package/dist/cli/quo.js +201 -0
  96. package/dist/cli/stand.d.ts +37 -0
  97. package/dist/cli/stand.js +110 -0
  98. package/dist/dock/agents.d.ts +102 -0
  99. package/dist/dock/agents.js +105 -0
  100. package/dist/dock/apns.d.ts +27 -0
  101. package/dist/dock/apns.js +98 -0
  102. package/dist/dock/console.d.ts +6 -0
  103. package/dist/dock/console.js +20 -0
  104. package/dist/dock/dialers.d.ts +56 -0
  105. package/dist/dock/dialers.js +75 -0
  106. package/dist/dock/faculty.d.ts +24 -0
  107. package/dist/dock/faculty.js +142 -0
  108. package/dist/dock/fcm.d.ts +13 -0
  109. package/dist/dock/fcm.js +77 -0
  110. package/dist/dock/grants.d.ts +41 -0
  111. package/dist/dock/grants.js +99 -0
  112. package/dist/dock/http2.d.ts +4 -0
  113. package/dist/dock/http2.js +68 -0
  114. package/dist/dock/index.d.ts +41 -0
  115. package/dist/dock/index.js +113 -0
  116. package/dist/dock/local.d.ts +13 -0
  117. package/dist/dock/local.js +44 -0
  118. package/dist/dock/location.d.ts +19 -0
  119. package/dist/dock/location.js +11 -0
  120. package/dist/dock/notifications.d.ts +38 -0
  121. package/dist/dock/notifications.js +60 -0
  122. package/dist/dock/push.d.ts +79 -0
  123. package/dist/dock/push.js +132 -0
  124. package/dist/dock/service.d.ts +4 -0
  125. package/dist/dock/service.js +107 -0
  126. package/dist/dock/timer.d.ts +52 -0
  127. package/dist/dock/timer.js +149 -0
  128. package/dist/dock/webpush.d.ts +12 -0
  129. package/dist/dock/webpush.js +144 -0
  130. package/dist/harbor/browser.d.ts +15 -0
  131. package/dist/harbor/browser.js +82 -0
  132. package/dist/harbor/capacitor.d.ts +28 -0
  133. package/dist/harbor/capacitor.js +218 -0
  134. package/dist/harbor/disk.d.ts +27 -0
  135. package/dist/harbor/disk.js +249 -0
  136. package/dist/harbor/edge/alarm.d.ts +10 -0
  137. package/dist/harbor/edge/alarm.js +38 -0
  138. package/dist/harbor/edge/edge.d.ts +54 -0
  139. package/dist/harbor/edge/edge.js +370 -0
  140. package/dist/harbor/edge/exercise.d.ts +7 -0
  141. package/dist/harbor/edge/exercise.js +232 -0
  142. package/dist/harbor/edge/given.d.ts +26 -0
  143. package/dist/harbor/edge/given.js +42 -0
  144. package/dist/harbor/edge/platform.d.ts +39 -0
  145. package/dist/harbor/edge/storage.d.ts +30 -0
  146. package/dist/harbor/edge/storage.js +180 -0
  147. package/dist/harbor/edge/worker.d.ts +17 -0
  148. package/dist/harbor/edge/worker.js +67 -0
  149. package/dist/harbor/files.d.ts +15 -0
  150. package/dist/harbor/files.js +220 -0
  151. package/dist/harbor/http.d.ts +19 -0
  152. package/dist/harbor/http.js +177 -0
  153. package/dist/harbor/idb.d.ts +15 -0
  154. package/dist/harbor/idb.js +0 -0
  155. package/dist/harbor/quo.d.ts +10 -0
  156. package/dist/harbor/quo.js +42 -0
  157. package/dist/harbor/resolve.d.ts +16 -0
  158. package/dist/harbor/resolve.js +17 -0
  159. package/dist/harbor/seal.d.ts +3 -0
  160. package/dist/harbor/seal.js +25 -0
  161. package/dist/harbor/sealed.d.ts +35 -0
  162. package/dist/harbor/sealed.js +162 -0
  163. package/dist/harbor/tauri.d.ts +16 -0
  164. package/dist/harbor/tauri.js +59 -0
  165. package/dist/harbor/ward.d.ts +9 -0
  166. package/dist/harbor/ward.js +15 -0
  167. package/dist/human/app.d.ts +32 -0
  168. package/dist/human/app.js +527 -0
  169. package/dist/human/apps.d.ts +10 -0
  170. package/dist/human/apps.js +29 -0
  171. package/dist/human/dom.d.ts +2 -0
  172. package/dist/human/dom.js +15 -0
  173. package/dist/human/door.d.ts +19 -0
  174. package/dist/human/door.js +92 -0
  175. package/dist/human/guest.d.ts +3 -0
  176. package/dist/human/guest.js +33 -0
  177. package/dist/human/html.d.ts +55 -0
  178. package/dist/human/html.js +451 -0
  179. package/dist/human/local.d.ts +7 -0
  180. package/dist/human/local.js +10 -0
  181. package/dist/human/mark.d.ts +8 -0
  182. package/dist/human/mark.js +47 -0
  183. package/dist/human/move.d.ts +17 -0
  184. package/dist/human/move.js +90 -0
  185. package/dist/human/screen.d.ts +19 -0
  186. package/dist/human/screen.js +119 -0
  187. package/dist/human/style.d.ts +2 -0
  188. package/dist/human/style.js +60 -0
  189. package/dist/human/tab.bundle.txt +4775 -0
  190. package/dist/human/tab.d.ts +15 -0
  191. package/dist/human/tab.js +409 -0
  192. package/dist/human/tree.d.ts +50 -0
  193. package/dist/human/tree.js +157 -0
  194. package/dist/human/wake.d.ts +34 -0
  195. package/dist/human/wake.js +179 -0
  196. package/dist/human/web.d.ts +33 -0
  197. package/dist/human/web.js +184 -0
  198. package/dist/human/worker.bundle.txt +3868 -0
  199. package/dist/human/worker.d.ts +1 -0
  200. package/dist/human/worker.js +81 -0
  201. package/dist/human/worlds.d.ts +24 -0
  202. package/dist/human/worlds.js +99 -0
  203. package/dist/mcp/agent.d.ts +10 -0
  204. package/dist/mcp/agent.js +78 -0
  205. package/dist/mcp/allow.d.ts +20 -0
  206. package/dist/mcp/allow.js +88 -0
  207. package/dist/mcp/http.d.ts +27 -0
  208. package/dist/mcp/http.js +92 -0
  209. package/dist/mcp/oauth.d.ts +91 -0
  210. package/dist/mcp/oauth.js +241 -0
  211. package/dist/mcp/route.d.ts +15 -0
  212. package/dist/mcp/route.js +42 -0
  213. package/dist/mcp/runner.d.ts +45 -0
  214. package/dist/mcp/runner.js +99 -0
  215. package/dist/mcp/server.d.ts +11 -0
  216. package/dist/mcp/server.js +102 -0
  217. package/dock/agents.ts +121 -0
  218. package/dock/apns.ts +138 -0
  219. package/dock/console.ts +23 -0
  220. package/dock/dialers.ts +85 -0
  221. package/dock/faculty.ts +148 -0
  222. package/dock/fcm.ts +103 -0
  223. package/dock/grants.ts +111 -0
  224. package/dock/http2.ts +70 -0
  225. package/dock/index.ts +133 -0
  226. package/dock/local.ts +48 -0
  227. package/dock/location.ts +22 -0
  228. package/dock/notifications.ts +67 -0
  229. package/dock/push.ts +161 -0
  230. package/dock/quo-app-bells.md +280 -0
  231. package/dock/quo-dock.md +1942 -0
  232. package/dock/service.ts +100 -0
  233. package/dock/timer.ts +174 -0
  234. package/dock/webpush.ts +182 -0
  235. package/harbor/browser.ts +97 -0
  236. package/harbor/capacitor.ts +223 -0
  237. package/harbor/disk.ts +237 -0
  238. package/harbor/edge/alarm.ts +48 -0
  239. package/harbor/edge/edge.ts +407 -0
  240. package/harbor/edge/exercise.ts +241 -0
  241. package/harbor/edge/given.ts +106 -0
  242. package/harbor/edge/platform.d.ts +39 -0
  243. package/harbor/edge/storage.ts +179 -0
  244. package/harbor/edge/text.d.ts +15 -0
  245. package/harbor/edge/worker.ts +70 -0
  246. package/harbor/edge/wrangler.toml +30 -0
  247. package/harbor/files.ts +221 -0
  248. package/harbor/http.ts +188 -0
  249. package/harbor/idb.ts +0 -0
  250. package/harbor/quo-harbor.md +850 -0
  251. package/harbor/quo.ts +44 -0
  252. package/harbor/resolve.ts +31 -0
  253. package/harbor/seal.ts +26 -0
  254. package/harbor/sealed.ts +183 -0
  255. package/harbor/tauri.ts +79 -0
  256. package/harbor/ward.ts +28 -0
  257. package/human/app.ts +580 -0
  258. package/human/apps.ts +47 -0
  259. package/human/dom.ts +21 -0
  260. package/human/door.ts +113 -0
  261. package/human/guest.ts +34 -0
  262. package/human/html.ts +490 -0
  263. package/human/local.ts +30 -0
  264. package/human/mark.ts +53 -0
  265. package/human/move.ts +122 -0
  266. package/human/quo-human.md +1042 -0
  267. package/human/screen.ts +136 -0
  268. package/human/style.ts +63 -0
  269. package/human/tab.bundle.txt +4775 -0
  270. package/human/tab.ts +446 -0
  271. package/human/tree.ts +217 -0
  272. package/human/wake.ts +191 -0
  273. package/human/web.ts +259 -0
  274. package/human/worker.bundle.txt +3868 -0
  275. package/human/worker.ts +124 -0
  276. package/human/worlds.ts +127 -0
  277. package/mcp/agent.ts +94 -0
  278. package/mcp/allow.ts +131 -0
  279. package/mcp/http.ts +92 -0
  280. package/mcp/oauth.ts +270 -0
  281. package/mcp/quo-mcp.md +343 -0
  282. package/mcp/route.ts +45 -0
  283. package/mcp/runner.ts +136 -0
  284. package/mcp/server.ts +101 -0
  285. package/package.json +121 -0
@@ -0,0 +1,850 @@
1
+ # Transports
2
+
3
+ This is how a harbor lives on a real device and how two of them meet over a real
4
+ network. It implements the harbor of `quo/SPEC.md` on every terrain and
5
+ adds nothing to it: a harbor boots wards, keeps what they are made of, carries
6
+ bytes to one pk, returns what came back or nothing, never reads them, and
7
+ answers nothing only where it knows nothing was sent. This document names no
8
+ being and no side; it knows seeds, partitions, code, sockets and disks.
9
+
10
+ ## One harbor per device, one ward per world
11
+
12
+ A device runs one harbor. It is what the device already has, offered to Quo,
13
+ and there is one of it because a device has one keychain, one disk and one
14
+ set of processes. Two harbors on one device are a curiosity, not a design.
15
+
16
+ A harbor hosts many wards, and a ward is a world: a seed, a partition and a
17
+ body of code, with an origin. The wards of the device's owner and wards
18
+ whose code came from a stranger are hosted the same way. A native app is
19
+ therefore a browser of worlds: one harbor, a ward per world, each in its own
20
+ runtime, and the screen showing one at a time.
21
+
22
+ A ward is whole only where all three of its parts are. The harbor keeps a
23
+ **ward record** per ward, and it is the harbor's, never the ward's:
24
+
25
+ ```
26
+ ward record
27
+ pk derived from the seed; the key of the record
28
+ seed where it is, in the secret store
29
+ partition where it is, in the data store
30
+ code where the class bodies come from, and which version boots
31
+ ```
32
+
33
+ One of those wards is the harbor's own, the **dock ward**. A harbor with
34
+ anything to lend boots it and keeps its ask pointer instead of handing it
35
+ out, so it is that ward's root, and every faculty of the box is a being in
36
+ it. It is a ward in every other respect: a seed the store keeps, a
37
+ partition that persists, a door judging the thirteen cases. Two things are
38
+ its own. It is never adopted or dropped as a migration, because its beings
39
+ are the box's and die with it. And the harbor cleans up after a ward that
40
+ leaves: it is the root, so when it drops or hands out a ward it removes the
41
+ relations that ward's beings held at faculties, since a faculty's occupant
42
+ rows are not bounded the way a knock record is. A faculty cleans too, from
43
+ her own end, dropping a subscriber she has heard `unreached` from long
44
+ enough; the two catch different failures and neither is redundant.
45
+
46
+ `lend` is the last member of the ground, and it is built per ward: given
47
+ a name, the harbor asks its own root to invite on that faculty and hands
48
+ back the invitation, or null. Which ward may ask for which name is the
49
+ harbor's own decision, taken where the ground is built, and a ward whose
50
+ code came from a stranger is lent nothing. The ground is per ward and per
51
+ harbor, so `lend` reaches this box and no other; a ward that migrates is
52
+ handed the new harbor's, which is why a being asks again at every birth.
53
+
54
+ A harbor need not announce the dock ward's pk on the lines it dials. An
55
+ announce is a claim it chooses to make, and nothing is lost by withholding
56
+ this one: the only way through that door is a heir a faculty minted, and
57
+ the only source of one is `lend`.
58
+
59
+ ## Three parts, three native places
60
+
61
+ The harbor adapts nothing. Every terrain already has a place for secrets, a
62
+ place for data and a place for code, and those are the three the harbor
63
+ needs:
64
+
65
+ | a ward is | Mac, iPhone | droplet | edge | browser tab |
66
+ | ----------- | --------------------- | -------------------------- | ----------------------- | ---------------------- |
67
+ | seed | a file, sealed | `.env` or a secrets file | the platform's secrets | IndexedDB, per origin |
68
+ | partition | a file, sealed | a file on disk | Durable Object storage | IndexedDB, per origin |
69
+ | the key | Keychain | the environment, if any | the platform's secrets | none |
70
+ | code | the app bundle | a folder | the deployed worker | a bundle URL |
71
+
72
+ Envoys read their own secrets from the same secret store as the seed. A
73
+ device has one place for secrets and the harbor is one more user of it.
74
+
75
+ The browser column is the honest weakness: a tab has no secret store for
76
+ bytes, so the seed sits beside the partition, guarded by the origin and
77
+ nothing more. That is the price of a tab, and why a tab is a rented room and
78
+ a native app is not.
79
+
80
+ ### The partition, split or wrapped
81
+
82
+ Inside the partition one branch is secret, the bind table: each standing's
83
+ current and next keys, and the heirs minted and not yet spoken. Everything
84
+ else is data. That is why a side never holds one. What a harbor hands a side
85
+ for a ward it serves is `Hosted`: the door and the ask, the pk, the name, the
86
+ record, a save, and the beings it made, by key and as a list of keys. No
87
+ partition. A side that wants to know which beings a ward has, or which one it
88
+ marks public, asks the ward and reads the describe; `harbor/ward.ts` is that
89
+ read, `beingsOf` and `publicKey`, and every side here uses it. Asking costs
90
+ nothing to write: a ward that wrote nothing has nothing to save.
91
+
92
+ The partition is values only and the harbor may persist it as
93
+ it likes, so it may keep the secret branch apart from the data, by terrain:
94
+
95
+ - **Split.** The bind branch goes to the secret store beside the seed, the
96
+ rest to the data store. Fits a phone with a few relations; keychain items
97
+ are small and a being with a thousand standings has a thousand key pairs.
98
+ - **Wrapped.** The secret store holds one key per harbor; the data store
99
+ holds each ward whole, seed, partition and record as one blob encrypted
100
+ under it. Any size, one secret. The droplet and edge answer, and it
101
+ works on the phone too.
102
+
103
+ The ward never knows any of this. It hands values and gets values back, and
104
+ split or wrapped, one store or two, is the terrain's business. The one thing
105
+ it learns is yes or no: whether what it wrote was kept. A heir given away as
106
+ an invitation is a live credential until it speaks, and on a device other
107
+ apps share that is the argument for the secret store.
108
+
109
+ Wrapped is what is built, and it is one piece, `seal.ts`: AES-GCM under a
110
+ 32-byte key, a fresh nonce each time, nonce and ciphertext together as hex,
111
+ on WebCrypto so it runs wherever a store does. The key is the one secret a
112
+ harbor reads from its terrain's secret store, named `QUO_SEED_KEY` on every
113
+ terrain: a platform secret on the edge, the environment of a daemon on a
114
+ device, handed to it from the Keychain by the app that spawned it. A
115
+ droplet gives no key and its folder stays plain under its file modes. A
116
+ store speaks one form, the one its key decides, and refuses a ward kept in
117
+ the other by name, so a daemon started without its key, or with one
118
+ against a plain folder, fails loudly and boots nothing. Sealing a plain
119
+ folder is a deliberate operator command, never a boot's doing.
120
+
121
+ ### A save the store refuses
122
+
123
+ A save is part of the ask that caused it. At the end of every arrival, after
124
+ the being has answered and before the ward seals the reply, the ward asks the
125
+ harbor to keep what it wrote. The harbor saves and says whether the store
126
+ took it. That is the last moment anything can still be said: a reply sealed
127
+ to an asker's lid cannot be unsaid, and only the ward can seal.
128
+
129
+ A store that refused makes that arrival a failed ask, said with the words a
130
+ failed ask already has, `threw` to a key the door holds and silence to
131
+ everyone else. Never a reason. A full disk is an insides of this device, and
132
+ the far side hears no more of it than it hears of anything else in here.
133
+
134
+ A row the store would not take leaves the being standing at what is kept. The
135
+ harbor knows what that is without asking anybody: it keeps the copy it handed
136
+ the last save that succeeded, row by row, and puts the refused row back to it
137
+ in the ward's memory, in place and behind the cells guard. Her next ask sees
138
+ what the device sees and a smaller write after it is taken. She loses the
139
+ writes that were refused, which is exactly what their askers were told. The
140
+ row is written in place because a booted being holds her cells and her bind
141
+ table by reference: a fresh object in their place would strand her on cells
142
+ nothing keeps. A row the last successful save dropped is a being the store no
143
+ longer keeps, and her row goes with it.
144
+
145
+ The harbor never asks a store what it holds to find that out. The one moment
146
+ it would ask is from inside a save the store has just refused, which is the
147
+ read least likely to be true: the edge answers it with a partition missing its
148
+ head row, and a ward put back from that loses its heirs and answers its own
149
+ occupants with silence. What the copy costs is one copy of the rows of each
150
+ save, taken before the store is asked and kept only if the store takes it,
151
+ plus one copy of the partition when a ward is hosted, so that a being whose
152
+ first write of the run is refused is put back too. No store is read and no
153
+ extra write is made.
154
+
155
+ The fault is counted too, and a box says how many saves it refused wherever a
156
+ box says anything about itself: `/health` on the daemon and on the edge
157
+ carries the count per ward, and a daemon journals one line the first time a
158
+ ward's save is refused. A counter nobody reads is the thing this replaces.
159
+ A harbor that keeps nothing is asked nothing, and a ward running over no
160
+ store answers as though none of this were here. A store that refuses every
161
+ save cannot keep a ward's first write either, so no ward stands on it at all.
162
+
163
+ The reason this is worth a member of the ground: on a terrain where a being
164
+ is one row with a ceiling, a refused save is not a rare disk fault. Past the
165
+ ceiling every save of that being is refused, her memory runs ahead of what is
166
+ kept, and everything written to her afterwards is lost at the next reload
167
+ while every door goes on answering success. A door that says yes to what was
168
+ never kept is the worse fault, and it is the one a counter cannot fix.
169
+
170
+ ### A being's row has a ceiling
171
+
172
+ A being is one row, and every store that keeps a row as one value has a
173
+ ceiling on it. The store refuses the row itself, before the platform is
174
+ asked, and it reads the ceiling from one place beside the rest of what that
175
+ terrain is: two stores disagreeing about the limit is a row kept the day
176
+ before and refused the day after with nothing in the tree saying why. Where a
177
+ terrain publishes no number, its store has no ceiling of its own and a full
178
+ disk is what refuses.
179
+
180
+ The edge is the terrain that publishes one. A SQLite-backed Durable Object
181
+ takes a key and a value of 2 MB together, which is `ROW_CEILING` in
182
+ `harbor/edge/given.ts`, and a being whose cells and bind table pass it is
183
+ refused by `DurableStorage` rather than by the platform. What lives beside a
184
+ ward for values a partition cannot hold is a design of its own: a partition
185
+ holds values, and a holiday video is not one.
186
+
187
+ ### Custody is a lease
188
+
189
+ `quo/SPEC.md` makes custody the harbor's vouch: two harbors over one
190
+ seed are two wards with one pk, and a harbor refuses to boot a ward another
191
+ running harbor holds. The dock makes that a property of the store, not a rule
192
+ people follow: a lease beside the seed in the secret store, taken by the daemon
193
+ that boots the ward, checked before any boot, refused while held. A restored
194
+ backup meets a held lease and does not boot. A second tab on one origin meets a
195
+ lock and is the held page, `papers/quo-human.md` says which, and so does the
196
+ service worker on that origin meet it: it is
197
+ one more claimant of the same lock and holds the harbor only when no page does.
198
+ The edge is a Durable Object, which is the lease built in. None of it stops
199
+ a deliberate copy, and nothing can; it stops every accident, and accidents
200
+ are the whole population of this problem.
201
+
202
+ ### Migration is three moves
203
+
204
+ A ward moves between harbors by moving its three parts: the seed from one
205
+ secret store to another, the partition from one data store to another, and
206
+ the new harbor pointed at the same code. Same seed, same pk, every standing
207
+ anyone holds still points at her. The old harbor deletes its copies first:
208
+ two harbors holding one seed are two wards with one pk, diverging silently.
209
+
210
+ What a move costs is not the three parts, which are bytes, but how many
211
+ peers wrote down where the ward was. A pk is permanent by arithmetic; a
212
+ location is soft state in every other harbor's directory, and only that goes
213
+ stale. So the bill is proportional to who hard-coded it, and there are two
214
+ kinds of ward.
215
+
216
+ A ward whose harbor dials out is free. It announces what it holds when a
217
+ line opens and whenever that changes, the listener proves each claim at the
218
+ door and binds it, and a ward that arrives on a new box is reachable as soon
219
+ as its harbor dials. Nobody is told and no peer acts.
220
+
221
+ A ward that is dialed keeps a second name. Peers hold a hint, pk at a URL,
222
+ in their own stores and across their own restarts, so that URL is a promise
223
+ the same way the pk is. Moving boxes does not break it, because a hostname
224
+ is not a location: DNS and the proxy point it wherever the harbor now runs,
225
+ which is the whole reason the route is a hostname and not an address.
226
+ Changing the URL itself is the move that costs, and it cannot be announced
227
+ to a peer this ward cannot reach.
228
+
229
+ When it must change, what is republished is a hint and never an invitation.
230
+ The relation, its keys and its count are untouched, and the invitation is
231
+ as good as it was: that is why the two travel separately, a link being one
232
+ next to the other. Peers on a rendezvous this ward also dials relearn by
233
+ themselves, since a binding is proven at the door and names no URL; peers
234
+ holding a hint need a new one, and a hint is not a capability, so it may be
235
+ published in the open to all of them at once. Nobody is invited twice.
236
+
237
+ The rule, then: a dialable ward keeps two names and owes the second the same
238
+ care as the first, and a ward that only dials keeps one, which is why a
239
+ laptop, a tab and a phone move home without ceremony and the droplet does
240
+ not.
241
+
242
+ ### Code is a harbor decision
243
+
244
+ A ward's code has an origin: a folder the owner wrote, a bundle a world
245
+ published, a worker deployed. When the origin ships new bodies the harbor
246
+ decides whether the ward reboots on them. A reboot is silent, the ward cannot
247
+ tell old bodies from new, and a body the cells were not written for is an
248
+ absence this run. So the harbor pins what a ward boots on, records it in the
249
+ ward record, and moves it deliberately: on its own for the owner's own code,
250
+ with the owner's word for a world's.
251
+
252
+ ## The daemon, and the two cores
253
+
254
+ There are two harbor cores, and every device runs one of them:
255
+
256
+ - **The node daemon**, `quo serve`. A disk or keychain store, a unix socket
257
+ for the owner, listeners on localhost for the routes. Droplet, Mac, Linux
258
+ box: wherever a home runs.
259
+ - **The browser harbor.** A dialer inside a webview or a tab, an IndexedDB
260
+ store or a sealed store handed in, one socket per world it is connected
261
+ to. A tab, and every app, phone and desk alike, since an app is a
262
+ presence and a presence dials.
263
+
264
+ The two cores meet a ward they cannot open differently, and both are right.
265
+ The core carries on and names it in `unbooted`, because a device with many
266
+ worlds must not lose them all to one. The daemon stops there and says which
267
+ ward and why: a daemon's folder is an operator's, every ward in it is meant
268
+ to be served, and coming up quietly with one missing is that world gone dark
269
+ with nobody told. It is the same rule that makes a sealed folder with no key
270
+ loud rather than a daemon serving nothing.
271
+
272
+ Tauri and Capacitor are packaging, not harbors. Both apps ship the browser
273
+ harbor over a native sealed store, the phone with push and the desk without
274
+ it, and each is its own owner for its one harbor. Neither ships the daemon:
275
+ a home runs on a box that runs day and night, and a laptop is a presence
276
+ like a phone. The edge is the daemon's core over a Durable Object, one
277
+ single-writer object per ward, or it diverges.
278
+
279
+ A wrapped app carries its own bundle and never loads it from a hostname.
280
+ Only a plain tab fetches the bundle from a world's `web.`, because it has
281
+ nowhere else to get it. A wrapped app visits a world's `web.` exactly once,
282
+ in a system browser sheet, to do the credential exchange and come back with
283
+ an invitation; after that it speaks to that world's `quo.` for bytes and
284
+ never to its `web.` again. One app on a device meets many worlds, one ward
285
+ each, and there is never an app per world.
286
+
287
+ ## Listeners and dialers
288
+
289
+ Two kinds of harbor exist in the world, and every pair reduces to them:
290
+
291
+ | terrain | can be dialed | can dial | lifetime | wakes on |
292
+ | ------------ | ------------- | -------- | ----------------------- | -------- |
293
+ | droplet | yes | yes | as long as wanted | itself |
294
+ | edge | yes | fetch | per request | request |
295
+ | desktop app | no | yes | while running | itself |
296
+ | phone app | no | yes | foreground, throttled | push |
297
+ | browser tab | no | yes | while open | itself |
298
+
299
+ A ward on a dialer is reached only if its harbor dialed first and something
300
+ holds that line open. A phone's ask must survive a push wake-up, and a
301
+ device that comes back from sleep tells its dialer so, `wake`, since the
302
+ system closes a sleeping socket without a word; `quo/SPEC.md` says
303
+ what the dialer does with it. A tab is a
304
+ device, and one seed never lives in two tabs.
305
+
306
+ ## Transport by necessity
307
+
308
+ Transport is chosen by necessity and nothing else. Two wards in one harbor:
309
+ no transport, the harbor calls its other door. Both harbors can listen: a
310
+ request, plain HTTP or raw TCP, and a reverse ask is a request the other
311
+ way. One of them cannot listen, behind NAT, in a tab, on a phone: a socket,
312
+ opened by the one that can dial and held by the one that can listen, with
313
+ both sides sending asks over it and a frame id matching replies. There is no
314
+ fourth situation, so there is no fourth reach, and none is ever picked
315
+ because it is nice. A being cannot tell which one carried her ask, and that
316
+ is the proof the choice was free.
317
+
318
+ A connection is a reach, never a harbor, and a ward outlives it. When a
319
+ socket drops, the listener's directory entry for that pk goes with it, the
320
+ listener answers nothing, the dialer's harbor turns that into unreached, and
321
+ the relation is untouched: seed, partition and keys are on the dialer, not on
322
+ the wire. When the dialer reconnects it announces its pks, the listener
323
+ probes each one at its door and rebinds what proved, and the next ask goes
324
+ through under the same keys and the same count. A phone in a tunnel loses
325
+ signal, never identity.
326
+
327
+ Untouched is the relation and not her record of it. An ask that reached
328
+ nobody has still taken its number and staged the key it announced, because
329
+ she must never offer one number twice; the count carries the gap and the far
330
+ door honours any number above its mark. So a tunnel costs a number and a
331
+ rotation, and nothing else.
332
+
333
+ ## Reach, directory, rendezvous
334
+
335
+ A harbor needs two reach kinds and nothing more, which is what
336
+ `quo/SPEC.md` says and what `packages/quo/src/harbor/reach.ts` is:
337
+
338
+ - **request**: a URL. Send bytes, get bytes. Listener to listener.
339
+ - **socket**: a held connection, opened by whichever side can dial, used in
340
+ both directions with a frame id. A WebSocket, a WebRTC channel and a
341
+ native socket are one socket reach.
342
+
343
+ Two runtimes on one device are no third kind. Either they are one harbor,
344
+ and it calls its other door with no transport at all, or they are two
345
+ harbors, and one of them dials the other as any dialer does.
346
+
347
+ Plus one forwarding rule: a pk whose reach is another harbor that holds a
348
+ socket to it. That rule is the **rendezvous**, and with it every dialer is
349
+ reachable: a phone holds one socket to `quo.acme.com`, acme's harbor binds
350
+ the phone's pk to that socket, and a third harbor sending to the phone's pk
351
+ sends to acme, which forwards the same bytes to the same pk. Nobody changes
352
+ a pk or a byte, and an intermediary opens nothing.
353
+
354
+ What a box can see is the wire and only the wire. Its `/health` shows every
355
+ foreign pk and the socket it holds it on, and one phone is one harbor
356
+ holding one socket, so a phone that joined two worlds hosted on that box
357
+ announces both wards together: that is the box hosting both worlds, and
358
+ two worlds on two estates share no socket and have nothing to correlate.
359
+ No being of either world sees any of it, since an occupant sees pks never.
360
+
361
+ What it does learn is worth writing down, because "pks and ciphertext" is
362
+ true and reads as a smaller thing than it is. A harbor forwarding for
363
+ someone else holds, for every ask it carries: the destination ward pk, in
364
+ the clear, because that is the address and there is no forwarding without
365
+ it; the socket it came in on, and so the dialer's own address; the size of
366
+ the ask and of the reply, which are not padded and so move with the
367
+ plaintext; the time each crossed, and which reply belongs to which ask,
368
+ because it awaits the one to answer the other; and, from the announce
369
+ frame, which ward pks are held by one device. So an operator of a
370
+ rendezvous can keep who spoke to whom, when, and how much, for as long as
371
+ it likes, and break no ciphertext to do it. What it cannot learn is the
372
+ relation: the heir is sealed inside the ask, so one standing at a ward
373
+ cannot be told from another, nor followed from one rendezvous to the next.
374
+ Being reachable through someone costs the fact that you spoke. It does not
375
+ cost what you said, nor who you are to the far being.
376
+
377
+ A rendezvous is a listener and nothing more, so there is never one of them.
378
+ A harbor may hold sockets to several and be reached through any: the
379
+ directory is keyed by pk and a bind is per socket, so each listener binds
380
+ the same pks independently. Plurality is the design and not a deployment
381
+ habit, and a world that can only be reached through one listener chose
382
+ that.
383
+
384
+ A line also says which **wire suite** it speaks, one number that is not
385
+ negotiated: in the text frame a socket announces itself with, and as the
386
+ header `quo-suite` on a request. It is never on an ask, so an ask is bytes
387
+ with nothing in front of them, and a harbor still knows before it carries
388
+ anything whether the far side can open what it would send. A side meeting a
389
+ suite it does not know closes the line; a request door answers nothing
390
+ delivered. `quo/SPEC.md` owns the number and says why it exists.
391
+
392
+ The **directory** is the map from pk to reach. The memory harbor stubs it with
393
+ linked peers. A real one is a table the harbor fills however it likes, and the
394
+ one entry it must learn by itself is a dialer's: when a dialer connects it
395
+ announces the ward pks it holds, and the listener binds to that socket each one
396
+ whose door answered its probe; `quo/SPEC.md` says what the probe is.
397
+ After a migration the pk is unchanged, only its reach, and the next connect
398
+ rebinds it.
399
+
400
+ ## The quo. route, as built
401
+
402
+ The reach interface and its two implementations are the library's,
403
+ `packages/quo/src/harbor/reach.ts`, and pass its reach suite here: the request
404
+ and a socket to a daemon in `packages/dock/test/wire.test.ts`, the request
405
+ through the worker inside the edge. The route is one file on every terrain,
406
+ `packages/dock/harbor/quo.ts`, `Request` in and `Response` out, mounted by
407
+ the daemon under `/quo` and by the worker under its harbor's prefix; it asks
408
+ its harbor for one thing, an ask delivered to a pk. The listener half is the
409
+ terrain's own and is not in it: `ws` at the daemon's door in
410
+ `packages/dock/harbor/http.ts`, a socket pair in the edge harbor.
411
+
412
+ ```
413
+ request POST <url>/<pk>, the sealed ask as the body, the sealed reply as the answer;
414
+ <url> is the world's quo. route, which the proxy maps onto the daemon's /quo
415
+ 404 is "no reach for that pk" and comes back as nothing; a line lost after
416
+ sending answers nothing at all, and the ward's bound ends the ask
417
+ at most 1 MiB is read, and a body over it is answered by that same 404: an
418
+ ask too big for this door is nothing delivered, never a silence, so it is
419
+ safe to send again and it will fail again. The number is this daemon's and
420
+ is on no wire; a caller cannot ask for it and is never told which of the
421
+ two a 404 was
422
+ socket a WebSocket at <url>, held by the dialer, used both ways
423
+ text frame { announce: [pk, ...] } the ward pks this side holds
424
+ ask frame [0][id 4][pk 64][bytes] a frame id matches the reply
425
+ reply frame [1][id 4][bytes]
426
+ none frame [2][id 4] nothing was delivered
427
+ directory pk -> reach, filled three ways: a socket a dialer holds, bound once proven
428
+ and unbound at close; a hint from a link, `quo reach <pk> <url>`, kept in
429
+ <dir>/reach.json; and the harbor's own doors, always first
430
+ ```
431
+
432
+ One rule makes the rendezvous: bytes that arrive from the wire go to an own
433
+ door or to a socket this harbor holds for that pk, and never onward by
434
+ request. So a third harbor with a hint that a phone's pk is at acme's
435
+ `quo.` sends there, and acme forwards down the socket the phone holds.
436
+ A row on the dock ward's `dialers` faculty makes a daemon a dialer, placed
437
+ once by the root, `quo ask --ward dock dialers keep '{"url":"https://quo.acme.com"}'`,
438
+ kept in her cells and dialed again at every boot: it holds one socket per
439
+ URL, announces its wards on open, binds the listener's pks, and reconnects
440
+ with backoff from a second to thirty when the line drops. `drop` closes
441
+ the line and forgets it. A pk is 64 bytes on the wire, the ward key and
442
+ the padlock.
443
+
444
+ The proof is `packages/dock/test/wire.test.ts`: the library's conformance suite,
445
+ untouched, against two disk harbors in two folders with HTTP doors on
446
+ loopback, once with every ask a request and once with the second harbor
447
+ dialing the first; down is the reach refused, migrate is the ward folder
448
+ moved and adopted. Then the rendezvous on its own.
449
+
450
+ Two rules the daemon obeys and a reader would otherwise rediscover: a harbor
451
+ writes the partition before it lets a folder go, because a being driven
452
+ in-process changes it without passing a door; and a proxy sends the route
453
+ root as `/quo/`, with the trailing slash, which the door takes.
454
+
455
+ ## The two cores, as built
456
+
457
+ The harbor core is the library's, `packages/quo/src/harbor/core.ts`, and so is
458
+ the dialer, `packages/quo/src/harbor/dial.ts`; `quo/SPEC.md` says what
459
+ they are. Every harbor here extends the core and hands it three things: a
460
+ **store**, the library's interface, with `files.ts` on a disk, plain as three
461
+ files per ward or sealed as one blob when the daemon holds `QUO_SEED_KEY`,
462
+ and `idb.ts` in a tab, each passing the library's store suite, the files
463
+ store in both forms; a **loader**, the code half, a
464
+ module from a folder on a daemon and the bundle in a tab; and a **lease**, a pid
465
+ file on disk and a web lock on the database name in a tab, so a second tab on
466
+ one origin meets the lock and is the held page. The disk harbor, `disk.ts`, is the
467
+ core plus files, the loader and the pid, and every socket dialed to it is held
468
+ by its HTTP door, which is where this terrain's `ws` listener stands; the
469
+ browser harbor, `browser.ts`, is the core plus
470
+ IndexedDB, the built-in beings, the lock and one dialer per world.
471
+
472
+ The harbor folder is the device's and not a package: it has no node_modules
473
+ and is not meant to, and a class file in it imports the library by name like
474
+ any code. So the disk harbor resolves a class file's packages from where the
475
+ dock itself is installed, `resolve.ts`, a module resolution hook registered
476
+ once before the first class loads: a bare name that does not resolve from a
477
+ file outside any node_modules is resolved again from the dock's own place.
478
+ A class sees the packages the daemon sees, the library and the dock's own,
479
+ and one copy of each, so a `Being` in a class file is the daemon's `Being`.
480
+
481
+ A tab's store keeps values through JSON, as a file does, because the ward
482
+ hands its partition out through a guard that structured clone refuses.
483
+ The daemon's `/quo` and `/health` answer any origin, with the preflight a
484
+ binary POST needs, because a tab on one world's `web.` reaching another
485
+ world's `quo.` is the ordinary case.
486
+
487
+ `/health` asks for nothing and tells what this harbor hosts: the wards by
488
+ name and pk, the sockets held, the directory, and the saves its store
489
+ refused, per ward. That is a map of the
490
+ device, so it is an operator's surface and not a route: the proxy maps the
491
+ three hostnames onto their paths and nothing onto this one, and a request
492
+ for it from outside meets a 404 at the proxy. It answers any origin because
493
+ it is on loopback, and the day it is put behind a proxy it needs a reason
494
+ and a door.
495
+
496
+ The proof is `packages/dock/test/terrain/browser.test.ts`, behind
497
+ `npm run deep:dock` beside the library's own browser chapter: the
498
+ conformance suite, untouched, inside a real Chromium, against two browser
499
+ harbors in one tab with two databases, both dialing a Node daemon on
500
+ loopback, so every relation crosses the rendezvous; down is the reach
501
+ refused on both, migrate lifts seed, partition and record from one database
502
+ into the other and boots there, the tab-to-home scene. And the lock: a
503
+ second harbor over one database does not boot.
504
+
505
+ ### One thing a terrain's fetch decides
506
+
507
+ APNs, the ring `dock/apns.ts` is, speaks HTTP/2 and refuses HTTP/1.1.
508
+ Node's `fetch` is undici, which has no HTTP/2 client, so a daemon on Node
509
+ rings Apple through `dock/http2.ts` instead, the same ring over
510
+ `node:http2`. A worker's fetch reaches Apple over HTTP/2 and so does
511
+ Deno's and Bun's, and those terrains use the `fetch` one. What the two
512
+ send is one function in `dock/apns.ts`, the path, the headers and the
513
+ body, because a phone rung two ways is rung the same way twice; what
514
+ differs is the transport and nothing else. The Node one is its own file
515
+ so that a worker bundle never pulls `node:http2` in, which is why nothing
516
+ terrain neutral names it and `dock/index.ts` does not export it. All of
517
+ this is here rather than in the being because a being names no terrain.
518
+
519
+ ## The edge, as built
520
+
521
+ `packages/dock/harbor/edge/` is the core over one Durable Object, and the object
522
+ is the harbor. The platform runs one instance of it at a time, which is
523
+ the lease and the single writer every ward needs. Its storage is the
524
+ store, `storage.ts`: one row per ward, seed, partition and record, with the
525
+ seed sealed by `seal.ts` under a key from the platform's secrets,
526
+ `QUO_SEED_KEY`, so the storage holds ciphertext and the secret store holds
527
+ the one key, as the table above says. The deployed worker is the code, the
528
+ built-in beings and whatever it hands in. It is a listener and never a
529
+ dialer: reached by request at `<origin>/h/<name>/quo`, holding the sockets
530
+ dialers open to it on the platform's own socket pair, the rendezvous for
531
+ them, awake per request and kept awake by a held socket.
532
+
533
+ Its owner door is a route, not a socket, because the platform has no local
534
+ process: the root is whoever holds `QUO_ROOT`, a platform secret, and the
535
+ root's asks arrive as one POST at `<origin>/h/<name>/root` under it, the
536
+ first of them `{ init: { ward, user } }`, the root's setup. That is the
537
+ device's own rule on this terrain, as file permissions are on a disk. The
538
+ first thing a root on the edge does is invite an owner on the ward's pk,
539
+ and from then on the edge is piloted from elsewhere over a standing, with
540
+ `--via`, like any ward.
541
+
542
+ What a side mounted there is given is one shape, `given.ts`, and it arrives
543
+ in two halves because the halves change at different times. The deploy
544
+ carries the tab's bundle and the service worker's, as text modules a
545
+ `type = "Text"` rule in the `wrangler.toml` names, since a built artefact of
546
+ a deploy is not a row a ward keeps. What is worn or configured while the
547
+ worker runs is rows in the object's own storage, written by the root's door:
548
+ the public origins, the apps table and the bell key under `settings`, and the
549
+ design a world wears one row per file. So the four routes run there with
550
+ nothing of them changed. `<origin>/h/<name>/web/<ward>` is a world's page, and
551
+ the routes that page hands its tab are the worker's own until a root names
552
+ public ones; `<origin>/h/<name>/api` is a describe as JSON for a stranger or
553
+ a bearer, and `<origin>/h/<name>/mcp` is the dance and the endpoint, with the
554
+ dance's records kept in the same object under one key.
555
+
556
+ A worker stands a box ward, like every other terrain: the box's state has one
557
+ home everywhere, the dock ward's partition, and a terrain that cannot stand
558
+ that ward does not get a second place to keep box state. What differs by
559
+ terrain is which faculties the ward lends, and `given.ts` holds the edge's
560
+ list with the reason for each name that is missing. It lends two. `grants`,
561
+ the bearers its routes honour, which is a record in her cells and the same
562
+ code here as on a disk. And `timer`, on the object's own storage alarm:
563
+ `Alarm` in `edge/alarm.ts` is the box's timer with the one method that knows
564
+ a terrain replaced, since a `setTimeout` dies with the request and an alarm
565
+ does not. One harbor is one object and every ward of it lives there, so the
566
+ alarm wakes the whole harbor and a tick from the timer to a home's Clock is
567
+ in-process here as it is on a disk. The object's `alarm` handler opens the
568
+ harbor, boots every ward from storage and runs the round, so a home on the
569
+ edge with a sweep at three in the morning wakes for it with no request to
570
+ cause it.
571
+
572
+ The other two are not lent: no place and no screen, since a worker is at
573
+ neither; and no ring, since a ring is signed under this world's own web
574
+ origin and a worker learns its origin from the request and not at boot. A
575
+ faculty the platform has no answer for is not lent, rather than lent and
576
+ hollow.
577
+
578
+ Which ward a caller who names none is at is the terrain's word too. On a disk
579
+ it is the mark an operator wrote in the folder; at the edge it is the one
580
+ world the object holds, and an object holding several says nothing rather
581
+ than guessing.
582
+
583
+ The proof is `packages/dock/test/terrain/edge.test.ts`, behind
584
+ `npm run deep:dock`, in the platform's own runtime on loopback under
585
+ `wrangler dev`: the conformance suite, untouched, inside the platform
586
+ against two edge harbors in one object over two prefixed views of its
587
+ storage, reaching each other by request through the worker; the root door
588
+ refused without the secret and answering under it; the box configured after
589
+ it is deployed, its settings merged and answered and a design file worn and
590
+ taken off; a world's page served out of the deploy's bundles and those rows;
591
+ an invitation traded for a bearer at the program side, an ask through the
592
+ avatar answered with the same JSON a Node daemon answers, and the model side
593
+ challenging a caller with no bearer; a home keeping a one-shot for the next
594
+ minute and being woken for it with nothing asking, the fire written down at
595
+ the minute it named and read back a minute later; and a Node daemon
596
+ dialing the edge, its pk held there, reached by a third harbor through the
597
+ edge, unreached when the line drops. Two platform rules shape it: one object
598
+ may not touch another's storage, so the exercise's two harbors share one
599
+ object the way two tabs share a page; and the platform's crypto refuses a
600
+ Uint8Array over a shared buffer and wants a plain one.
601
+
602
+ The `ws` question the road asked is answered by this terrain: the socket
603
+ reach's framing and the dialer are written on the standard surface and
604
+ hold on every terrain; the listener half is the terrain's own, `ws` on
605
+ Node and the socket pair on the edge, and stays where the terrain is. What
606
+ the library takes, when it takes the reach, is the framing and the dialer.
607
+
608
+ ## The phone, as built
609
+
610
+ `sealed.ts` is the store an app holds on a device in a hand, the phone and
611
+ the desk alike: one key per harbor in the platform's secret store, marked
612
+ this device only so it never travels in a backup, and one sealed file per
613
+ ward, seed, partition and record as one blob, in a folder no backup
614
+ copies. The seal is `seal.ts`, the same piece the daemon and the edge use.
615
+ Opening the store is where custody is decided, since a backup is the one
616
+ copy nobody makes on purpose: a key with no folder is a reinstall, and the
617
+ stale key is deleted; a folder with no key is a restore to another device,
618
+ and the unreadable files are deleted; either way the harbor starts fresh
619
+ and there is never a twin. What a platform hands it is two small seams,
620
+ files and secrets, and the rule is proven once on the two in memory,
621
+ `test/sealed.test.ts`. `capacitor.ts` hands the phone's, the Filesystem
622
+ plugin in the folder iCloud does not copy and a secure-storage plugin over
623
+ the Keychain. The app that holds the store is `app/`, which the dock does
624
+ not know. Its page is `app/phone/entry.ts`, terrain glue and nothing else:
625
+ this harbor, the dock's shell over it, and the link the platform hands the
626
+ app, as the launch URL and then on every link opened while it runs.
627
+
628
+ The harbor on a phone is the browser harbor, `browser.ts`, over that
629
+ store: the browser harbor takes a name, which is an IndexedDB database
630
+ under a web lock, or a store handed in, whose lease is the process that
631
+ holds it, since an app has one harbor and nothing else can open its
632
+ files. `nativeHarbor` in `capacitor.ts` is that harbor booted and armed:
633
+ the App plugin's foreground event tells it `wake`, which tells every
634
+ dialer, because a phone asleep loses its sockets silently and the wake is
635
+ what dials them back.
636
+
637
+ On Android the same store stands on the same two plugins: the key in the
638
+ Keystore-backed secure store, the files in the app's own data folder,
639
+ which the manifest excludes from backup with `allowBackup` false, so a
640
+ restore finds nothing there either.
641
+
642
+ The phone's faculties are its plugins, geolocation and local
643
+ notifications, and `capacitor.ts` is two being classes over them, booted
644
+ into the phone's dock ward and lent by name: a position as latitude,
645
+ longitude, accuracy and time, and a notification shown under a
646
+ title. The platform's permission prompt is the first ask's; the terrain
647
+ grants it ahead, since no script taps a prompt, with `simctl privacy` and a
648
+ set location on iOS, and with `pm grant` and a geo fix on Android.
649
+
650
+ On iOS the notifications faculty also plants the banner key, which is the
651
+ one thing a second process needs and cannot ask a ward for. It goes
652
+ through the same secure store the harbor keeps its own key in: an item
653
+ written there lands in the app's first keychain group, which is the group
654
+ the notification extension is in, so the two meet with nothing written to
655
+ make them meet. A plugin of the app's own could not carry it, because the
656
+ bridge builds the page's plugin headers before it hands control to the
657
+ app, and the list that registers one earlier is a file `cap sync`
658
+ rewrites.
659
+
660
+ A notification is scheduled with `isExactNotification` false, which is not
661
+ the plugin's default. A notification that wants an exact alarm and has no
662
+ permission for one opens the Alarms and reminders settings screen on
663
+ Android 12 and up, and the call never answers until a human comes back
664
+ from it, so the ask goes `late` and nothing says why. Ours is shown now
665
+ and waits for no clock, and the permission it would otherwise want is one
666
+ a store asks an alarm app to justify.
667
+
668
+ The phone's ear is one more function from the same file, `phoneEar`,
669
+ handed to the shell rather than to a class, since the token is the box's
670
+ and no being's; what the human reads after a wake is the Notifications
671
+ faculty's ask, made by the shell through the harbor's own pointer, and no
672
+ second body. The ear registers for
673
+ remote notifications and answers the token, `apns` on one platform and
674
+ `fcm` on the other, and it is never sprung: registering is what puts the
675
+ permission prompt up, and an app that asks the moment it opens is an app a
676
+ human refuses on principle, so it answers null until notifications are
677
+ already allowed, which they are on the phone's own screen. The token
678
+ arrives as an event rather than as an answer, so the registration is
679
+ awaited once and kept, and the shell asks again on every foreground
680
+ because the platform hands out a new one whenever it likes. Which of
681
+ Apple's two push hosts minted it is the build's word and not the token's:
682
+ `phone/build.ts` writes in whether this shell was built pointed at a
683
+ world, which is the same signal that decides the link claim, and a build
684
+ told nothing is the store build that speaks to the production host.
685
+
686
+ The proof is two tests behind `npm run deep:app` on one stage,
687
+ `packages/dock/test/terrain/phone.ts`: `ios.test.ts` inside the real app
688
+ in the iOS Simulator and `android.test.ts` inside it in the Android
689
+ emulator. Each runs the store suite, untouched, and the custody rule
690
+ against a real secret store and a real folder; the whole conformance
691
+ suite over two harbors on native stores, both dialing a daemon on the
692
+ Mac's loopback through the tab's own probe; the wake, the app sent
693
+ behind Settings and brought back, its dialer told and its socket held
694
+ again; and the phone joining the daemon's world on an invitation, then a
695
+ model on the daemon asking the user being where the phone is, reaching the
696
+ phone's being on the standing home holds and her own location faculty
697
+ behind it, and getting where the Simulator or the emulator was put.
698
+ The app is synced with the stage's
699
+ origin as its page, built with xcodebuild or gradle, installed fresh and
700
+ launched with simctl or adb; the
701
+ page loads the bundled exercise, `native.ts`, runs it and posts the list
702
+ back. The device names the Mac `localhost`, the Simulator because it
703
+ shares the Mac's network and the emulator because adb reverses the
704
+ stage's two ports, and it has to be that name: a webview grants WebCrypto
705
+ to a plain-http page only on `localhost`, the one origin it counts secure
706
+ without TLS. Through adb's reversed port a socket that Node dropped stays
707
+ open on the device's side, so the stage answers every request with the
708
+ connection closed and every fetch the page makes carries a timeout. Two
709
+ things the plugins taught, held in the store: the secure store keeps
710
+ JSON, so a value is read with the call that parses; and mkdir refuses a
711
+ folder that exists, recursive or not.
712
+
713
+ ## The desk
714
+
715
+ The desk app is the phone's shell on a desk: the same browser harbor, over
716
+ the same sealed store, showing the same screen, and a presence like the
717
+ phone. `app/tauri/` is the Tauri project, and its Rust core holds three
718
+ small things and nothing else: the files under the app's own data folder,
719
+ which no backup copies, refusing a path that climbs out of it; the keys in
720
+ the OS keychain under the app's service name, this device only; and the
721
+ notification center. `tauri.ts` hands the first two to the sealed store as
722
+ its seams over the core's commands, and its notifications faculty asks
723
+ the third. The window runs `human/app.ts` over that harbor, one ward per
724
+ world, every world dialed, carries the dock's stylesheet in its bundle,
725
+ and tells the harbor it is back whenever the window comes to the front,
726
+ since a desk sleeps and wakes like a phone.
727
+
728
+ The desk's dock ward lends what a desk has: notifications through the
729
+ platform's notification center, and nothing else. Location is what the
730
+ platform gives, which on a desk is nothing, so it is not lent rather than
731
+ lent and dumb. No timer, because a desk holds presences and no home, so no
732
+ Clock lives on it and a Timer there would arm for nobody; a Timer stands
733
+ only on a box that hosts a home. It has no ear, since no store rings a
734
+ desk, so a desk is reached while the app is open and drains what it
735
+ missed when it opens. The one
736
+ window the desk app has is a surface over the DOM like the tab's, and the
737
+ screen runs in it: nothing of the desk app is a daemon, spawns one or
738
+ attaches to one. A person who wants a home on a Mac runs `quo serve` on it
739
+ as an operator, and that is a home like any other and no concern of the
740
+ app's.
741
+
742
+ The desk has no honest link. macOS verifies an associated domain for an
743
+ app on the store and Windows and Linux verify nothing, so a join link on a
744
+ desk opens the browser, and the way a world reaches the desk app is the
745
+ Courier: the tab offers, the desk app takes the offer typed or pasted in.
746
+ The store and its custody rule are proven on the two seams in memory, and
747
+ the desk's seams, the keychain and the folder, are proven live: the app
748
+ built with cargo, coming up cold on the Console on the operator's Mac.
749
+
750
+ ## The link
751
+
752
+ An invitation carries the ward pk of the world that minted it, and not where
753
+ that pk lives. A harbor that has never heard of that world cannot carry the
754
+ first knock. So a link is an invitation next to a **reach hint**, the URL of
755
+ that world's `quo.` route. The being receives the invitation opaque, as
756
+ always; the hint goes to the directory. The link is single-use, because the
757
+ heir dies at the first knock, and whoever opens it first is bound.
758
+
759
+ ## Routes on a daemon
760
+
761
+ ```
762
+ device
763
+ quo serve one process, one harbor, the ask pointer
764
+ quo.sock owner asks; local only; never behind a proxy
765
+ side.sock local sides; local only; never behind a proxy
766
+ 127.0.0.1:8787 the HTTP door, loopback only; routes mount by path
767
+ /health the wards it hosts, by name and pk, and the saves refused; harbor/http.ts
768
+ /mcp the model side; mcp/route.ts
769
+ /web the worlds' pages and the allow page; human/web.ts, mcp/allow.ts
770
+ /quo the socket door: request in, sockets held, the rendezvous; harbor/quo.ts
771
+ /api a describe as JSON, a stranger's or a bearer's; api/route.ts
772
+ reverse proxy
773
+ mcp.example.com -> 127.0.0.1:8787/mcp
774
+ web.example.com -> 127.0.0.1:8787/web
775
+ quo.example.com -> 127.0.0.1:8787/quo
776
+ api.example.com -> 127.0.0.1:8787/api
777
+ ```
778
+
779
+ A route is `Request` in and `Response` out, the shapes every runtime speaks,
780
+ and `packages/dock/harbor/http.ts` is that contract. A handler answering
781
+ nothing declines and the mount tries what comes after it. `listenHttp` is
782
+ the one place Node's own objects appear: its door turns an `IncomingMessage`
783
+ into a `Request` and writes a `Response` back onto a `ServerResponse`, and
784
+ no route names either. So the same route runs on this daemon, on a worker at
785
+ the edge, in a browser's own service worker, on Deno and on Bun, and the
786
+ terrain is the door and never the side.
787
+
788
+ The daemon listens on loopback only, and only when asked, with `--http PORT`
789
+ or `QUO_HTTP`. The proxy faces the world and terminates TLS; the daemon never
790
+ does. `quo estate` writes a systemd unit that runs the daemon as one user
791
+ forever, a launchd agent that does the same on a Mac, and a Caddyfile that
792
+ maps the hostnames onto the one port by path, from the templates in
793
+ `packages/dock/cli/estate/`.
794
+ Unattended, the daemon is that unit: restarted if it dies, the lease
795
+ released on SIGTERM, and every ward rebooted from its folder on the next
796
+ start with relations intact. A Mac is a dialer and not a listener: its
797
+ agent opens no HTTP door, and the routes it holds a socket to are rows the
798
+ root kept on its `dialers` faculty.
799
+
800
+ The socket is the root owner. Owning a droplet from elsewhere is a standing
801
+ at its ward, minted by the root, carried as a sealed ask like any relation.
802
+ There is no `cli.` route.
803
+
804
+ **Where the addresses come from, per terrain.** One convention holds
805
+ everywhere: the four routes are `mcp.`, `web.`, `quo.` and `api.` under one
806
+ domain, over https, and nobody types a hostname four times. On a droplet
807
+ the domain is said once, `quo init --domain acme.com` or `quo domain
808
+ acme.com`, and `<dir>/routes.json` is written by the convention; an estate
809
+ whose names break it edits that file, and it is the one file that stands
810
+ beside the wards by right, since a ward knows its pk and never its
811
+ hostname, which is the proxy's fact. With no `routes.json` the daemon
812
+ serves `/web`, `/api` and `/quo` on its loopback door, names that door in
813
+ pages and links, and opens no model door, since OAuth needs a public
814
+ issuer. The app types nothing: it opens a link, the link carries the
815
+ world's `quo.` hint, and the world's `GET /web/` hands it the harbor's
816
+ whole config. The edge is its own origin, `<origin>/h/<name>/quo`, known at
817
+ deploy. A tab is the origin it was served from, with the `quo` address in
818
+ the page's config script. So a domain is said in one place, on the box,
819
+ and every other terrain learns the addresses from a link or a request.
820
+
821
+ ## Acceptance
822
+
823
+ A harbor here is accepted when it passes the library's conformance suite
824
+ untouched, and by nothing else. Three do: the disk harbor over the wire
825
+ under both reaches, the browser harbor through a daemon, and the edge harbor
826
+ inside the platform's runtime. Every store passes the library's store suite,
827
+ `packages/quo/src/conformance/store.ts`: files in
828
+ `packages/dock/test/store.test.ts`, IndexedDB and the edge's storage inside
829
+ their terrains behind `deep:dock`. Every reach passes the library's reach
830
+ suite, `packages/quo/src/conformance/reach.ts`. The store, the reach and the
831
+ core are the library's, and every harbor here is one of them over its own
832
+ terrain.
833
+
834
+ ## Where the tree stands
835
+
836
+ The one place under this paper where a gap between it and the folders is
837
+ named. Each line is a debt to close, not a note to keep.
838
+
839
+ - **A torn write is read, not refused.** A plain store keeps a ward as
840
+ rows written one after another, and a write interrupted between two of
841
+ them leaves rows that do not belong to one another. The partition
842
+ assembled from them boots; it should refuse to, and say which row is
843
+ wrong, the way a shape it cannot read already does.
844
+ - **A sealed store is one value per ward, and whether it should be rows is
845
+ undecided.** The phone and the desk seal the whole ward under one key, so
846
+ a being with much in her cells costs her neighbours there as she costs
847
+ nobody on a droplet, in a tab or on the edge. Rows there are one seal per
848
+ being and move the custody rule the sealed store is written around, and
849
+ that is a question about what a restore means before it is one about
850
+ speed. The human decides it, and not in passing.