Files
minabot/docs/PRODUCTION.md

98 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Minabot production
Live: https://mina.teppelinlabs.com
SSH: `ssh syntaxbullet@46.62.156.93` (key authentication; passwordless sudo).
Deployment root: `/opt/minabot`. Compose manages `app`, `caddy`, and `monitor`.
Docker starts at boot; containers use `restart: unless-stopped`.
## Release and configuration
Current image: `minabot:20260907-fonts-1`, built on the Linux amd64 server from
commit `1721ee4`, including development write protection and bundled share fonts.
Its immutable image ID is recorded by `docker image inspect`; source is retained
under `/opt/minabot/releases/20260907-fonts-1`, with `source.tar.gz` and build
logs in `/opt/minabot`. Bun 1.3.14 and Caddy images are pinned by digest.
`/opt/minabot/.env` selects release images. `/opt/minabot/.env.production` contains
runtime secrets (0600). Discord client and bot credentials were copied from the
effective development environment; production has a separate generated cookie
signing secret. Runtime uses `APP_ORIGIN=https://mina.teppelinlabs.com`,
`TRUST_PROXY=true`, `HOST=0.0.0.0`, and `DATABASE_PATH=/data/minabot.sqlite`.
`TRUST_PROXY` only accepts HTTPS from `X-Forwarded-Proto`, retains the actual
request Host, and must only be enabled behind the private Caddy proxy. Port 3000
is not published. Caddy exposes TCP 80/443 and persists certificate state in
`minabot_caddy_data` and `minabot_caddy_config`. SSH remains on 22. Fail2ban uses
the iptables-compatible SSH action alongside Docker/UFW.
The registered Discord callback is
`https://mina.teppelinlabs.com/api/auth/discord/callback`; localhost remains registered.
## Data and backups
`/srv/minabot/data` is bind-mounted at `/data`, owned by UID 1000. A verified
`VACUUM INTO` snapshot copied the development database, including its one account,
four habits, history, reminders, and live Discord state. The transfer hash and
counts are recorded in `/opt/minabot/migration-manifest.json`. Local development
was stopped before the snapshot so only production manages the existing live cards.
Development now blocks channel writes at both snapshot-send and live-card entry
points. It also skips copied live-card recovery and change scheduling. Preview and
download remain available. Stubbed tests inject their own transport/configuration;
real writes through the environment loader are enabled only in production.
Daily verified snapshots and pre-migration snapshots are stored under
`/srv/minabot/data/backups`, retaining seven snapshots. The restore rehearsal
passed using a separate file; production data was not replaced by the rehearsal.
Restore revokes sessions and disables reminder opt-ins; see [OPERATIONS.md](OPERATIONS.md).
Off-server backup replication and delivered uptime alerts are **not configured**:
the operator has no destinations set up yet. The monitor checks public HTTPS every
minute in a separate container on this server and writes failure/recovery events
to its logs. It cannot report a complete server outage to an external destination.
## Common commands
Run after SSH login:
```sh
cd /opt/minabot
sudo docker compose ps
sudo docker compose logs --tail=100 app caddy monitor
sudo docker compose exec -T app bun scripts/backup.ts
sudo docker compose exec -T monitor bun scripts/monitor.ts --once
```
When sending several shell commands over SSH stdin, redirect noninteractive
`docker compose exec` calls from `/dev/null` so they do not consume later commands.
For updates, build a new release tag, run the container smoke test, take a backup,
update `APP_IMAGE` in `.env`, stop the old app, then run
`sudo docker compose up -d --wait --wait-timeout 120`. Never run overlapping app
workers or `docker compose down -v`. Keep the last known-good image and a matching
pre-migration snapshot. The initial `-1` image predates the proxy fix and is not
a suitable rollback target for working production sign-in.
## Verified September 7, 2026
- Typecheck, build, 197 tests, and production HTTP smoke tests passed.
- The Linux runtime passed migrations, native 1920×1080 PNG rendering, and
persistence across a process restart using a disposable database.
- Public HTTPS health returns `{"status":"ok"}`; HTTP redirects to HTTPS.
- Browser Discord sign-in completed and displayed the migrated account and habits.
- Read-only authenticated production API/PNG checks passed; unauthenticated access
and cross-origin writes were rejected. Temporary verification sessions were removed.
- Snapshot integrity, restore rehearsal, and transferred database hash passed.
- Application port 3000 is unpublished and an external HTTP probe timed out.
- Docker and Fail2ban are active; runtime secrets and the SQLite file are mode 0600.
Live Discord test posts/DMs were not sent. Existing configured workers continue
their normal production behavior.
## Share fonts
Instrument Serif and Noto Sans (400/500/600) are bundled for both browser and native
canvas share renderers. Mac/Linux fixture text metrics match exactly; pixel-channel
differences are at most 2/255 from native rasterization. Missing native font files
fail startup. Renderer version 3 refreshes existing live cards with the new fonts.