Every `just deploy` now takes a pg_dumpall to
/opt/colectivo/backups/predeploy-<ts>-<sha>.sql.gz BEFORE rebuilding the
app or applying migrations, aborts the deploy if the dump is < 1 KiB,
and (on success) appends `<iso-ts> \t <sha> \t <backup-file>` to
/opt/colectivo/.deploys.log on ambrosio. Keeps the newest 10 backups
(prunes older predeploy-* files).
New `infra/scripts/rollback-erosi.sh` reads .deploys.log and pairs a
code rollback with a DB restore atomically:
just rollback-list # show recent deploys
just rollback # roll back to N-1
just rollback-to <sha> # roll back to a specific deploy
just rollback-code # roll back code only, keep current DB
Rollback safety:
- 5-second abort window.
- Verifies the target SHA exists locally + the backup is still on prod
(warns if it's been pruned past the 10-deploy window).
- Uses a temporary git worktree so the user's working tree isn't
disturbed.
- Stops app/auth/rest/realtime/storage before the gunzip|psql restore.
- Rebuilds the app image at the rolled-back SHA with the same GIT_SHA
build-arg path the deploy uses (so __APP_COMMIT__ in the bundle
matches the running code).
- Does NOT write a new .deploys.log entry — rollback is intentionally
not a deploy event; the next `just deploy` is from current HEAD.
- Storage volume (/var/lib/storage user uploads) is NOT rolled back.
Justfile `deploy` recipe repointed from the stale `deploy.sh` (which
referenced GHCR pull) to `deploy-erosi.sh` (the active prod path).
New project-scoped skill: `.claude/skills/deploy/SKILL.md` documents the
flow + safety boundaries for Claude-assisted invocations. `.gitignore`
keeps `.claude/settings.local.json` ignored but tracks `.claude/skills/`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wraps Fase 13:
- docs/history/fase-13-server-admin.md captures the full fase
breakdown (model, RPCs, UI, store wiring, tests, verification,
deviations from plan, scope out).
- docs/deployment.md gains a Server administration section with the
SERVER_ADMIN_EMAIL bootstrap recipe + the "first prod boot has no
users yet" gotcha + manual re-run command (with the </dev/null
redirect for the heredoc-stdin gotcha) + last-admin guard note +
audit log query snippet.
- CLAUDE.md "Project Status" updated: MVP2 now 5/6, Fase 13 ✅, test
totals, the one pre-existing SV-02 flake noted.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The stack no longer talks to any specific external proxy. Removed traefik.*
labels and the shared `traefik` external network from kong + app, and dropped
TRAEFIK_NETWORK / TRAEFIK_ENTRYPOINT / TRAEFIK_CERTRESOLVER from the .env
template and the deploy-script bootstrap.
Internal edge is a `caddy:2-alpine` container on the `colectivo` network,
publishing host port 3000. `infra/Caddyfile.erosi` does the path split:
/rest|/auth|/realtime|/storage|/graphql|/pg -> kong:8000, everything else
-> app:3000. Global block sets `auto_https off` + `admin off` so :3000 is
the only listener.
Whatever proxy fronts the host (NetBird's Traefik, nginx, anything) just
terminates TLS for erosi.limonia.net and forwards plain HTTP to
ambrosio:3000. Off-stack and out of this repo.
Docs (CLAUDE.md + docs/deployment.md) updated to describe the two-layer
edge. Includes the gotcha that bind-mounted file edits (Caddyfile) need
`docker compose restart caddy` since `up -d` won't recreate the container
on file-only changes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Document what we learned doing the real NetBird+Traefik migration:
- Keycloak realm/client names are operator-chosen; the realm-export.json's
literal "colectivo-web" / "colectivo" values are illustrative, not required.
- Legacy Keycloak /auth/ base path: PUBLIC_KEYCLOAK_URL must include the
suffix when the deployment serves realms under /auth/realms/... (hit this
on auth.fosil.eu). Verify with the discovery URL returning 200.
- NetBird's installer deploys Traefik with idleTimeout=0 (unlimited) by
default — verify instead of prescribing 3600s.
- Runbook: --no-cache fixes the intermittent vite SSR "transforming..."
hang that surfaces as a PostHog shutdown timeout.
- Runbook: any PUBLIC_* change needs an app rebuild (build args); secret
changes only need `docker compose restart auth`.
- Runbook: TRUNCATE recipe for wiping all app + auth data while keeping
schema + migration tracking intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ambrosio's TLS is now handled by the Traefik that NetBird's self-hosted
installer deploys. kong + app attach to that Traefik network and expose
themselves via Docker-provider labels (erosi-kong priority 100 for
Supabase API paths, erosi-app priority 1 for everything else on
erosi.limonia.net). No container publishes host ports; host Caddy is
out of the deploy path permanently.
Keycloak is no longer bundled — PUBLIC_KEYCLOAK_URL points at an
external IdP. The deploy script writes .env with FILL_IN_* placeholders
for PUBLIC_KEYCLOAK_URL, KEYCLOAK_CLIENT_SECRET, and TRAEFIK_NETWORK,
and fails fast until they are filled.
New domain: erosi.limonia.net. realm-export.erosi.json is retained
as reference for the external Keycloak operator; it is no longer
imported by this stack.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.md shrinks to rules + status + index (175 lines, down from 476).
Dev setup, prod deploy, and per-phase build records move to docs/.
Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy,
platform) and unnumbered so they don't drift as new ones land.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>