create-op-node 0.2.0 → 0.4.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.
- package/README.md +103 -7
- package/dist/cli.js +215 -152
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ That's it. The wizard walks you through:
|
|
|
13
13
|
2. **GitHub** — creates your region's node repo from the [`OpusPopuli/opuspopuli-node`](https://github.com/OpusPopuli/opuspopuli-node) template via the GitHub API. Seeds the 5 required GitHub Secrets (Cloudflare token, account ID, zone ID, Terraform Cloud token, TFC org) for you.
|
|
14
14
|
3. **Terraform Cloud** — verifies your TFC token, prepares the workspace.
|
|
15
15
|
4. **First PR** — writes `environments/prod.tfvars` from your answers, commits, opens the first PR. The node repo's `cloudflare-infra.yml` workflow runs `terraform plan` against the PR; on merge to `main` it applies — Tunnel, DNS, R2 buckets, and Pages project come up automatically.
|
|
16
|
-
5. **pgsodium master key** — generates a fresh 64-hex root key, stores it in
|
|
17
|
-
6. **Tunnel token retrieval** — after `terraform apply` lands, fetches the Tunnel token from Terraform Cloud outputs and stores it
|
|
16
|
+
5. **pgsodium master key** — generates a fresh 64-hex root key, stores it in your **macOS login Keychain** as `org.opuspopuli.<region>/pgsodium-root-key`. No third-party password manager required.
|
|
17
|
+
6. **Tunnel token retrieval** — after `terraform apply` lands, fetches the Tunnel token from Terraform Cloud outputs and stores it alongside the pgsodium key in your Keychain.
|
|
18
18
|
|
|
19
19
|
Then on the Mac Studio itself:
|
|
20
20
|
|
|
@@ -22,7 +22,50 @@ Then on the Mac Studio itself:
|
|
|
22
22
|
npx create-op-node bootstrap
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Configures macOS power settings, installs Homebrew + the CLI tool list, sets up Docker Desktop + Tailscale + Ollama, clones the node repo you created,
|
|
25
|
+
Configures macOS power settings, installs Homebrew + the CLI tool list, sets up Docker Desktop + Tailscale + Ollama, clones the node repo you created, reads the pgsodium key + Tunnel token from the Studio's Keychain (or prompts you to paste them once, then persists for re-runs), writes the LaunchAgent plist, logs into ghcr.io, pulls + warms the LLM model, and finally `docker compose --profile public pull && up -d` brings the whole stack online. Health-check loop waits until all containers are `(healthy)`.
|
|
26
|
+
|
|
27
|
+
### Local-only mode (no Cloudflare)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx create-op-node bootstrap --region us-ca --local-only
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Brings the Studio up for local dev / testing — frontend on your laptop
|
|
34
|
+
hits the Studio over Tailscale, no public exposure. Differences from
|
|
35
|
+
the standard run:
|
|
36
|
+
|
|
37
|
+
- **No Tunnel token required.** `init` is unnecessary; if the pgsodium
|
|
38
|
+
key isn't in Keychain, bootstrap generates one inline and persists it.
|
|
39
|
+
- **`cloudflared` stays down.** It's gated behind the `public` compose
|
|
40
|
+
profile, which `--local-only` doesn't activate. Bootstrap also evicts
|
|
41
|
+
any leftover cloudflared from a prior public run so it doesn't strand
|
|
42
|
+
in `compose ps`.
|
|
43
|
+
- **Backup stack skipped by default.** `docker-compose-backup.yml`
|
|
44
|
+
isn't loaded; pass `--compose-file docker-compose-backup.yml` to
|
|
45
|
+
include it explicitly.
|
|
46
|
+
- **LaunchAgent omits `TUNNEL_TOKEN`.** Only `PGSODIUM_ROOT_KEY` is
|
|
47
|
+
exported into the launchd session.
|
|
48
|
+
- **Outro tells you to use Tailscale**, not `npx create-op-node verify`.
|
|
49
|
+
|
|
50
|
+
When you're ready to go public, re-run `bootstrap` without `--local-only`
|
|
51
|
+
and the same Studio promotes to the full production-shaped deploy.
|
|
52
|
+
|
|
53
|
+
> **Template version**: this mode depends on the `opuspopuli-node`
|
|
54
|
+
> template having `profiles: [public]` on its cloudflared service. If
|
|
55
|
+
> you cloned the template before that landed, refresh your fork
|
|
56
|
+
> (or recreate from template) before using `--local-only` — otherwise
|
|
57
|
+
> cloudflared starts regardless and will restart-loop without a
|
|
58
|
+
> TUNNEL_TOKEN.
|
|
59
|
+
|
|
60
|
+
> **Secret transport between laptop and Studio**
|
|
61
|
+
>
|
|
62
|
+
> The macOS `security` CLI writes to the local login keychain — items
|
|
63
|
+
> don't sync to iCloud Keychain automatically. On the Studio's first
|
|
64
|
+
> bootstrap, the operator pastes the pgsodium key + Tunnel token once
|
|
65
|
+
> (from the laptop's Keychain Access, or wherever you copied them); the
|
|
66
|
+
> Studio bootstrap validates the format and persists locally so re-runs
|
|
67
|
+
> read straight through. Use AirDrop / `security find-generic-password`
|
|
68
|
+
> output / Tailscale `scp` to ferry the values.
|
|
26
69
|
|
|
27
70
|
## Resetting the Studio
|
|
28
71
|
|
|
@@ -52,7 +95,10 @@ with `bootstrap` without losing data.
|
|
|
52
95
|
those need separate cleanup.
|
|
53
96
|
|
|
54
97
|
Reset does **not** touch cloud-side state: the Cloudflare resources,
|
|
55
|
-
the GitHub repo, the TFC workspace
|
|
98
|
+
the GitHub repo, and the TFC workspace remain. Keychain items on the
|
|
99
|
+
Studio are also left in place — `security delete-generic-password -s
|
|
100
|
+
org.opuspopuli.<region> -a pgsodium-root-key` etc. if you want them
|
|
101
|
+
gone.
|
|
56
102
|
`init` is idempotent against existing cloud setup, so re-running it
|
|
57
103
|
won't duplicate anything.
|
|
58
104
|
|
|
@@ -280,13 +326,63 @@ rather than in the regions repo itself:
|
|
|
280
326
|
green run here, as the real signal. If the two ever disagree, the schema
|
|
281
327
|
wins and this command needs updating.
|
|
282
328
|
|
|
329
|
+
## What lands where
|
|
330
|
+
|
|
331
|
+
`create-op-node` touches several secret stores. Here's the full map of
|
|
332
|
+
what we own (the two macOS Keychain items) vs. what we just route to its
|
|
333
|
+
destination.
|
|
334
|
+
|
|
335
|
+
### Stored by `create-op-node` in macOS Keychain
|
|
336
|
+
|
|
337
|
+
Two items per region, both generic-password class
|
|
338
|
+
(`kSecClassGenericPassword`). Visible in **Keychain Access.app**
|
|
339
|
+
(`/System/Applications/Utilities/Keychain Access.app`) — **not** in the
|
|
340
|
+
new Passwords.app, which is filtered to website-login items only.
|
|
341
|
+
|
|
342
|
+
| # | Service | Account | Label (GUI display) | Value format | Written by | Read by |
|
|
343
|
+
|---|---|---|---|---|---|---|
|
|
344
|
+
| 1 | `org.opuspopuli.<region>` | `pgsodium-root-key` | `Opus Populi (<region>) — pgsodium root key` | 64 lowercase hex chars | `init` on laptop | `bootstrap` on Studio |
|
|
345
|
+
| 2 | `org.opuspopuli.<region>` | `tunnel-token` | `Opus Populi (<region>) — Cloudflare Tunnel token` | JWT-style base64url string | `init` on laptop (after TFC apply) | `bootstrap` on Studio |
|
|
346
|
+
|
|
347
|
+
Both items also carry `-D 'Opus Populi secret'` (the "Kind" column in
|
|
348
|
+
Keychain Access) so you can filter for them at a glance.
|
|
349
|
+
|
|
350
|
+
Inspect from a shell:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
# Metadata only (safe to share output):
|
|
354
|
+
security find-generic-password -s org.opuspopuli.us-ca -a pgsodium-root-key
|
|
355
|
+
|
|
356
|
+
# Reveal the value (you'll be prompted to allow access on first call):
|
|
357
|
+
security find-generic-password -s org.opuspopuli.us-ca -a pgsodium-root-key -w
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Stored elsewhere (we don't put these in Keychain)
|
|
361
|
+
|
|
362
|
+
Everything else flows through transiently or lives in its destination
|
|
363
|
+
system's own credential store.
|
|
364
|
+
|
|
365
|
+
| Secret | Where it lives | Why not in Keychain |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| Cloudflare API token | Pasted into `init` prompt → forwarded to **GitHub Secrets** + **Terraform Cloud** vars | One-shot during init. Re-runs prompt again. We could store it; adds risk vs benefit. |
|
|
368
|
+
| Cloudflare account ID, zone ID | Same as above | Not really a "secret" but flow alongside the token |
|
|
369
|
+
| Terraform Cloud token | Pasted, used to verify + poll runs | Same one-shot pattern |
|
|
370
|
+
| GitHub PAT | Read from `gh auth token` if available, else pasted | `gh` already manages it |
|
|
371
|
+
| pgsodium key (Studio runtime form) | `~/.config/opuspopuli/pgsodium_root_key` (mode `0400`) | LaunchAgent reads it at every login → interpolates into the `PGSODIUM_ROOT_KEY` env var. Same value as in Keychain; file is runtime form. |
|
|
372
|
+
| Cloudflare Tunnel token (Studio runtime form) | Baked into `~/Library/LaunchAgents/org.opuspopuli.envloader.plist` (mode `0600`) | launchd's `launchctl setenv TUNNEL_TOKEN` injects it into the session at every boot. Same value as in Keychain; plist is runtime form. |
|
|
373
|
+
| ghcr.io credentials | `~/.docker/config.json` or `docker-credential-osxkeychain` | Docker manages its own credential store — it actually saves the ghcr token to a separate Keychain item under service `ghcr.io`. We just call `docker login`. |
|
|
374
|
+
|
|
375
|
+
Per region, the **only** persistent secrets `create-op-node` owns are
|
|
376
|
+
the two Keychain items above. Everything else is either transient
|
|
377
|
+
(prompted, used, forgotten) or lives in its destination system.
|
|
378
|
+
|
|
283
379
|
## Why this exists
|
|
284
380
|
|
|
285
381
|
Each Opus Populi region is operated independently by a local maintainer — its own Cloudflare account, its own Mac Studio, its own domain. The full bootstrap is a few hours of manual steps across Cloudflare, GitHub, Terraform Cloud, macOS Setup Assistant, Docker Desktop, Tailscale, Ollama, and the node's own Docker Compose stack. Doable from the runbook, but error-prone.
|
|
286
382
|
|
|
287
383
|
This CLI exists to make that bootstrap **foolproof** — every prompt validates immediately, every secret is retrieved from a secure source (never echoed, never written to disk in plaintext), and every step has an explicit "what happens next" message. The goal is zero documentation reading required to get a node running.
|
|
288
384
|
|
|
289
|
-
The CLI itself never holds any credentials beyond the scope of a single command — secrets flow from your
|
|
385
|
+
The CLI itself never holds any credentials beyond the scope of a single command — secrets flow from your macOS Keychain → through the CLI → directly into the destination (GitHub Secrets, Terraform Cloud workspace variables, Mac Studio LaunchAgent). Nothing persists in this process.
|
|
290
386
|
|
|
291
387
|
## Architecture
|
|
292
388
|
|
|
@@ -322,9 +418,9 @@ The CLI itself never holds any credentials beyond the scope of a single command
|
|
|
322
418
|
|
|
323
419
|
## Status
|
|
324
420
|
|
|
325
|
-
**`init` — fully wired.** Full Phase 1 of the runbook: prompts → Cloudflare 5-scope probe → Terraform Cloud verify → GitHub template clone → 5 repo secrets seeded → branch + prod.tfvars committed → PR opened → pgsodium key generated → (after operator merges PR) Terraform apply polled → Tunnel token retrieved + saved to
|
|
421
|
+
**`init` — fully wired.** Full Phase 1 of the runbook: prompts → Cloudflare 5-scope probe → Terraform Cloud verify → GitHub template clone → 5 repo secrets seeded → branch + prod.tfvars committed → PR opened → pgsodium key generated → (after operator merges PR) Terraform apply polled → Tunnel token retrieved + saved to the macOS Keychain.
|
|
326
422
|
|
|
327
|
-
**`bootstrap` — fully wired.** Phase 2 on the Mac Studio: macOS sanity (auto-restart, disk sleep), Homebrew + tool installs (gh, pnpm, jq, cloudflared, rclone, ollama, docker, tailscale), GitHub + Tailscale signin prompts, pgsodium key + Tunnel token read from
|
|
423
|
+
**`bootstrap` — fully wired.** Phase 2 on the Mac Studio: macOS sanity (auto-restart, disk sleep), Homebrew + tool installs (gh, pnpm, jq, cloudflared, rclone, ollama, docker, tailscale), GitHub + Tailscale signin prompts, pgsodium key + Tunnel token read from the Studio's Keychain (or pasted in once if first run on that machine, then persisted), LaunchAgent written + loaded, ghcr.io login, Ollama models pulled + warmed, region repo located or cloned, `docker compose pull && up -d`, health-check loop until everything reports `(healthy)`.
|
|
328
424
|
|
|
329
425
|
**`verify` — scaffold stub.** Type-safe argument parsing only; prints a roadmap-style message and exits.
|
|
330
426
|
|