The bot's secrets live in Gitea: signed settings travel with the release bundle

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>
This commit is contained in:
2026-11-02 21:37:35 -06:00
parent b05f129bed
commit 1e4c0e88dc
7 changed files with 106 additions and 8 deletions

View File

@@ -50,6 +50,12 @@ signed with the repository secret `BOT_DEPLOY_SECRET`; the server verifies the s
keeps the previous release for rollback, and restarts the container. The install log comes back in your CI
job, so a failed deploy fails the run. Branches and pull requests test and build, and deploy nothing.
**Your settings travel with it.** `DISCORD_BOT_TOKEN` (an Actions secret) and `DISCORD_GUILD_ID` (an Actions
variable) are signed together with the bundle and written into the container's environment by the server, so
this repo is where they are managed and rotated. The server accepts only the names its `BOT_ENV_KEYS` allows
and reports the rest as `settings_refused`; it keeps ownership of what it generates, such as `DB_PASSWORD`.
A new setting means asking the admin to add its name to that list (§6).
Rules that follow from this:
- Keep `requirements.txt` resolvable with **prebuilt wheels only**; a dependency that must compile will fail

View File

@@ -12,6 +12,22 @@ release kept for `--rollback`) and restarts the container. The install log comes
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