POST /deploy-bot may now carry X-Vanguard-Env, base64 JSON of the bot's own settings, and the HMAC covers that header together with the body so the pair cannot be separated or swapped. dumpd merges only the keys BOT_ENV_KEYS allows (DISCORD_BOT_TOKEN, DISCORD_GUILD_ID by default) into the stack .env, atomically and preserving mode and owner, and reports the rest to the CI job as settings_refused. The server keeps what it generates: a CI job cannot touch the database password or the Authentik keys. Verified: allowed keys applied, server-owned keys refused while the rest still applied, a settings swap after signing rejected with 401, and an empty settings header leaving .env untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
77 lines
4.7 KiB
Markdown
77 lines
4.7 KiB
Markdown
# discord-bot — for the bot dev
|
|
|
|
Start with **AGENT.md** in this folder: the stack, the rules, the layout, the web-UI auth contract.
|
|
|
|
## How code reaches the server
|
|
|
|
Your repo's Gitea Actions workflow builds a **release bundle**: one tar.gz holding `main.py`, `app/`,
|
|
`website/`, `version.json`, `BUILD.json` and `vendor/` (your dependencies as prebuilt wheels). **A push to
|
|
`main` installs itself**: the workflow's last job posts the bundle to the server's deploy service, which
|
|
checks its signature and every checksum, swaps it into `/srv/bot/app` (bind-mounted at `/app`, previous
|
|
release kept for `--rollback`) and restarts the container. The install log comes back in the CI job.
|
|
|
|
A branch or pull request runs the tests and builds the artifact, and deploys nothing.
|
|
|
|
## Your secrets live in Gitea
|
|
|
|
Set them on this repo under Settings → Actions. The deploy job sends them with the bundle, signed, and the
|
|
server writes the ones it allows into the environment the container gets:
|
|
|
|
| where | name | what |
|
|
|---|---|---|
|
|
| Secrets | `BOT_DEPLOY_SECRET` | signs the upload; must match the server (the admin has it) |
|
|
| Secrets | `DISCORD_BOT_TOKEN` | the bot token |
|
|
| Variables | `DISCORD_GUILD_ID` | the Discord server id |
|
|
|
|
Rotating the token is an edit here plus a re-run of the workflow; nobody needs to touch the server. The
|
|
server keeps what it generates, chiefly `DB_PASSWORD`, and accepts only the names its `BOT_ENV_KEYS` lists —
|
|
anything else is reported in the job as `settings_refused`. Need a new setting? Ask the admin to add its name
|
|
to that list, then add it here.
|
|
|
|
The image contains **no** bot dependencies: the container imports yours from `/app/vendor`
|
|
(`PYTHONPATH=/app/vendor`). Adding a dependency means editing `app/requirements.txt` and pushing — no image
|
|
rebuild, no package index on the server. The image only changes if you need a new system package (ffmpeg and
|
|
curl are already there); ask the admin for that.
|
|
|
|
## Day to day (on the server, in ~/vanguard-stream)
|
|
./deployBotBundle.sh vanguard-bot-bundle.zip # install a release by hand (zip or tar.gz)
|
|
./deployBotBundle.sh --status # what is installed; container state
|
|
./deployBotBundle.sh --rollback # previous release
|
|
docker compose logs -f discord-bot # live log
|
|
docker compose restart discord-bot # restart without changing code
|
|
docker compose exec discord-bot python # a REPL inside the container (your vendor/ is on sys.path)
|
|
|
|
## Environment the container gives you
|
|
| var | value |
|
|
|-----|-------|
|
|
| `DISCORD_BOT_TOKEN` | from `.env` (stack root) — the Bot added to the existing Authentik Discord application |
|
|
| `DISCORD_GUILD_ID` | the server |
|
|
| `DB_BACKEND` | `mariadb` |
|
|
| `DB_HOST` / `DB_PORT` | `host.docker.internal` / `3306` (MariaDB runs on the host network) |
|
|
| `DB_NAME` / `DB_USER` / `DB_PASSWORD` | your own database + user (`vanguardbot` / `bot` / generated) |
|
|
| `DATA_DIR` | `/data` — persistent, owned by you |
|
|
| `MEDIA_CLIPS` | `/media/clips` — rw mount of `/srv/media/clips` (folder per user, for clip submissions) |
|
|
| `WEB_HOST` / `WEB_PORT` | `0.0.0.0` / `8000` — published as `https://bot.<domain>/` via Caddy + Authentik |
|
|
| `WEB_PUBLIC_URL` | `https://bot.<domain>` for absolute links |
|
|
| `WEB_SERVE_UI` | `true` — serve your pages at `/` on the same port as `/api` |
|
|
| `WEB_ALLOWED_GROUPS` | `bot-users,admins` — the groups your own code must accept |
|
|
| `AUTH_HEADER_USER` / `_GROUPS` / `_EMAIL` | `X-Authentik-Username` / `X-Authentik-Groups` / `X-Authentik-Email` — identity injected by Caddy, trust these, implement no login |
|
|
| `PYTHONPATH` | `/app/vendor` |
|
|
| `TZ` | America/New_York |
|
|
|
|
SQLAlchemy URL: `mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}`
|
|
async: `mysql+aiomysql://...`
|
|
|
|
## Database from your own machine
|
|
MariaDB is reachable directly: `media.dumpstervigilantes.rip:3306`, user `bot`, database `vanguardbot`,
|
|
password from the admin (`./configure.py --show` on the server). Root is local-only. New connections are
|
|
rate limited at the firewall; if yours get dropped, ask the admin to add your IP to `db_allow`.
|
|
|
|
## Conventions
|
|
- Run as uid 1000 (files you write under `/media/clips` are owned like everything else there).
|
|
- `/app` holds a released bundle; treat it as read-only and write to `/data` instead.
|
|
- If you write clips: `/media/clips/<discord-username>/<name>.mp4` plus `<name>.json` with
|
|
`{"timestamp": "<UTC ISO-8601>", "source": "medal", "source_url": "...", "uploader": "...", "title": "..."}`
|
|
— the Jellyfin plugin reads that sidecar for the exact recording time.
|
|
- Log to stdout; the container captures it. One event per line, `key=value`, keeps it searchable (`LOGGING.md`).
|