diff --git a/CLAUDE.md b/CLAUDE.md index b577d09..3606d1f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -135,6 +135,21 @@ To add a second user (e.g. a scoped app account), edit the `authorization.users` - **Coolify's deploy status ≠ container health.** A deploy returning `status: "finished"` from `/api/v1/deployments/…` just means the build + `docker compose up -d` step ran to completion. If the container then crash-loops (e.g. bad `nats-server.conf`), the deploy is still "finished" but the app's `/api/v1/applications/{uuid}` shows `status: "restarting:unknown"` and `/api/v1/applications/{uuid}/logs` returns `Application is not running.` — no runtime logs via API. Fall back to `docker logs ` on the Coolify host to see why the process is dying. Container name pattern: `nats--`. +- **`docker_compose_domains` regresses after config-touching deploys.** The stored value on this app is `{"nats":{"domain":"https://nats.tes.gd:8080"}}` — object-keyed, no `name` field. Coolify's Traefik-label generator wants each entry to carry an explicit `name`; without it, on some deploys the labels fall back to defaults (`Host(.tes.gd)` on port 80) and `wss://nats.tes.gd` stops routing to the container even though the container itself is `running:healthy`. Symptom: `curl https://nats.tes.gd/` returns a 404 or lands on an unrelated service; NATS logs show `websocket handshake error: invalid value for header 'Upgrade'` for requests that do slip through. Fix by PATCHing with the **array form** (per `deploying-to-coolify-via-api` skill), then redeploying: + + ```bash + APP_UUID=$(jq -r '.app.uuid' deploy.json) + curl -sS -X PATCH "$COOLIFY_URL/api/v1/applications/$APP_UUID" \ + -H "Authorization: Bearer $COOLIFY_KEY" -H 'Content-Type: application/json' \ + -d "$(jq -c '.app.docker_compose_domains | to_entries | map({name: .key, domain: .value.domain}) | {docker_compose_domains: .}' deploy.json)" + curl -sS -X POST "$COOLIFY_URL/api/v1/deploy?uuid=$APP_UUID" \ + -H "Authorization: Bearer $COOLIFY_KEY" + ``` + + Verify by curling `https://nats.tes.gd/`: you want `HTTP 400 Bad Request` with a `sec-websocket-version: 13` response header (NATS's WS handler answering a non-Upgrade request). Anything else means Traefik still isn't pointed at the NATS container. + +- **`nats` npm package doesn't speak WebSocket from Node.** From Node, `import { connect } from "nats"` with `servers: "wss://…"` fails with `CONNECTION_REFUSED` — that package is TCP-only from Node. For Node-side WebSocket clients use `nats.ws` with a `ws` polyfill (see the "With `nats.js` / `nats.ws`" snippet earlier in this file); in the browser `nats.ws` works directly. + ## Files in this repo | File | Purpose |