@openparachute/hub 0.6.3-rc.1 → 0.6.3-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -35
- package/package.json +1 -1
- package/src/__tests__/api-hub-upgrade.test.ts +690 -0
- package/src/__tests__/api-modules-ops.test.ts +121 -0
- package/src/__tests__/api-modules.test.ts +67 -0
- package/src/__tests__/expose-cloudflare.test.ts +163 -72
- package/src/__tests__/expose-off-auto.test.ts +26 -1
- package/src/__tests__/expose.test.ts +260 -240
- package/src/__tests__/host-admin-token-validation.test.ts +218 -0
- package/src/__tests__/hub-control.test.ts +1 -242
- package/src/__tests__/hub-server.test.ts +64 -0
- package/src/__tests__/lifecycle.test.ts +431 -1886
- package/src/__tests__/migrate-cutover.test.ts +840 -0
- package/src/__tests__/migrate-offer.test.ts +240 -0
- package/src/__tests__/migrate.test.ts +148 -0
- package/src/__tests__/operator-token.test.ts +277 -0
- package/src/__tests__/status-supervisor.test.ts +12 -77
- package/src/__tests__/status.test.ts +157 -708
- package/src/__tests__/upgrade.test.ts +351 -5
- package/src/api-hub-upgrade.ts +384 -0
- package/src/api-hub.ts +2 -1
- package/src/api-modules-ops.ts +28 -2
- package/src/api-modules.ts +25 -2
- package/src/cli.ts +85 -10
- package/src/commands/expose-cloudflare.ts +63 -71
- package/src/commands/expose-supervisor.ts +247 -0
- package/src/commands/expose.ts +59 -48
- package/src/commands/lifecycle.ts +184 -873
- package/src/commands/migrate-cutover.ts +837 -0
- package/src/commands/migrate.ts +71 -2
- package/src/commands/status.ts +35 -282
- package/src/commands/upgrade.ts +100 -2
- package/src/help.ts +128 -68
- package/src/host-admin-token-validation.ts +96 -0
- package/src/hub-control.ts +23 -162
- package/src/hub-server.ts +47 -3
- package/src/hub-upgrade-helper.ts +306 -0
- package/src/hub-upgrade-mode.ts +209 -0
- package/src/hub-upgrade-status.ts +150 -0
- package/src/managed-unit.ts +20 -2
- package/src/migrate-offer.ts +186 -0
- package/src/operator-token.ts +96 -5
- package/src/origin-check.ts +10 -0
- package/src/process-state.ts +19 -3
- package/src/supervisor.ts +29 -24
- package/web/ui/dist/assets/index-D_6AFvZy.js +61 -0
- package/web/ui/dist/assets/{index-BiBlvEaj.css → index-mz8XcVPP.css} +1 -1
- package/web/ui/dist/index.html +2 -2
- package/web/ui/dist/assets/index-CIN3mnmf.js +0 -61
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`@openparachute/hub` — the local hub for the [Parachute](https://parachute.computer) ecosystem. The `parachute` binary is one of its surfaces.
|
|
4
4
|
|
|
5
|
-
The hub coordinates the modules running on your machine: it installs them,
|
|
5
|
+
The hub coordinates the modules running on your machine: it installs them, supervises them as child processes (the hub itself runs under your platform's process manager — launchd / systemd / the container runtime), exposes them over Tailscale, serves the discovery document at `/.well-known/parachute.json`, and (soon) issues OAuth tokens. Each module (vault, app, scribe, …) stays a standalone package; the hub stitches them together.
|
|
6
6
|
|
|
7
7
|
> Previously published as `@openparachute/cli`. Renamed 2026-04-26 to better reflect the role — see [parachute-patterns/hub-as-issuer](https://github.com/ParachuteComputer/parachute-patterns/blob/main/patterns/hub-as-issuer.md). The `parachute` binary name is unchanged.
|
|
8
8
|
|
|
@@ -75,7 +75,9 @@ parachute init
|
|
|
75
75
|
|
|
76
76
|
`parachute init` is idempotent — every re-run is safe. End to end it:
|
|
77
77
|
|
|
78
|
-
1. **
|
|
78
|
+
1. **Installs and starts the hub** as a managed unit (launchd on a Mac,
|
|
79
|
+
systemd on a Linux VM) on port `1939`, so it survives reboots. Re-runs are
|
|
80
|
+
no-ops if the unit is already up.
|
|
79
81
|
2. **Offers to expose it** so you can reach the wizard from other devices. In a
|
|
80
82
|
terminal you pick: stay loopback-only, your **tailnet** (`tailscale serve` —
|
|
81
83
|
private to your own Tailscale devices), or a **Cloudflare Tunnel** (public
|
|
@@ -108,9 +110,9 @@ UI. Verify the stack any time:
|
|
|
108
110
|
|
|
109
111
|
```sh
|
|
110
112
|
parachute status
|
|
111
|
-
# SERVICE PORT VERSION
|
|
112
|
-
# parachute-hub 1939 0.5.14
|
|
113
|
-
# parachute-vault 1940 0.4.5
|
|
113
|
+
# SERVICE PORT VERSION STATE PID UPTIME LATENCY SOURCE
|
|
114
|
+
# parachute-hub 1939 0.5.14 active 12344 20s 1ms managed unit (launchd)
|
|
115
|
+
# parachute-vault 1940 0.4.5 active 12345 12s 2ms npm (0.4.5)
|
|
114
116
|
```
|
|
115
117
|
|
|
116
118
|
Vault is up on `127.0.0.1:1940`; Claude Code picks up the MCP on your next
|
|
@@ -144,7 +146,7 @@ still work and are additive:
|
|
|
144
146
|
```sh
|
|
145
147
|
parachute install vault # install + register + create first vault + start one module
|
|
146
148
|
parachute setup # older interactive multi-pick: survey + install vault/notes/scribe
|
|
147
|
-
parachute start vault #
|
|
149
|
+
parachute start vault # start one module via the running hub's supervisor
|
|
148
150
|
```
|
|
149
151
|
|
|
150
152
|
### Expose across your tailnet
|
|
@@ -180,35 +182,77 @@ deleted or reset from the Users page — it changes its own password at
|
|
|
180
182
|
|
|
181
183
|
## Service lifecycle
|
|
182
184
|
|
|
183
|
-
|
|
185
|
+
Parachute runs **one runtime everywhere**: `parachute serve` — the hub in the
|
|
186
|
+
foreground with an in-process supervisor that runs each module as an attached
|
|
187
|
+
child, multiplexes their logs, and restarts crashed ones. That `serve` process
|
|
188
|
+
runs under your platform's own process manager — **launchd** on a Mac,
|
|
189
|
+
**systemd** on a Linux VM, the **container runtime** on Render / Fly — so the
|
|
190
|
+
hub (and every module under it) survives reboots and crashes without you
|
|
191
|
+
SSHing back in.
|
|
192
|
+
|
|
193
|
+
`parachute init` installs and starts that managed hub unit for you; the
|
|
194
|
+
lifecycle verbs then drive the running supervisor:
|
|
184
195
|
|
|
185
196
|
```sh
|
|
186
|
-
parachute start #
|
|
187
|
-
parachute start vault #
|
|
188
|
-
parachute stop
|
|
189
|
-
parachute
|
|
197
|
+
parachute start # ensure the hub unit is up (boots every module)
|
|
198
|
+
parachute start vault # start one module via the running supervisor
|
|
199
|
+
parachute stop vault # stop one module via the supervisor
|
|
200
|
+
parachute stop # stop the hub unit (children stop with it)
|
|
201
|
+
parachute restart vault # restart one module via the supervisor
|
|
202
|
+
parachute restart # restart the hub unit (re-boots every module)
|
|
190
203
|
parachute logs vault # last 200 lines
|
|
191
204
|
parachute logs vault -f # tail (like `tail -f`)
|
|
192
205
|
```
|
|
193
206
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- `
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
207
|
+
How the verbs map to the model:
|
|
208
|
+
|
|
209
|
+
- **`start` / `stop` / `restart <svc>`** are clients of the running hub. They
|
|
210
|
+
ensure the hub unit is up, then call its supervisor over a loopback
|
|
211
|
+
module-ops API (authenticated with your operator token) to start / stop /
|
|
212
|
+
restart that one module. There's no per-module daemon to track — the
|
|
213
|
+
supervisor owns module processes.
|
|
214
|
+
- **`start` (no service)** ensures the hub unit is up; the hub boots every
|
|
215
|
+
installed module on start, so this brings the whole stack up.
|
|
216
|
+
- **`stop` (no service)** stops the **hub unit** through the platform manager
|
|
217
|
+
(`launchctl bootout` / `systemctl stop`); the modules are attached children
|
|
218
|
+
and stop with it.
|
|
219
|
+
- **`restart` (no service)** restarts the **hub unit** (`launchctl kickstart
|
|
220
|
+
-k` / `systemctl restart`), which re-boots every module — it is *not* a
|
|
221
|
+
fan-out of per-module restarts.
|
|
222
|
+
|
|
223
|
+
`parachute status` shows a hub row even with zero modules installed — that row
|
|
224
|
+
is derived from the platform manager (`launchctl print` / `systemctl
|
|
225
|
+
is-active`, or "container runtime (managed)" on Render / Fly), since the
|
|
226
|
+
supervisor runs the modules but the manager runs the hub.
|
|
227
|
+
|
|
228
|
+
### No process manager installed yet?
|
|
229
|
+
|
|
230
|
+
If you've never run `parachute init` (or you're on a legacy install that used
|
|
231
|
+
the old detached-daemon model), the lifecycle verbs will prompt you to run
|
|
232
|
+
`parachute migrate --to-supervised`, which installs the hub unit and moves you
|
|
233
|
+
onto the supervised model. See **Migrating a legacy install** below.
|
|
234
|
+
|
|
235
|
+
On a host with no process manager at all (a bare container, an init-less
|
|
236
|
+
image), there's no unit to install — run `parachute serve` in the foreground
|
|
237
|
+
instead (this is exactly what the container `CMD` does).
|
|
238
|
+
|
|
239
|
+
### Migrating a legacy install
|
|
240
|
+
|
|
241
|
+
Earlier Parachute releases ran each service as an independent detached daemon
|
|
242
|
+
(its own pidfile + log file under `~/.parachute/<service>/`), with no
|
|
243
|
+
supervisor and no reboot survival. To move an existing box onto the supervised
|
|
244
|
+
model:
|
|
204
245
|
|
|
205
246
|
```sh
|
|
206
|
-
|
|
207
|
-
rm ~/Library/LaunchAgents/computer.parachute.vault.plist
|
|
208
|
-
parachute start vault
|
|
247
|
+
parachute migrate --to-supervised # install + start the hub unit, cut over
|
|
209
248
|
```
|
|
210
249
|
|
|
211
|
-
|
|
250
|
+
The cutover is idempotent and re-runnable. It writes the platform unit, stops
|
|
251
|
+
the old detached processes, verifies the canonical ports are free, then starts
|
|
252
|
+
the hub unit and confirms the hub is healthy. To roll it back, `parachute
|
|
253
|
+
migrate --teardown` removes the unit (run that *before* `bun remove -g
|
|
254
|
+
@openparachute/hub` so a removed package doesn't leave a unit pointing at a
|
|
255
|
+
deleted binary).
|
|
212
256
|
|
|
213
257
|
### Migrating from pre-CLI installs
|
|
214
258
|
|
|
@@ -225,17 +269,21 @@ Anything swept goes to `~/.parachute/.archive-<YYYY-MM-DD>/` with its original n
|
|
|
225
269
|
## Two supported layers (plus an exploratory third)
|
|
226
270
|
|
|
227
271
|
Each additive; each can be turned off without affecting the layer below.
|
|
272
|
+
Exposure is decoupled from the hub's own lifecycle: `expose` makes the
|
|
273
|
+
already-running hub reachable on a network layer, and `expose <layer> off`
|
|
274
|
+
tears down only that exposure — the hub keeps running as its managed unit
|
|
275
|
+
either way.
|
|
228
276
|
|
|
229
|
-
- **Local** —
|
|
277
|
+
- **Local** — the hub on loopback. Zero config. Browsers treat `localhost` as a secure context, so OAuth, PKCE, and Web Crypto all just work out of the box.
|
|
230
278
|
- **Tailnet** — `parachute expose tailnet` wraps `tailscale serve` for every registered service. HTTPS via Tailscale's MagicDNS cert. Only machines on your tailnet can reach the URL. **This is the documented shape for the hub today.** Tailnet is already authenticated at the network layer, every user's tailnet is their own, and the OAuth + module access work happening in the hub is being designed against this shape first.
|
|
231
279
|
|
|
232
280
|
### Public exposure (exploratory)
|
|
233
281
|
|
|
234
282
|
`parachute expose public` exists for early testers. It routes each handler through `tailscale funnel` (or, with `--cloudflare`, a named Cloudflare tunnel) so the same URLs become reachable from the public internet. The code path is live and the flag still works, but the public-internet posture (DNS, cross-internet OAuth, Funnel quirks) hasn't been hardened the way tailnet has — expect rough edges.
|
|
235
283
|
|
|
236
|
-
When the hub's OAuth issuer + per-module scope enforcement land, public will re-enter the documented narrative as "now safe." Until then, prefer tailnet.
|
|
284
|
+
When the hub's OAuth issuer + per-module scope enforcement land, public will re-enter the documented narrative as "now safe." Until then, prefer tailnet. (If you route a public layer through Cloudflare, note their bot-protection / Browser Integrity Check can interfere with OAuth and MCP clients — see the caveat on [parachute.computer](https://parachute.computer).)
|
|
237
285
|
|
|
238
|
-
Under the hood, tailnet mode uses `tailscale serve` and public mode uses `tailscale funnel`; both write into the same node-level serve config. The CLI records which layer is live so that `expose <other-layer> off` is a no-op rather than a surprise teardown of the active layer.
|
|
286
|
+
Under the hood, tailnet mode uses `tailscale serve` and public mode uses `tailscale funnel`; both write into the same node-level serve config. The CLI records which layer is live so that `expose <other-layer> off` is a no-op rather than a surprise teardown of the active layer. `expose off` only ever tears down exposure — it never stops the hub (the platform manager owns the hub's lifecycle now).
|
|
239
287
|
|
|
240
288
|
## Path-routing (and why)
|
|
241
289
|
|
|
@@ -251,7 +299,9 @@ https://parachute.<tailnet>.ts.net/.well-known/parachute.json ← discovery
|
|
|
251
299
|
|
|
252
300
|
The hub page fetches the discovery doc at load, then each service's `/.parachute/info` endpoint for display name, tagline, and icon. Adding a new service is zero CLI code — drop in its manifest entry and the hub picks it up.
|
|
253
301
|
|
|
254
|
-
Under the hood, `/` and `/.well-known/parachute.json` are
|
|
302
|
+
Under the hood, `/` and `/.well-known/parachute.json` are served by the hub
|
|
303
|
+
process on the loopback interface — the same `parachute serve` process the
|
|
304
|
+
platform manager keeps running. Tailscale's file-serve mode is sandbox-restricted on macOS, so a localhost proxy is the portable shape. The hub is a persistent managed unit: it runs whether or not any exposure layer is up, so `expose tailnet off` / `expose public off` tears down only the *exposure*, leaving the hub serving on loopback. `parachute status` lists the hub row (manager-derived) at the top.
|
|
255
305
|
|
|
256
306
|
The `/.well-known/parachute.json` document is an always-present descriptor — flat `services[]` array that the hub iterates, plus top-level keys for legacy clients:
|
|
257
307
|
|
|
@@ -402,19 +452,21 @@ Public-internet exposure (`parachute expose public`) is exploratory — see "Pub
|
|
|
402
452
|
Run `parachute --help` for the top-level list, and `parachute <subcommand> --help` for details on any individual command.
|
|
403
453
|
|
|
404
454
|
```
|
|
405
|
-
parachute init fresh-install front door:
|
|
406
|
-
|
|
455
|
+
parachute init fresh-install front door: install + start the
|
|
456
|
+
managed hub, offer expose, install vault, wizard
|
|
407
457
|
parachute setup-wizard --hub-url <url>
|
|
408
458
|
in-terminal mirror of /admin/setup (Account/Vault/Expose)
|
|
409
459
|
parachute setup older interactive multi-pick service installer
|
|
410
460
|
parachute install <service> install and register a service
|
|
411
|
-
parachute status show installed services,
|
|
412
|
-
parachute start [service] start
|
|
413
|
-
parachute stop [service] stop
|
|
414
|
-
parachute restart [service]
|
|
461
|
+
parachute status show installed services, run state, health
|
|
462
|
+
parachute start [service] start a module via the supervisor (or ensure the hub is up)
|
|
463
|
+
parachute stop [service] stop a module via the supervisor (or the hub unit)
|
|
464
|
+
parachute restart [service] restart a module via the supervisor (or the hub unit)
|
|
465
|
+
parachute serve run the hub + supervisor foregrounded (the runtime)
|
|
415
466
|
parachute logs <service> [-f] print/tail service logs
|
|
416
467
|
parachute expose tailnet [off] HTTPS across your tailnet (supported)
|
|
417
468
|
parachute expose public [off] HTTPS on the public internet (exploratory)
|
|
469
|
+
parachute migrate --to-supervised move a legacy detached install to the managed hub
|
|
418
470
|
parachute migrate [--dry-run] archive legacy files at ecosystem root
|
|
419
471
|
parachute vault <args...> dispatch to parachute-vault
|
|
420
472
|
```
|
package/package.json
CHANGED