feat(prompts): N1c NetBird embedded-Dex sandbox verification prompt (#258)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# N1c — NetBird EMBEDDED-DEX Sandbox Verification (background agent) — server-01, throwaway
|
||||
|
||||
> Executes a de-risk pass for Phase N2 of `playbook_netbird_phases.md`. **Authelia was DROPPED for
|
||||
> NetBird (2026-09-18)** — NetBird now uses its OWN built-in IdP = **embedded Dex** (single-factor,
|
||||
> email/password, users managed from the dashboard). This sandbox proves the embedded-Dex deploy path
|
||||
> works end-to-end BEFORE N2 touches primary — specifically the thing Authelia blocked at: reach the
|
||||
> dashboard, create the first admin at `/setup`, log in, and mint a setup key.
|
||||
> **THROWAWAY. Nothing here is production. Tear it ALL down at the end.**
|
||||
|
||||
## How this is spawned
|
||||
Agent tool (general-purpose), `--max-turns 15`, same security hooks as the main session (no direct
|
||||
mutating host `sudo`; no commits/pushes).
|
||||
|
||||
## Loop detection (verbatim)
|
||||
If you issue the same tool call twice with identical arguments, STOP and output the wrap-up block with
|
||||
status=partially_succeeded.
|
||||
|
||||
## Owner-command relay (pause-for-sudo/browser) — READ FIRST
|
||||
You are non-interactive and CANNOT run host `sudo`, interactive auth, or **browser** steps. When you hit
|
||||
one, do NOT improvise or self-escalate. **Pause and ask the owner:**
|
||||
1. End your run with, as your **final message**, a block headed exactly `⏸ OWNER-COMMAND-REQUEST`
|
||||
(this sentinel, NOT the wrap-up JSON — a pause is not a finish), with exactly:
|
||||
- `host:` where to run it (`server-01 — ssh administrator@192.168.1.90`, or `primary (this host)`, or `browser`).
|
||||
- `command:` exact copy-pasteable command(s) OR the exact URL + form values for a browser step. **No secret values** (name a Vault path instead).
|
||||
- `why:` one line — what it unblocks.
|
||||
- `paste_back:` exactly what you need returned.
|
||||
- `resume_at:` the step number you'll continue from.
|
||||
Then STOP. Do not run it yourself, do not wrap up, do not proceed past `resume_at`.
|
||||
2. The owner runs it and the main session feeds the output back (context intact). Resume at `resume_at`.
|
||||
3. If the pasted output shows failure, you may issue **one** follow-up relay to remediate; else partial wrap-up.
|
||||
4. **Cap: at most 3 owner-command relays this run.** Beyond that = partial wrap-up.
|
||||
|
||||
## Blocked = stop (unanticipated blocks only)
|
||||
If a hook/permission blocks something the relay does NOT cover, emit a partial wrap-up. No creative workarounds.
|
||||
|
||||
## server-01 baseline (do NOT treat this host as clean — it runs offloaded prod)
|
||||
At prompt-writing time `docker ps` shows these **7 resident containers** — leave every one UNTOUCHED:
|
||||
```
|
||||
agent-sudo-agent-sudo-1
|
||||
bitwarden-bridge-sandbox-d2celewbvh7e4fer77fcp9b5
|
||||
hermes
|
||||
jenkins
|
||||
n8n-prod-h10eww4au274owpxozgizysh
|
||||
n8n-sandbox-d2celewbvh7e4fer77fcp9b5
|
||||
vault-sandbox-d2celewbvh7e4fer77fcp9b5
|
||||
```
|
||||
**Step 0 MUST re-snapshot** `docker ps --format '{{.Names}}' | sort` and treat THAT live set as the
|
||||
teardown baseline (the list above is only a sanity anchor). Host facts: `administrator` is in the
|
||||
`docker` group (all docker ops need NO sudo); the `wireguard` kernel module is NOT loaded (use userspace
|
||||
WG if you do the optional peer step); **jenkins/n8n/etc. may occupy common host ports (8080, etc.) — pick
|
||||
HIGH, FREE host ports for your dashboard (e.g. 18080/18443); verify a port is free before binding.**
|
||||
|
||||
## Scope allowlist (touch NOTHING else)
|
||||
- **server-01 only** (`ssh administrator@192.168.1.90`), in a scratch dir `~/netbird-edx-sandbox/` you create,
|
||||
and a dedicated docker bridge `nbedx` + the containers your compose defines.
|
||||
- **Do NOT** touch the 7 resident containers, the primary server, any prod DB, or the host outside your
|
||||
scratch dir. **No host installs (system-wide). No commits, no pushes.**
|
||||
- **No external IdP, no Vault secrets needed** — embedded Dex is self-contained. (If a step unexpectedly
|
||||
wants a secret, STOP and relay; do not invent one.)
|
||||
|
||||
## Objective (bounded)
|
||||
Prove the embedded-Dex NetBird control plane **stands up and is usable**: control plane healthy →
|
||||
dashboard served → `/setup` first-admin creation works (owner does the browser step) → a **setup key can
|
||||
be minted**. That is the N2 go/no-go signal. The 2-peer forced-relay capture is BONUS only (relay is
|
||||
already proven architecturally non-cloudflare = `rel://relay:33080` private bridge IP; do it only if
|
||||
steps 1–6 succeed with turns to spare).
|
||||
|
||||
## Steps (issue each mutating command standalone; verify as a SEPARATE command)
|
||||
0. SSH to server-01. **Snapshot baseline:** `docker ps --format '{{.Names}}' | sort` → save it (teardown target). Create `~/netbird-edx-sandbox/`.
|
||||
1. **Fetch, don't blind-pipe.** `curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh -o getting-started.sh` then READ it. Determine: (a) how it selects the **built-in/embedded-Dex** IdP (vs external OIDC), (b) exactly which files it generates (`docker-compose.yml`, `config.yaml` (a.k.a. `management.json`), `dashboard.env`), (c) how it wires TLS/reverse-proxy. **Vendor-artifact-first — do NOT reverse-engineer config the script already encodes.**
|
||||
2. **Deploy a sandbox-adapted embedded-Dex stack** in the scratch dir: embedded Dex (NO external IdP), **SQLite** store, on the `nbedx` bridge, **NO Let's Encrypt** (choose the no-public-TLS / self-signed reverse-proxy option), dashboard published on a **HIGH FREE host port** (verify free first). Use an internal domain resolved within the compose network (e.g. `netbird-edx.local` via container `/etc/hosts`/alias); the dashboard just needs to be reachable at `http(s)://<server-01-ip>:<highport>` for the owner's browser. Bring it up: `docker compose up -d` (no sudo).
|
||||
3. Verify (separate command): `management`, `signal`, `relay`, `dashboard` Up/healthy (`docker ps`), and **none of the resident baseline containers changed state**.
|
||||
4. **Prove embedded Dex is the IdP + `/setup` is reachable (no browser):** `grep`/`cat` the generated `config.yaml`/`management.json` to confirm the issuer/IdP is NetBird's **own embedded Dex** (self-issued), NOT an external URL. Then `curl -ksS` the dashboard root and confirm it serves and that `/setup` is the entry when no users exist (or the dashboard config points there). Record both.
|
||||
5. **Owner-verifies (owner-command relay) — the browser `/setup`:** pause with the exact dashboard URL and the form to fill (email / name / password → **Create Account**). `paste_back:` confirmation the account was created and login lands in the dashboard. (This is the exact step Authelia hung on — it MUST work cleanly here.)
|
||||
6. **Mint a reusable setup key** — after the admin exists, create one via the management API/CLI inside the control plane (or instruct the owner to click Setup Keys → Create in the dashboard via a second relay if the API needs the admin token). **MASK the key** in all output.
|
||||
7. **(BONUS, only if turns remain)** enroll `peer-a`/`peer-b` (containers, `cap_add:[NET_ADMIN]`, `/dev/net/tun`, userspace WG) with the setup key, force relay via container-scoped `iptables` DROP on the direct WG path, and confirm `netbird status --detail` = relayed via the private `relay` endpoint (not cloudflare). Record `relay_transport`/`relay_is_cloudflare=false`. If short on turns, SKIP and say so.
|
||||
8. **Tear down to baseline:** `docker compose down -v` in the scratch dir, `rm -rf ~/netbird-edx-sandbox`. Verify (separate command) `docker ps --format '{{.Names}}' | sort` **equals the step-0 baseline** — resident 7 unchanged, none restarted. Leave NOTHING of yours running.
|
||||
|
||||
## Verify before reporting success
|
||||
`status=succeeded` ONLY if: stack came up healthy, config confirmed embedded-Dex (self-issued, no external IdP), `/setup` reachable, **owner confirmed admin creation + login**, a setup key was minted, AND teardown == step-0 baseline. Otherwise `partially_succeeded` (and if the owner browser step is still pending when you must stop, that's `partially_succeeded` with a clear resume note — NOT a failure).
|
||||
|
||||
## Persistence (final actions — your context is discarded on finish)
|
||||
- Append a dated handoff to `research/netbird-design_2026-09-17.md` §7 ("Embedded-Dex sandbox — what stood up / setup+login result / setup-key minted / bonus relay / N2 go-no-go / Next step"). Appending to the research doc is in scope; do NOT commit.
|
||||
- Write per-agent run log `logs/agent_runs/N1c.json` with the wrap-up JSON.
|
||||
- Update the semantic index: `python3 /opt/appdata/docker/.claude/scripts/embed_memory_dir.py --only-recent 3`.
|
||||
- Do NOT rely on the Stop hook's MEMORY_EMBED tag.
|
||||
|
||||
## Wrap-up block (emit REGARDLESS of success/failure; mask all secrets)
|
||||
```json
|
||||
{
|
||||
"status": "succeeded | partially_succeeded | failed",
|
||||
"project": "netbird #258 / N1c embedded-Dex sandbox",
|
||||
"actions_taken": [],
|
||||
"actions_failed": [],
|
||||
"files_touched": [],
|
||||
"owner_command_relays": [],
|
||||
"containers_restarted": [],
|
||||
"baseline_restored": true,
|
||||
"stack_healthy": true,
|
||||
"idp_is_embedded_dex": true,
|
||||
"setup_page_reachable": true,
|
||||
"admin_created_by_owner": "true | pending | failed",
|
||||
"setup_key_minted": true,
|
||||
"relay_transport": "not-tested | udp-relay-container",
|
||||
"relay_is_cloudflare": false,
|
||||
"teardown_clean": true,
|
||||
"n2_go_no_go": "GO | NO-GO | GO-with-caveats",
|
||||
"next_step": "",
|
||||
"notes": ""
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user