diff --git a/docs/superpowers/specs/2026-08-26-nats-external-apps-user-design.md b/docs/superpowers/specs/2026-08-26-nats-external-apps-user-design.md new file mode 100644 index 0000000..c1f5264 --- /dev/null +++ b/docs/superpowers/specs/2026-08-26-nats-external-apps-user-design.md @@ -0,0 +1,60 @@ +# Design: NATS `external-apps` user + +## Goal + +Add a third NATS user, `external-apps`, so third-party / external applications can connect to `wss://nats.tes.gd` with their own credential instead of borrowing the `admin` or `volcanic-agents` account. + +## Scope decisions + +- **Permissions:** unrestricted. Same shape as the existing `admin` and `volcanic-agents` users — no `permissions` block, so full publish/subscribe on any subject, JetStream management included. Scoped permissions are explicitly out of scope for this change; if we later need to lock down external callers, that's a follow-up. +- **Credential style:** password (not opaque bearer token). Env var is `NATS_EXTERNAL_APPS_PASSWORD`, following the `NATS_PASSWORD` convention used by `admin` rather than the `..._TOKEN` convention used by `volcanic-agents`. +- **Wire protocol / transport:** unchanged. The user connects the same way as the others (WebSockets at `wss://nats.tes.gd`, standard user+password auth on the wire). + +## Change surface + +1. **`docker-compose.yml`** — inside `configs.nats-conf.content`, append one entry to `authorization.users`: + ``` + { user: "external-apps", password: "${NATS_EXTERNAL_APPS_PASSWORD}" } + ``` + Substitution happens at Docker Compose parse time, so NATS sees a static config on disk. + +2. **Coolify env var** — add `NATS_EXTERNAL_APPS_PASSWORD` on the `nats` application (project `Volcanic MFEs`, app UUID from local `deploy.json`). Value: a fresh random secret (e.g. `openssl rand -hex 32`). + +3. **`credentials.local.json`** — add a third entry under `users`: + ```json + "external-apps": { "user": "external-apps", "password": "…", "note": "…" } + ``` + Mirrors the Coolify env value. File is gitignored. + +4. **`CLAUDE.md`** — update the two spots that enumerate users so the doc stays in sync: + - The "Connection details (for agents managing this NATS)" intro list. + - The "Auth" bullet under "What's inside the server". + Mention that `external-apps` is the intended account for third-party callers, unrestricted for now. + +5. **Deploy** — commit the compose + doc changes on `main`, then fire-and-forget `POST /api/v1/deploy?uuid=` on Coolify. Do not poll. + +## Ordering constraint + +Set the Coolify env var **before** pushing the compose change. Otherwise the compose parser substitutes an empty string, the config lands as `password: ""`, and until the next deploy anyone who connects with empty credentials for user `external-apps` is authenticated. Order: + +1. PATCH the env var into Coolify. +2. Commit + push the compose + doc changes. +3. Trigger the deploy. + +## Verification + +After the deploy triggers, connect from a workstation with the `nats` CLI as the new user and confirm auth works: + +``` +nats --server=wss://nats.tes.gd \ + --user=external-apps --password="$NATS_EXTERNAL_APPS_PASSWORD" \ + server info +``` + +A `Server ID` response = success. An auth error = the env var didn't land or the substitution failed; check `docker logs` on the Coolify host per the existing "Gotchas" section of `CLAUDE.md`. + +## Out of scope + +- Scoped/restricted permissions for external callers (deliberate — noted above). +- Rotating the existing `admin` or `volcanic-agents` credentials. +- Adding a UI or self-service flow for provisioning further users.