# SPEC-007 — Deploy + hosts Two uberspace hosts, one release: **fjerkroa** (service `kroa`, config `kroa.toml`) and **ggg** (service `luma`, config `ggg.toml`). Deploys are push-based from the dev machine — `git archive ` over ssh, no repo credentials on the hosts (D-015). Coverage here is `manual`: rows in `manual-verification.md` with date + result. ### DEP-01 — Deploys go by tag, in place, configs survive (coverage: manual) `deploy/deploy.sh ` refuses unknown hosts and refs that are not tags. The tag's tree is extracted over `~/fjerkroa_bot` — untracked files (live TOML config, `history/`, news snapshot) are never touched. Dev-on-host drift ends here: hosts run tag content only. ### DEP-02 — Per-host service map (coverage: manual) fjerkroa → supervisord program `kroa`, config `kroa.toml`; ggg → program `luma`, config `ggg.toml`. The script owns this map; restart via `supervisorctl restart `. ### DEP-03 — Pre-deploy state backup (coverage: manual) Before the restart, every `bot.db` under `~/fjerkroa_bot` is copied to `bot.db.pre-` on the host. Schema migrations are forward-only (PER-06) — rolling back past a schema bump means restoring this backup. ### DEP-04 — Smoke test gates the deploy (coverage: manual) After restart the script fails loudly unless the service reports RUNNING and the log shows a fresh Discord login line. On failure the operator instruction is printed: deploy the previous tag (DEP-06). ### DEP-05 — No kroa deploys during service hours (coverage: manual) Deploys to fjerkroa between 11:00 and 22:00 Europe/Oslo are refused unless `DEPLOY_FORCE=1` is set. The restaurant does not beta-test during dinner. ### DEP-06 — Rollback is a deploy of an older tag (coverage: manual) `deploy.sh ` is the rollback path; when the schema version moved, restore the DEP-03 backup first. Venv is rebuilt from the tag's pyproject either way.