@dcasegr/skein 0.0.0-stage → 0.1.1

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 (176) hide show
  1. package/README.md +522 -2
  2. package/bin/skein-host.js +22 -0
  3. package/bin/skein.js +8 -0
  4. package/dist/client/admin-cli.d.ts +10 -0
  5. package/dist/client/admin-cli.js +370 -0
  6. package/dist/client/admin.d.ts +139 -0
  7. package/dist/client/admin.js +256 -0
  8. package/dist/client/bundle.d.ts +9 -0
  9. package/dist/client/bundle.js +34 -0
  10. package/dist/client/cli.d.ts +44 -0
  11. package/dist/client/cli.js +306 -0
  12. package/dist/client/client.d.ts +161 -0
  13. package/dist/client/client.js +237 -0
  14. package/dist/client/config.d.ts +18 -0
  15. package/dist/client/config.js +31 -0
  16. package/dist/client/conversation.d.ts +62 -0
  17. package/dist/client/conversation.js +113 -0
  18. package/dist/client/raw.d.ts +94 -0
  19. package/dist/client/raw.js +154 -0
  20. package/dist/client/target.d.ts +34 -0
  21. package/dist/client/target.js +158 -0
  22. package/dist/dev/cli.d.ts +8 -0
  23. package/dist/dev/cli.js +248 -0
  24. package/dist/dev/scan.d.ts +15 -0
  25. package/dist/dev/scan.js +173 -0
  26. package/dist/envelope-cbor.d.ts +110 -0
  27. package/dist/envelope-cbor.js +160 -0
  28. package/dist/envelope.d.ts +38 -0
  29. package/dist/envelope.js +101 -0
  30. package/dist/host/arc.d.ts +131 -0
  31. package/dist/host/arc.js +425 -0
  32. package/dist/host/billing.d.ts +102 -0
  33. package/dist/host/billing.js +167 -0
  34. package/dist/host/boot.d.ts +246 -0
  35. package/dist/host/boot.js +643 -0
  36. package/dist/host/cli.d.ts +57 -0
  37. package/dist/host/cli.js +1253 -0
  38. package/dist/host/clock.d.ts +3 -0
  39. package/dist/host/clock.js +8 -0
  40. package/dist/host/control.d.ts +45 -0
  41. package/dist/host/control.js +170 -0
  42. package/dist/host/cron.d.ts +99 -0
  43. package/dist/host/cron.js +196 -0
  44. package/dist/host/deliver.d.ts +53 -0
  45. package/dist/host/deliver.js +251 -0
  46. package/dist/host/deploy.d.ts +29 -0
  47. package/dist/host/deploy.js +55 -0
  48. package/dist/host/door-answer.d.ts +24 -0
  49. package/dist/host/door-answer.js +21 -0
  50. package/dist/host/events.d.ts +89 -0
  51. package/dist/host/events.js +222 -0
  52. package/dist/host/fake-arcade.d.ts +40 -0
  53. package/dist/host/fake-arcade.js +149 -0
  54. package/dist/host/fake-discovery.d.ts +4 -0
  55. package/dist/host/fake-discovery.js +58 -0
  56. package/dist/host/feeds.d.ts +127 -0
  57. package/dist/host/feeds.js +422 -0
  58. package/dist/host/frontdoor.d.ts +92 -0
  59. package/dist/host/frontdoor.js +172 -0
  60. package/dist/host/genesis.d.ts +259 -0
  61. package/dist/host/genesis.js +353 -0
  62. package/dist/host/handles.d.ts +40 -0
  63. package/dist/host/handles.js +88 -0
  64. package/dist/host/hostenv.d.ts +30 -0
  65. package/dist/host/hostenv.js +83 -0
  66. package/dist/host/image-chain.d.ts +127 -0
  67. package/dist/host/image-chain.js +440 -0
  68. package/dist/host/install.d.ts +22 -0
  69. package/dist/host/install.js +139 -0
  70. package/dist/host/instances.d.ts +227 -0
  71. package/dist/host/instances.js +430 -0
  72. package/dist/host/kernel.d.ts +246 -0
  73. package/dist/host/kernel.js +603 -0
  74. package/dist/host/liveness.d.ts +65 -0
  75. package/dist/host/liveness.js +155 -0
  76. package/dist/host/manifest.d.ts +160 -0
  77. package/dist/host/manifest.js +597 -0
  78. package/dist/host/p2p.d.ts +337 -0
  79. package/dist/host/p2p.js +1002 -0
  80. package/dist/host/packet.d.ts +121 -0
  81. package/dist/host/packet.js +562 -0
  82. package/dist/host/plan.d.ts +154 -0
  83. package/dist/host/plan.js +418 -0
  84. package/dist/host/providers.d.ts +238 -0
  85. package/dist/host/providers.js +565 -0
  86. package/dist/host/roster.d.ts +72 -0
  87. package/dist/host/roster.js +172 -0
  88. package/dist/host/router.d.ts +773 -0
  89. package/dist/host/router.js +2389 -0
  90. package/dist/host/signer.d.ts +49 -0
  91. package/dist/host/signer.js +167 -0
  92. package/dist/host/store-sqlite.d.ts +39 -0
  93. package/dist/host/store-sqlite.js +170 -0
  94. package/dist/host/stores.d.ts +43 -0
  95. package/dist/host/stores.js +342 -0
  96. package/dist/host/sync.d.ts +121 -0
  97. package/dist/host/sync.js +467 -0
  98. package/dist/host/testbeef.d.ts +21 -0
  99. package/dist/host/testbeef.js +37 -0
  100. package/dist/host/testhost.d.ts +149 -0
  101. package/dist/host/testhost.js +211 -0
  102. package/dist/host/vcdiff.d.ts +9 -0
  103. package/dist/host/vcdiff.js +210 -0
  104. package/dist/host/wasm-kernel.d.ts +87 -0
  105. package/dist/host/wasm-kernel.js +296 -0
  106. package/dist/host/wasm-worker.d.ts +1 -0
  107. package/dist/host/wasm-worker.js +186 -0
  108. package/dist/peers/cron.d.ts +33 -0
  109. package/dist/peers/cron.js +75 -0
  110. package/dist/peers/infer-main.d.ts +2 -0
  111. package/dist/peers/infer-main.js +74 -0
  112. package/dist/peers/infer.d.ts +163 -0
  113. package/dist/peers/infer.js +367 -0
  114. package/dist/runtime/beef.d.ts +28 -0
  115. package/dist/runtime/beef.js +107 -0
  116. package/dist/runtime/bitcoin.d.ts +36 -0
  117. package/dist/runtime/bitcoin.js +141 -0
  118. package/dist/runtime/cid.d.ts +14 -0
  119. package/dist/runtime/cid.js +51 -0
  120. package/dist/runtime/dispatch.d.ts +92 -0
  121. package/dist/runtime/dispatch.js +142 -0
  122. package/dist/runtime/envelope.d.ts +60 -0
  123. package/dist/runtime/envelope.js +101 -0
  124. package/dist/runtime/heads.d.ts +25 -0
  125. package/dist/runtime/heads.js +37 -0
  126. package/dist/runtime/identity.d.ts +28 -0
  127. package/dist/runtime/identity.js +67 -0
  128. package/dist/runtime/index-store.d.ts +152 -0
  129. package/dist/runtime/index-store.js +715 -0
  130. package/dist/runtime/log.d.ts +62 -0
  131. package/dist/runtime/log.js +104 -0
  132. package/dist/runtime/programs.d.ts +30 -0
  133. package/dist/runtime/programs.js +61 -0
  134. package/dist/runtime/records.d.ts +135 -0
  135. package/dist/runtime/records.js +149 -0
  136. package/dist/runtime/sqlite.d.ts +33 -0
  137. package/dist/runtime/sqlite.js +604 -0
  138. package/dist/runtime/store.d.ts +141 -0
  139. package/dist/runtime/store.js +24 -0
  140. package/dist/runtime/syscalls.d.ts +9 -0
  141. package/dist/runtime/syscalls.js +41 -0
  142. package/dist/runtime/tree.d.ts +60 -0
  143. package/dist/runtime/tree.js +196 -0
  144. package/dist/runtime/types.d.ts +100 -0
  145. package/dist/runtime/types.js +7 -0
  146. package/dist/testapps.d.ts +101 -0
  147. package/dist/testapps.js +275 -0
  148. package/dist/testkit.d.ts +25 -0
  149. package/dist/testkit.js +48 -0
  150. package/dist/wallet.d.ts +35 -0
  151. package/dist/wallet.js +87 -0
  152. package/images/README.md +68 -0
  153. package/images/default/README.md +38 -0
  154. package/images/default/bin/frontdoor.cid +1 -0
  155. package/images/default/bin/frontdoor.json +5 -0
  156. package/images/default/bin/messagebox.cid +1 -0
  157. package/images/default/bin/messagebox.json +8 -0
  158. package/images/default/bin/wallet.cid +1 -0
  159. package/images/default/bin/wallet.json +14 -0
  160. package/images/default/etc/config.json +1 -0
  161. package/images/default/etc/dispatch.json +82 -0
  162. package/images/host/etc/apps.json +8 -0
  163. package/kernel-zig/README.md +1247 -0
  164. package/kernel-zig/web/client.js +65 -0
  165. package/kernel-zig/web/kernel.d.ts +59 -0
  166. package/kernel-zig/web/kernel.js +329 -0
  167. package/kernel-zig/web/store.js +67 -0
  168. package/kernel-zig/web/worker.js +79 -0
  169. package/kernel-zig/zig-out/web/skein-kernel.wasm +0 -0
  170. package/package.json +77 -4
  171. package/wasm/README.md +35 -0
  172. package/wasm/frontdoor.wasm +0 -0
  173. package/wasm/messagebox.wasm +0 -0
  174. package/wasm/resolve.wasm +0 -0
  175. package/wasm/wallet.wasm +0 -0
  176. package/wasm/wire-probe.wasm +0 -0
package/README.md CHANGED
@@ -1,3 +1,523 @@
1
- # Temporary Holding Version
1
+ # skein
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A skein is a deterministic WASI machine over a content-addressed graph. Every
4
+ package that reaches it is appended to its log as received, every step a
5
+ program takes is recorded, and replaying the log rebuilds the same graph,
6
+ byte for byte. What a skein does depends on the apps installed in it, and
7
+ the same history runs under any host: a server, a laptop, a browser tab.
8
+
9
+ This repository is the kernel, the boundary programs, the node host and the
10
+ client. Apps live in their own repositories (below). The public explainer is
11
+ https://skein.nexus; these docs are the reference.
12
+
13
+ ## What it is
14
+
15
+ Three layers:
16
+
17
+ | layer | what | where |
18
+ |---|---|---|
19
+ | **kernel** | the machine (store, log, scheduler, WASI, fuel) and its four tables, plus the signer import (`wallet`). One implementation, Zig over wasmtime; it also builds for the browser | `kernel-zig/` |
20
+ | **apps** | everything else, each a tree of WASI programs installed under its own name: the front door, messagebox, resolve, wallet, chain, static sites, overlays, the shell, chat, your own | `programs/` here (the boundary programs and the wallet); the rest in sibling repos |
21
+ | **hosts** | whatever drives a kernel from outside: transports, providers, a store and the signer. The host judges nothing and routes nothing | `src/host/` (the node host, `skein-host`); `web/kernel/` (the browser host) |
22
+
23
+ The kernel's four tables:
24
+
25
+ | table | holds | written by |
26
+ |---|---|---|
27
+ | objects | blocks by CID (dag-cbor, git-raw, bitcoin, raw) | the kernel's `objects` operation; programs `put`/`keep` |
28
+ | heads | name → root, each with an owner (the app the name is under) | the kernel's `head` operation; programs `advance`, only under their own app's name |
29
+ | dispatch | rows `(transport, address, sender, program)`: boxes, HTTP paths, libp2p topics. A row is the permission | the kernel's `dispatch` operation |
30
+ | address book | key → transport and address; providers are `local` rows | the kernel's `peers` operation |
31
+
32
+ The four admin operations are the kernel's own, taken on admin messages from
33
+ the owner (or a key the owner delegated with another row). So is the
34
+ claim: a skein started from the default image has no owner until a message
35
+ reaches its claim row, and then the kernel writes the sender's admin rows
36
+ (#127: the owner is the claim's sender, never a key in its body) and removes
37
+ the claim row. No program writes a kernel table. An app writes only heads under its own name; reads are
38
+ global by CID; a call to another app hands back CIDs, not data.
39
+
40
+ ## Run a skein locally
41
+
42
+ Needs Node 26 and Zig 0.16.0 through `mise`. The owner's admin messages
43
+ (installing an app, a dispatch row, the address book, a directory into
44
+ `main`) are `bin/skein install|uninstall|routes|grant|peers|host|claim|deploy`:
45
+ the client signs them in its own process with the operator's key
46
+ (`SKEIN_OPERATOR_KEY`, default `~/.skein/operator.key`, which `skein-host
47
+ run` makes) and sends them — over the host's control socket on the host
48
+ machine (`--instance <handle>`), else on one BRC-104 session with the
49
+ instance's origin (#142). A browser wallet sends the same messages from the
50
+ management page. The `bin/skein` client's other commands
51
+ use a wallet reachable over HTTP (default `http://127.0.0.1:3322`; the dev
52
+ setup runs `1sat serve wallet-api`, `scripts/host/wallets.sh`).
53
+
54
+ That HTTP wallet is deny-by-default. The client (origin `skein-client`,
55
+ `SKEIN_ORIGINATOR`) needs these grants
56
+ from the owner's wallet (`scripts/host/grants.sh` grants them for the dev
57
+ stack):
58
+
59
+ | protocol | level | counterparty |
60
+ |---|---|---|
61
+ | `identity key retrieval` | 1 | |
62
+ | `server hmac` | 2 | `self` |
63
+ | `messagebox` | 1 | |
64
+ | `auth message signature` | 2 | the instance's front-door key |
65
+ | `message encryption` | 2 | the instance's front-door key |
66
+
67
+ The front-door key is column 5 of `skein-host list`; a new instance is a
68
+ new counterparty. A missing grant fails the command with the wallet's
69
+ refusal, which names the `1sat permissions grant …` command for it.
70
+
71
+ ```
72
+ git clone https://github.com/shruggr/skein && cd skein
73
+ npm install
74
+ (cd kernel-zig && mise exec -- zig build --release) # kernel-zig/zig-out/bin/skein-kernel; fetches skein-sdk by URL+hash
75
+ bin/skein-host add martha --image default # an instance from the default image: identity derived from ~/.skein/master.key, no owner yet
76
+ bin/skein-host run # the host on :8100; martha at http://martha.localhost:8100 or /@martha
77
+ bin/skein claim --instance martha # the claim, signed with ~/.skein/operator.key: you, its sender, own martha; the claim row goes
78
+ ```
79
+
80
+ The default image (`images/default`, docs/BOOTSTRAP.md) is the same for
81
+ every user skein: the front door, the messagebox, the wallet, and one row
82
+ to the kernel, `claim`, from anyone: the first claim's sender owns it
83
+ (#127), so claim it first thing. The host signs no user's claim; `skein
84
+ claim --messagebox <url>` names where the instance reaches its owner. A
85
+ second claim is refused. It has no apps (#152). After the claim, install
86
+ the chain, git and site apps by message, and root's route `/` to the site:
87
+
88
+ ```
89
+ git clone -q --branch v0.6.0 https://github.com/shruggr/skein-chain ../skein-chain
90
+ git clone -q --branch v0.2.0 https://github.com/shruggr/skein-git ../skein-git
91
+ git clone -q --branch v0.10.0 https://github.com/shruggr/skein-site ../skein-site
92
+ bin/skein install ../skein-chain --instance martha
93
+ bin/skein install ../skein-git --instance martha
94
+ bin/skein install ../skein-site --instance martha
95
+ bin/skein routes add --prefix --filters site.get --settings '{"root":"www"}' / --instance martha
96
+ ```
97
+
98
+ Installing a newer version the same way upgrades the app and keeps its
99
+ state (docs/APPS.md, "Reconfiguration").
100
+
101
+ To have apps at birth, boot from an image directory that lists them: the
102
+ default image's `bin/` and `etc/` with an `etc/apps.json` naming each app
103
+ as a directory (relative to the image) or a release tarball by its tree's
104
+ git id (images/README.md):
105
+
106
+ ```json
107
+ { "install": [
108
+ { "url": "https://github.com/shruggr/skein-chain/archive/refs/tags/v0.6.0.tar.gz", "hash": "13a517e964c4785330d8b7ef77796e149657cbbb" },
109
+ { "url": "https://github.com/shruggr/skein-git/archive/refs/tags/v0.2.0.tar.gz", "hash": "0894fefd71d7aa1739d77b9d503421b4d55e497a" },
110
+ { "url": "https://github.com/shruggr/skein-site/archive/refs/tags/v0.10.0.tar.gz", "hash": "2a7b9a492beec6c1c865fc9e33628e18bd36d312" } ],
111
+ "routes": [ { "transport": "http", "address": "/", "prefix": true, "filters": ["site.get"], "root": "www" } ] }
112
+ ```
113
+
114
+ ```
115
+ bin/skein-host add martha --image ./my-image
116
+ ```
117
+
118
+ A plain `skein-host add martha` (no `--image`) is code genesis, with the
119
+ owner in it: `SKEIN_OWNER`, else `~/.skein/owner.identity` (the owner
120
+ wallet's identity key, one line of hex). The tests and the dev stack use it.
121
+
122
+ - State lives in `$SKEIN_HOME` (default `~/.skein`): `master.key` (the one
123
+ secret every instance key derives from), `host.db` (instances, queues, the
124
+ fuel ledger), `instances/<handle>/runtime.db` (each instance's store),
125
+ `logs/host.log`.
126
+ - BRC-169 on the host's own origin (`SKEIN_ROUTER_ORIGIN`) is the host
127
+ skein's onboarding app (shruggr/skein-onboard, #113): the router carries
128
+ `/manifest.json`, `/.well-known/metanet-handles/resolve` and `/search`,
129
+ `/bsvalias/id/…`, `POST /account/register` and `POST /account/profile`
130
+ into the host skein. The app registers handles (a mailbox instance from
131
+ the instance manager, signed over `register <name>@<domain>`), has the
132
+ host's certifier (a child of `master.key`, never in an instance) sign each
133
+ handle certificate under a serial of its own, records every issue, and
134
+ answers resolve, search and the manifest from those records. The handle
135
+ domain, the host's name and note are the app's config, written at the
136
+ host skein's birth from the host's settings (below).
137
+ Revocation is not implemented (the host has no wallet): the certificate's
138
+ revocation outpoint is BRC-52's disabled sentinel.
139
+ - Code genesis (no `--image`) is written at its first start: the owner, the
140
+ owner's admin rows, the boundary programs (front door, messagebox,
141
+ resolve) and their rows, and the host's providers in its address book.
142
+ It has no shell and no chat loop: those are apps, installed next. `skein-host add <h> --boot <dir>` boots
143
+ from a system tree instead (docs/BOOTSTRAP.md). A new log format means a
144
+ new store: move `runtime.db` aside and start again.
145
+ - The full dev stack (owner and inference wallets, mailbox instances, address
146
+ books, the inference peer) is `scripts/host/up.sh`; see
147
+ `scripts/host/README.md`.
148
+ - The host page is http://127.0.0.1:4600 (every instance, a link to its
149
+ origin). A skein from the default image serves its management page, the
150
+ explorer included, at its origin. `bin/skein-dev log | ls | show <cid>` and
151
+ `kernel-zig/zig-out/bin/skein-kernel dump | replay | fuel <store>` read a
152
+ store from the command line.
153
+
154
+ ## Run a host
155
+
156
+ ### Two shapes: the host loads the kernel, or runs it as a process
157
+
158
+ The kernel is one zig source built two ways (kernel-zig/README.md, "The
159
+ machine and the native host"). The node host runs either, chosen by an
160
+ instance's store:
161
+
162
+ | store (host.db `store`) | the machine | who uses it |
163
+ |---|---|---|
164
+ | `sqlite:<path>` — a set of the host's ONE SQLite database (`SKEIN_STORE_SQLITE`, default `$SKEIN_HOME/store.sqlite` when no native kernel is installed) | the **wasm kernel** (`kernel-zig/zig-out/web/skein-kernel.wasm`), loaded in the host's own process: one machine per active instance on a worker thread, none for an idle one; the host chain held by one more machine; storage node's built-in `node:sqlite` | the npm package (`npx skein-host`, `npx open-exchange`): node alone, any platform node runs on, no toolchain |
165
+ | a file, `lmdb:<dir>` (`SKEIN_STORE`), `rocksdb:<dir>` (`SKEIN_STORE_ROCKSDB`) | the **native kernel** (`kernel-zig/zig-out/bin/skein-kernel`): `serve` per instance, or the one `host` process over RocksDB | a checkout with the kernel built — the hosted shape (the rack) |
166
+
167
+ The same log gives the same state on both: `kernel-zig/equiv/wasm-host.ts`
168
+ replays every store the wasm host writes with the native `skein-kernel
169
+ replay` and compares the state CIDs.
170
+
171
+ The package: `npm pack` (its `prepack` builds `dist/` with tsc and the wasm
172
+ kernel with `zig build web`); it carries no native binary. `bin/skein-host.js`
173
+ and `bin/skein.js` run `dist/` (or a checkout's `src/`).
174
+
175
+ A host runs skeins for others too. It has one of its own, the **host
176
+ skein**: the operator's instance, where the host keeps what is state or
177
+ conversation (docs/ARCH.md, "The host skein").
178
+
179
+ ```
180
+ # ~/.skein/host.env — the host's settings: every SKEIN_* line; the environment wins
181
+ SKEIN_ROUTER_ORIGIN=https://id.example.com
182
+ SKEIN_INSTANCE_ORIGIN=https://{handle}.example.com
183
+ SKEIN_HANDLE_DOMAIN=example.com
184
+ SKEIN_HOST_NAME="Example host"
185
+ SKEIN_HTTP=fetch
186
+ ```
187
+
188
+ ```
189
+ bin/skein-host run # the first run: operator.key, master.key, host.db and the host skein; then the host
190
+ ```
191
+
192
+ - `run` reads `$SKEIN_HOME/host.env` (every `SKEIN_*` line; the environment
193
+ wins). On its first run it makes the operator's key
194
+ (`SKEIN_OPERATOR_KEY`, default `$SKEIN_HOME/operator.key`; a file you put
195
+ there is used instead: hex or WIF, mode 0600) and the **host skein**
196
+ (handle `host`) from the host image — the default image with the
197
+ onboarding app (shruggr/skein-onboard v0.4.0, its release tarball,
198
+ fetched and checked by its tree) — its genesis naming the operator's key
199
+ as its owner: its admin rows, its explorer row and the onboarding app's
200
+ routes are there at birth, with no claim. The onboarding app's
201
+ config comes from the settings (`domain` from `SKEIN_HANDLE_DOMAIN`, else
202
+ the domain of `SKEIN_ROUTER_ORIGIN`; `origin`; `name` from
203
+ `SKEIN_HOST_NAME`; `note` from `SKEIN_HOST_NOTE`). It prints the host
204
+ skein's identity and URL, then serves. Later runs start what exists; a
205
+ changed host.env does not reach into the host skein (`skein install
206
+ <a skein-onboard checkout> --instance host --config <file.json>` changes
207
+ the app's config). `skein-host init` is the first run's part alone. Its
208
+ address book alone names the **instance manager**, the host's provider
209
+ that creates, starts and stops instances; the manager acts for no one
210
+ else.
211
+ - `skein-host grant <your wallet's identity key> --apps` gives a browser
212
+ wallet the host skein's admin rows and its apps' owner rows (signed with
213
+ the operator's key), so the management page manages the host skein too.
214
+ - With the onboarding app installed, anyone with a wallet gets a skein: a
215
+ BRC-104 session to the host skein and `POST /onboard/call {"fn":
216
+ "onboard.create", "args": {"handle": "alice", "claim": …}}`, `claim` the
217
+ wallet's own signed claim naming no recipient (#127; skein-onboard's
218
+ README has its shape). The answer, on the same connection, is `{handle,
219
+ identity, url}`: a new instance from the default image, its claim
220
+ forwarded by the instance manager as its first entry — owned by the
221
+ signer, already claimed when its URL first answers. Its owner installs apps into it from there (next sections). A
222
+ handle that is taken is refused (409).
223
+ - **The management page.** Open the host skein's origin
224
+ (`http://host.localhost:8100/`) with a BRC-100 wallet in the browser: it
225
+ creates a skein for your key through that route and opens the new
226
+ skein's own copy of the page. Nothing is written into your wallet: the
227
+ page lists your skeins from the host (`GET <router>/skeins?key=<your
228
+ identity key>`, the skeins whose head `grants` names your key root). There it reads
229
+ the skein through its explorer and installs apps: the git app from the
230
+ image, then any app by repository URL and commit id — you see the rows each
231
+ asks for before your wallet signs. Deploy by hash needs the host to carry
232
+ requests beyond itself: run it with `SKEIN_HTTP=fetch`. Every skein from
233
+ the default image serves the same page (shruggr/skein-site).
234
+ - **A handle from the page** (#103). On the same page, "Register a handle"
235
+ asks the host for `<name>@<domain>` for your key (the domain is the
236
+ router's host name, `id.skein.nexus` in production; the page finds the
237
+ router through `/.well-known/skein-host`, which the router answers at every
238
+ skein's origin): your wallet signs the request, the host creates your
239
+ mailbox instance and answers with the BRC-52 handle certificate, and your
240
+ wallet keeps it (`acquireCertificate`). "Your handles" lists those
241
+ certificates (`listCertificates`) and the mailbox each resolves to. The
242
+ labels `id` and `host` and every instance's handle are refused.
243
+ - **Your profile** (#104). Each of your handles has a Profile form: a name
244
+ and an avatar (an image inscription, by outpoint). Your wallet signs the
245
+ profile record (the OpNS one: DAG-CBOR `{domain, name?, avatar?}`), and
246
+ the page writes it to your mailbox instance as its owner. The host serves
247
+ it with the handle when it is resolved or found; the page shows it as
248
+ signed by the handle's key. "Find a handle" searches the host by handle
249
+ or name.
250
+ - **One key for every instance** (#160, #161, #162). A host can be
251
+ provisioned with ONE private key instead of the master key:
252
+ `SKEIN_KEY_FILE=<path>` (64 hex digits, read and never made) or
253
+ `SKEIN_KEY=<hex>`; set together with `SKEIN_MASTER_KEY` /
254
+ `SKEIN_MASTER_KEY_FILE` the host refuses to start. Every instance then
255
+ signs with that key directly (`skein-host add` records the same identity
256
+ on every row), and every instance's libp2p peer key is the key's one child
257
+ ([2, "skein instance"], key ID `libp2p`): a program that asks its signer
258
+ for `libp2p:<handle>` gets it, whatever the handle. One libp2p node per key
259
+ carries all of them: gossip goes to every instance that takes the topic
260
+ (accepted if any accepts); an inbound stream goes to the instance its first
261
+ frame's `topic` names (a frame naming none: the one instance declaring the
262
+ protocol, else `{kind: "error", message: "topic required"}`). Unset: the
263
+ master key's derivation per instance, one node per instance, as before.
264
+ - **Born owned** (#164). A host program on skein's host as a library calls
265
+ `Router.createInstance(handle, owner, {image, owned: true})`: an image
266
+ instance whose genesis names `owner` root, as the host skein is born —
267
+ no claim, no claim route (the CLI has no flag for it).
268
+ - **Path-only handles** (#161). A token id `<txid>_<vout>` is a handle too:
269
+ its instance has no hostname and is reached at `/@<txid>_<vout>/…` on the
270
+ host's origin (its `url` says so). An overlay request at the bare host —
271
+ `POST /submit` (its `X-Topics`) or `POST /lookup` (its `service`
272
+ `ls_color_<id>`, or a `topic` `tm_color_<id>`) — goes to the enabled
273
+ path-only instance whose handle is that `<id>`, so a wallet keeps one
274
+ provider URL; topics naming two instances, or one here and one not, are 400.
275
+ Any other name (#137: `tm_color`, `ls_color`, …) goes to the
276
+ enabled instance whose row names it in its `topics` (host.db: what the
277
+ instance manager's `create` or `start` named), whether or not it has run
278
+ yet; two instances naming the same name are 400. Nothing maps: the
279
+ hostname's instance (or the bare host) as before.
280
+ - **An instance creates instances** (#162). `SKEIN_MANAGER_FOR=<handle>[,…]`
281
+ gives those instances the instance manager in their address book at birth
282
+ (as the host skein has it) and has the manager take their messages: `create
283
+ {handle, image?, config?, claim?, beef?, topics?}`, `start {handle,
284
+ topics?}`, `stop {handle}`. `topics` (#137): the overlay names, gossip
285
+ topics and lookup services alike (`["tm_color", "ls_color"]`),
286
+ the host routes to the instance at the bare host — plain non-empty
287
+ strings, no duplicates; stored with its row; a `start` naming them
288
+ replaces the row's, one naming none keeps them.
289
+ `image` is `default`, `mailbox`, or an image by name: the directory
290
+ `$SKEIN_HOME/images/<name>` (`SKEIN_IMAGES_DIR` names another directory;
291
+ the layout `skein-host add --image <dir>` takes, a link to an app
292
+ repository's `image/` included). `config` is the new instance's apps'
293
+ config at birth, `{<app>: {…}}`, merged as an install's `--config`. On a
294
+ one-key host a create with no `claim` is claimed by the host's own key
295
+ (its owner), so the instance is live at once; on a master-key host the
296
+ owner's signed claim stays required. `beef` (skein-amm#4): an Atomic BEEF
297
+ (bytes) the host hands into the new instance once it is claimed, published
298
+ and following headers — into its receive row `/skein/overlay/beef/1.0.0`
299
+ (the host's own entry, never served to peers), as a fetched BEEF is, from
300
+ the host's own peer; the host names
301
+ no topic (the instance's engine judges it under the topics its config
302
+ declares). The answer gains `delivered: {verdict, reason?}`; a BEEF the
303
+ door turns away (unmined, a bad BUMP) is reported there, not retried, and
304
+ the instance stays created. `stop` also takes the instance off its
305
+ libp2p node (its beacons stop beating); `start` declares it again.
306
+ - `scripts/host/up.sh` brings up the dev stack.
307
+ - Block headers for the chain app: run the host with
308
+ `SKEIN_HEADERS_URL=http://127.0.0.1:8083/chaintracks/v2/tip/stream`
309
+ (Arcade's chaintracks); every instance with the chain app installed is
310
+ subscribed to it (scripts/host/README.md, the feeds). The host fills the
311
+ default image with the whole header chain from that chaintracks' history
312
+ at start and appends every header the feed brings, so a new skein is born
313
+ with the chain (#132, docs/BOOTSTRAP.md, "The image's chain part").
314
+
315
+ ## Talk to it
316
+
317
+ An instance is an HTTP server at its own origin. Every request is appended
318
+ as received and the instance's front door (a program) runs BRC-103/104 on it
319
+ inside the VM; the host holds the connection until the request's thread
320
+ comes to rest, and the answer is signed on the session.
321
+
322
+ ```
323
+ bin/skein install shell --instance martha # the shell app (from martha's catalog, cloned by her git app): box shell/run
324
+ bin/skein install chat --instance martha # the chat app: box chat
325
+ bin/skein whoami
326
+ bin/skein import ~/some/dir # tree objects into the kernel's objects operation; prints the tree CID
327
+ bin/skein run --tree <cid> -- 'ls | head -3' # box shell/run (the shell app: the shell over a tree)
328
+ bin/skein inbox --wait # the answer, from your mailbox instance
329
+ bin/skein chat --new --wait 'what is here?' # box chat (the chat app; needs the inference peer, bin/skein-infer)
330
+ ```
331
+
332
+ - In a browser: a skein from the default image serves the management page
333
+ at its origin (`/`). It talks to that skein, and to every skein on its
334
+ host whose grants name your key root, with the same requests as the commands below,
335
+ signed by your wallet. Its Inbox lists a box of any mailbox you give it
336
+ (by default the mailbox your handle certificate resolves to) and syncs its
337
+ `metanet_inbox` into that wallet with `@1sat/actions`' `syncMetanetInbox`.
338
+ - Messages are BRC-33 (`/sendMessage`, `/listMessages`,
339
+ `/acknowledgeMessage`) on a BRC-104 session, bodies dag-cbor
340
+ (`application/cbor`, BRC-231) or JSON. A message's id is the CID of its
341
+ record; an answer names it in `replyTo`.
342
+ - What an instance sends you goes to your mailbox instance (an instance with
343
+ only the front door and the messagebox): a registration makes one (#113),
344
+ or `skein-host add <h> --mailbox --owner <key>` on a host with no host
345
+ skein.
346
+ - libp2p: an instance with libp2p rows gets a node in the host; topic
347
+ messages and stream frames go through the same front door.
348
+ - App routes live under `/<app>/` (an overlay's `/<app>/submit`,
349
+ `/<app>/lookup`); `{fn, args}` to an app's box calls a function.
350
+
351
+ The wire contract is docs/MESSAGES.md.
352
+
353
+ ## Install an app
354
+
355
+ ```
356
+ bin/skein install <name> --instance martha # a name from martha's catalog (her site's www/catalog.json)
357
+ bin/skein install https://github.com/<owner>/<repo>#<commit> http://martha.localhost:8100 # any repository and commit, at an origin
358
+ bin/skein install <name> --instance martha --dry-run # the prompt and the messages, nothing sent
359
+ bin/skein uninstall <app> --instance martha
360
+ ```
361
+
362
+ `install` prints the prompt — what the app asks for (its head, its rows,
363
+ its reads, its start) — and sends the owner's messages. A repository is
364
+ installed **by hash**: one message to the skein's git app, `{fn:
365
+ "git.clone", args: {url, hash}}`; the app fetches that one commit through
366
+ the host's fetch provider (the host runs with `SKEIN_HTTP=fetch`), checks it
367
+ against the hash, keeps its tree in the store and answers `{tree, app}`
368
+ (the app record, built in the VM). The client plans from the stored tree and
369
+ sends `head` (`<app>/app` → the app record), one `dispatch` per row, and the
370
+ app's `start` — no `objects`: the blocks are already there. Installing
371
+ again is the upgrade. A directory (`skein install <dir>`) is read on your
372
+ machine and the objects the skein lacks are sent. The management page does
373
+ the same from your wallet. docs/APPS.md §3.
374
+
375
+ ## Build an app
376
+
377
+ An app is a tree with `etc/app.json`. A complete minimal one is
378
+ `programs/test/app-demo/`: a counter with one interface, a box, a cron tick
379
+ and an HTTP route. Its manifest, with the `peek` function and the
380
+ description left out:
381
+
382
+ ```json
383
+ {
384
+ "kind": "app",
385
+ "name": "app-demo",
386
+ "version": "0.1.0",
387
+ "programs": { "demo": "bin/app-demo.wasm" },
388
+ "provides": [{ "interface": "demo.counter/1", "functions": {
389
+ "get": { "writes": false, "args": {}, "answer": { "count": "int" } },
390
+ "add": { "writes": true, "args": { "by": "int", "note?": "string" }, "answer": { "count": "int" } } } }],
391
+ "requires": [],
392
+ "dispatch": [
393
+ { "address": "app-demo", "sender": "*", "program": "demo" },
394
+ { "address": "tick", "sender": "$cron", "program": "demo" },
395
+ { "transport": "http", "address": "/call", "sender": "session", "program": "demo", "fn": "call" }
396
+ ],
397
+ "start": { "body": { "kind": "app-demo-start" } },
398
+ "stop": { "body": { "kind": "app-demo-stop" } }
399
+ }
400
+ ```
401
+
402
+ - **The name rule.** The app's programs advance only heads under
403
+ `app-demo/…`; its record is `app-demo/app`, its own state the record's
404
+ `state` link.
405
+ - **Rows.** `transport` defaults to `mailbox` (a box). An `http` address is
406
+ relative to `/<app>/`. Senders: `*`, `event`, `session` (http), `$owner`,
407
+ `$self`, `$<provider>` (`$cron`, `$status`), or a key in hex.
408
+ - **Programs** are WASI preview1 modules or WASI 0.2 components. In Zig,
409
+ depend on skein-sdk by tag:
410
+
411
+ ```
412
+ zig fetch --save=skein_sdk https://github.com/shruggr/skein-sdk/archive/refs/tags/v0.7.2.tar.gz
413
+ ```
414
+
415
+ and dispatch `{fn, args}` with the SDK's `app.serve`
416
+ (`programs/test/app-demo/main.zig`). Build with `zig build` for
417
+ `wasm32-wasi` and commit the module under `bin/`.
418
+
419
+ docs/APPS.md is the specification (the manifest fields, install, calling an
420
+ app, the security model, worked examples for an overlay and the chain app).
421
+
422
+ ## Write an overlay
423
+
424
+ An overlay is an app built on shruggr/skein-overlay: the engine program plus
425
+ your topic managers and lookup services, wired from `config.overlay` in its
426
+ manifest, serving BRC-22/24 at `<base>/submit` and `<base>/lookup`, its
427
+ BRC-23 base URL `https://<handle>.<host>/<app>` (`/@<handle>/<app>` on the
428
+ host's origin without wildcard DNS). It
429
+ needs the chain app installed (`requires: ["chain/1"]`). The engine exports
430
+ the `topic`, `lookup` and `sk` Zig modules to build against; its README and
431
+ docs/OVERLAY.md there have the walkthrough.
432
+
433
+ ## Build and test
434
+
435
+ ```
436
+ cd kernel-zig && mise exec -- zig build # first: the host tests run kernel-zig/zig-out/bin/skein-kernel
437
+ cd kernel-zig && mise exec -- zig build test # the kernel's unit tests
438
+ npm test # the host, client, formats (node --test src/**/*.test.ts)
439
+ TMPDIR=/tmp/sk kernel-zig/equiv/run.sh # equivalence: shell, git, replay exactness, apps, browser, the wasm host
440
+ SKEIN_TEST_STORE=sqlite npm test # the host suite with every test host on the wasm kernel
441
+ ```
442
+
443
+ - Gotcha: after a kernel change, a stale `zig-out/bin/skein-kernel` makes
444
+ `npm test` fail with "kernel probe: exited (1)" and can hang the host
445
+ suite. Run `zig build` first.
446
+ - CI (`.github/workflows/test.yml`) runs all four on Ubuntu for every push
447
+ and pull request, with the browser and component parts of the equivalence
448
+ suite skipped (no Playwright, Chromium, wasm-tools or adapter there).
449
+ - `equiv/run.sh` clones the pinned sibling apps (skein-chain, skein-overlay,
450
+ skein-site, skein-shell, skein-chat, skein-onboard, skein-git);
451
+ `SKEIN_CHAIN_DIR`, `SKEIN_OVERLAY_DIR`, `SKEIN_SITE_DIR`,
452
+ `SKEIN_SHELL_DIR`, `SKEIN_CHAT_DIR`, `SKEIN_ONBOARD_DIR`, `SKEIN_GIT_DIR`
453
+ point it at local checkouts. The git app's case (`git-clone.ts`) serves a
454
+ local repository with `git http-backend`. `npm test` installs the
455
+ shell app and the chat app where a test runs them (src/testapps.ts: the
456
+ same pins, fetched once into `$TMPDIR/skein-apps`). The browser cases need Playwright and
457
+ Chromium, else they are skipped with a note.
458
+ - The programs here: `scripts/build-programs.sh && scripts/pin-programs.sh`.
459
+ Developing against a local skein-sdk: clone it next to this checkout and
460
+ prefix a build with `scripts/sdk-local.sh`.
461
+
462
+ ## Repositories
463
+
464
+ | repo | what | version |
465
+ |---|---|---|
466
+ | [shruggr/skein](https://github.com/shruggr/skein) | kernel, boundary programs (front door, messagebox, resolve), the wallet program, the node host, the client | log format 8 |
467
+ | [shruggr/skein-sdk](https://github.com/shruggr/skein-sdk) | the Zig package every program is written against: the `skein` imports, codecs, `app`, `files`, `chain`, wallet library, WIT | v0.6.0 |
468
+ | [shruggr/skein-chain](https://github.com/shruggr/skein-chain) | the chain app: the one writer of `chain/state`, ingest a BEEF, the only broadcaster; born with the image's header chain (#132) | v0.4.0 |
469
+ | [shruggr/skein-overlay](https://github.com/shruggr/skein-overlay) | the overlay engine app: BRC-22/24 over topic managers and lookup services | v0.9.2 |
470
+ | [shruggr/skein-shell](https://github.com/shruggr/skein-shell) | the shell app: `run`, and the shell itself (brush, coreutils, the toolset, python's stdlib) | v0.1.1 |
471
+ | [shruggr/skein-chat](https://github.com/shruggr/skein-chat) | the chat app: the turn loop (`chat`), its `bash` calls in the shell app's shell | v0.1.0 |
472
+ | [shruggr/skein-onboard](https://github.com/shruggr/skein-onboard) | the onboarding app, installed in the host skein: creates a skein for a wallet through the instance manager (the wallet's signed claim forwarded, #127); registration over the registrant's session (#135) | v0.3.3 |
473
+ | [shruggr/skein-git](https://github.com/shruggr/skein-git) | the git app: clones one commit by hash into the store, in the VM, and builds its app record (deploy by hash) | v0.1.2 |
474
+ | [shruggr/skein-site](https://github.com/shruggr/skein-site) | the management site, an app (#125: its page at `/site/`, served from its own tree by skein-sdk's `files`; the owner may add a row for `/`): your skeins from the host, create a skein, install apps, the explorer, the Inbox, handles and their profiles | v0.7.6 |
475
+ | [shruggr/skein-nexus](https://github.com/shruggr/skein-nexus) | the source of https://skein.nexus | |
476
+
477
+ This repository's layout:
478
+
479
+ ```
480
+ kernel-zig/ the kernel: skein-kernel serve | replay | shell | dump | fuel; equiv/ (the equivalence suite)
481
+ images/ the default image (#89): a system tree with no owner and the claim row, no apps (#152); the host image's own part (#142); images/README.md
482
+ programs/ frontdoor, messagebox, resolve, wallet (Zig over skein-sdk); test/ (fixtures, app-demo)
483
+ wasm/ the committed modules the kernel pins (kernel-zig/src/programs.zig), see wasm/README.md
484
+ src/host/ the node host: skein-host (cli.ts), the HTTP transport, providers, feeds, broadcaster, libp2p node, signer, install
485
+ src/runtime/ the formats and a store reader in TypeScript (no machine)
486
+ src/client/ bin/skein: import, run, chat, inbox, dispatch
487
+ src/peers/ bin/skein-infer (the inference peer), a remote cron provider
488
+ src/dev/ bin/skein-dev (log, ls, show over a store file)
489
+ web/ the browser host (web/kernel/) and a front-end demo
490
+ scripts/ program builds and pins, sdk-local.sh; host/ (the dev stack)
491
+ ```
492
+
493
+ ## Docs
494
+
495
+ | doc | for |
496
+ |---|---|
497
+ | [docs/ARCH.md](docs/ARCH.md) | the architecture: the kernel, the host, the browser host, the apps |
498
+ | [docs/VM.md](docs/VM.md) | the machine: records, heads, the four tables, imports, `emit`, calls, requests, fuel, time, the index |
499
+ | [docs/MESSAGES.md](docs/MESSAGES.md) | the wire: the front door, sessions, routes, the log format, the messagebox, outbound, providers, broadcast, scheduling, libp2p |
500
+ | [docs/APPS.md](docs/APPS.md) | apps: the manifest, install, calling an app, security, worked examples |
501
+ | [docs/BOOTSTRAP.md](docs/BOOTSTRAP.md) | genesis: the system tree, packets, checkpoints, installing |
502
+ | [docs/WALLET.md](docs/WALLET.md) | the wallet program, the chain state it reads, proofs, SPV |
503
+ | [docs/OVERLAY.md](docs/OVERLAY.md) | pointer to skein-overlay's docs |
504
+ | [docs/SKILLS.md](docs/SKILLS.md) | which skill scripts run in the VM |
505
+ | [kernel-zig/README.md](kernel-zig/README.md) | building and running the kernel, components, the browser build, equivalence |
506
+ | [scripts/host/README.md](scripts/host/README.md) | the dev host: commands, ports, environment, Arcade, libp2p settings |
507
+ | [src/client/README.md](src/client/README.md) | the client |
508
+ | [docs/OPEN.md](docs/OPEN.md) | open questions still live from building |
509
+
510
+ Vocabulary: the **host** is transports + providers + store + signer (there
511
+ is no "router"; the dispatch table routes); the **kernel** is the machine,
512
+ the four tables and the signer import; **admin operations** are kernel
513
+ operations, not programs; every other thing is an **app** under its own
514
+ name; a **box** is a mailbox address in the dispatch table; **emit** is the
515
+ one way out (a message to a key in the address book — unsigned: a skein
516
+ receives mail only on a BRC-104 session to its own front door or over
517
+ libp2p, #126 step 4 — or an event).
518
+
519
+ ## Contributing
520
+
521
+ The tracker is issue [#31](https://github.com/shruggr/skein/issues/31):
522
+ decisions, conventions and what is ready to build. Where a doc and an issue
523
+ disagree, the issue is right.
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // skein-host for the npm package (and any checkout): src/host/cli.ts built to dist/ (node strips no
3
+ // types under node_modules), else the checkout's TypeScript. No shell, no toolchain: node alone
4
+ // (the kernel is the wasm file the package ships). As bin/skein-host does: SKEIN_HOME (default
5
+ // ~/.skein), and for run | add | event | init SKEIN_OWNER / SKEIN_INFER from owner.identity /
6
+ // infer.identity under it when unset.
7
+ import { existsSync, readFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
+
11
+ const root = new URL("..", import.meta.url);
12
+ const home = process.env.SKEIN_HOME || join(homedir(), ".skein");
13
+ process.env.SKEIN_HOME = home;
14
+ const cmd = process.argv[2] ?? "";
15
+ if (["run", "add", "event", "init"].includes(cmd)) {
16
+ for (const [k, f] of [["SKEIN_OWNER", "owner.identity"], ["SKEIN_INFER", "infer.identity"]]) {
17
+ if (!process.env[k] && existsSync(join(home, f))) process.env[k] = readFileSync(join(home, f), "utf8").trim();
18
+ }
19
+ }
20
+ const built = new URL("dist/host/cli.js", root);
21
+ const { main } = await import(existsSync(built) ? built.href : new URL("src/host/cli.ts", root).href);
22
+ process.exitCode = await main(process.argv.slice(2), { vars: process.env, out: (l) => process.stdout.write(`${l}\n`), err: (l) => process.stderr.write(`${l}\n`) });
package/bin/skein.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ // skein — the client — for the npm package (and any checkout): dist/client/cli.js, else src/client/cli.ts.
3
+ import { existsSync } from "node:fs";
4
+
5
+ const root = new URL("..", import.meta.url);
6
+ const built = new URL("dist/client/cli.js", root);
7
+ const { main } = await import(existsSync(built) ? built.href : new URL("src/client/cli.ts", root).href);
8
+ main(process.argv.slice(2)).then((code) => process.exit(code), (e) => { process.stderr.write(`skein: ${e?.message ?? e}\n`); process.exit(1); });
@@ -0,0 +1,10 @@
1
+ import { type Vars } from "../host/hostenv.ts";
2
+ export declare const ADMIN_USAGE = "usage (the operator's messages, signed with SKEIN_OPERATOR_KEY, default $SKEIN_HOME/operator.key):\n skein install <catalog-name | url#commit | dir> <where> [--config <file.json>] [--dry-run]\n an app (docs/APPS.md \u00A73): a name from the instance's catalog or a repository\n and commit, cloned by the instance's git app; head <app>/app, its rows, start\n skein uninstall <app> <where> [--dry-run] its stop, then its routes removed (the heads are left)\n skein routes add [--transport mailbox|event|http|libp2p] [--prefix] [--filters f,g] [--fn f] [--settings json] <address> [<handler>] <where> [--dry-run]\n root's own route (#143): <handler> a program record CID, a genesis program,\n or <app>.<role>; an http route with --filters and no handler is a read route\n (the site at /: routes add --prefix --filters site.get --settings '{\"root\":\"www\"}' /)\n skein routes remove [--transport t] [--prefix] <address> <where> [--dry-run]\n skein grant add|remove <key> [--role root|<app>.<role>] <where> [--dry-run] a grant (#143; root by default)\n skein peers add <key> <address> [--transport mailbox|libp2p|local] [--handle h@d] <where> [--dry-run]\n skein peers remove <key> <where> [--dry-run] an address-book entry (#70)\n skein host add|remove (<host-key> --x sats [--rates json] | --from <host origin>) <where> [--dry-run]\n the host row (#130)\n skein claim [--messagebox url] [--handle h@d] <where> [--dry-run] an image's claim (#127, #143): root to the key that sends it\n skein deploy <dir> [--only glob,glob | --all] <where> [--dry-run] a directory into main (objects, head)\n<where>: --instance <handle> (this host machine: over its control socket) | <origin> (one BRC-104 session)\n | --store <runtime.db> (read only: planned and printed, nothing sent)";
3
+ export declare const ADMIN_COMMANDS: readonly ["install", "uninstall", "routes", "grant", "peers", "host", "claim", "deploy"];
4
+ export interface AdminEnv {
5
+ vars: Vars;
6
+ out(line: string): void;
7
+ err(line: string): void;
8
+ }
9
+ /** `skein <command> …` for the admin commands. */
10
+ export declare function adminMain(cmd: string, argv: string[], env: AdminEnv): Promise<number>;