summaryrefslogtreecommitdiff
path: root/docs/DEPLOY.md
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-17 19:55:36 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-17 19:55:36 +0200
commit380195f7cd5e57acf2c1cf2bc41069e6b0b979ed (patch)
tree32a88fb22a7fbe8f1fd5c105156d1f928c93950d /docs/DEPLOY.md
downloadbokf-0.1.0.tar.gz
bokf-0.1.0.zip
Initial commit: daemon, clients, docs, Docker deploy pipelinev0.1.0
Diffstat (limited to 'docs/DEPLOY.md')
-rw-r--r--docs/DEPLOY.md156
1 files changed, 156 insertions, 0 deletions
diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md
new file mode 100644
index 0000000..5def5df
--- /dev/null
+++ b/docs/DEPLOY.md
@@ -0,0 +1,156 @@
+# bokf — running in Docker
+
+Status: Draft 0.1 · 2026-09-17 · License: GPL-3.0-or-later
+
+Two roles:
+
+- **host** — runs Docker + the compose plugin only. No toolchain, no registry.
+- **dev machine** — has the source and builds the image; deploys with
+ `scripts/deploy.sh` over SSH.
+
+There is no forge or registry in this flow. Releases are git tags, images are
+transferred directly with `docker save | ssh docker load`.
+
+State lives in two bind mounts next to `compose.yaml`:
+
+| Host path | Container | Contents |
+|---|---|---|
+| `var/db` | `/var/lib/bokfd` | SQLite database, `backup/`, `export/` |
+| `var/run` | `/run/bokfd` | Unix socket (mode 0660, owned by uid 10001) |
+
+The database is a single SQLite file. Back up with `backup.snapshot`
+(`VACUUM INTO`) and point restic at `var/db/backup` — never at the live file.
+
+## First run
+
+On the dev machine, put the SSH target in `.env`:
+
+```sh
+cp .env.example .env
+# set BOKF_HOST=user@host and BOKF_REMOTE_DIR=/srv/bokf
+scripts/deploy.sh v0.1.0
+```
+
+The first run stops after shipping the image and compose.yaml, and prints the
+one-time init command. Run it on the host:
+
+```sh
+cd /srv/bokf
+docker compose run --rm -e BOKFD_PASSWORD='<admin-password>' bokfd init
+docker compose up -d
+docker compose ps # wait for "healthy"
+```
+
+`init` creates the admin user and must run before the first `up`; it refuses
+to touch an already initialized database. Subsequent `scripts/deploy.sh`
+runs update the image, tag and compose file, restart the daemon and wait for
+the healthcheck.
+
+## Deploying upgrades
+
+```sh
+git tag v0.1.1
+scripts/deploy.sh # tag defaults to git describe
+scripts/deploy.sh v0.1.1 # or pass one explicitly
+```
+
+The script:
+
+1. `make` + `make test` on the dev machine,
+2. builds `bokf:<tag>`: locally and ships it with `docker save | gzip | ssh
+ docker load`, or — when the host runs a different CPU architecture —
+ builds it natively on the host from a source tar,
+3. copies `compose.yaml` and writes `BOKF_IMAGE`/`BOKF_TAG` into the host's
+ `.env` (other keys are preserved),
+4. `docker compose up -d --no-build`, then polls the container healthcheck,
+5. on failure, puts the previous `BOKF_TAG` back and rolls back to the image
+ that is still loaded on the host.
+
+Cross-architecture builds are automatic: `uname -m` is compared over SSH and
+a mismatch switches to a remote build. Override with `BOKF_BUILD=local` or
+`BOKF_BUILD=remote` (also settable in `.env`). A remote build pulls the
+Debian base image inside a container, so the host needs outbound network
+access but still no toolchain.
+
+Tags are `git describe` output unless passed. Tag releases (`v*`) so rollback
+and support have meaningful versions. The rollback image must still exist on
+the host; don't prune before the new version has proven itself.
+
+Manual rollback — set `BOKF_TAG` on the host and restart:
+
+```sh
+cd /srv/bokf
+sed -i 's/^BOKF_TAG=.*/BOKF_TAG=v0.1.0/' .env
+docker compose up -d --no-build
+```
+
+## Optional: a bare repository on the host
+
+For an off-machine copy of the source and an optional auto-deploy hook:
+
+```sh
+ssh host 'git init --bare /srv/git/bokf.git'
+git remote add host ssh://host/srv/git/bokf.git
+git push host main
+```
+
+If the host also has the toolchain and a checkout whose origin is that bare
+repo, `deploy/post-receive.sample` can build, test and restart on every push
+to `main`. The normal `scripts/deploy.sh` flow does not need any of this.
+
+## Clients
+
+Run clients inside the container — no host toolchain needed:
+
+```sh
+docker compose exec bokfd bokftui # interactive TUI
+docker compose exec -e BOKFD_PASSWORD='<pw>' bokfd \
+ bokfctl --user admin fiscal_year.list
+```
+
+The clients honor `BOKFD_SOCKET`; a host-installed client can also point at
+`var/run/bokfd.sock`, but that file is owned by uid 10001, so the host user
+must be in that group (or use `sudo`).
+
+## Mock company
+
+```sh
+docker compose exec -e BOKFD_PASSWORD='<pw>' bokfd \
+ bokfctl --user admin org.create \
+ '{"name":"Mock AB","org_nr":"556000-0000","fiscal_year_start_month":1}'
+```
+
+Develop against this org; agents get their own API token
+(`bokfctl token.create ...`, shown once).
+
+## Backup and restore
+
+```sh
+docker compose exec -e BOKFD_PASSWORD='<pw>' bokfd \
+ bokfctl --user admin backup.snapshot
+ls var/db/backup # <db>-<timestamp>.db + .sha256
+```
+
+Restore:
+
+```sh
+docker compose stop
+cp var/db/backup/<snapshot>.db var/db/bokfd.db
+rm -f var/db/bokfd.db-wal var/db/bokfd.db-shm
+docker compose start
+```
+
+To replace the mock with a real database from another host, restore its
+snapshot the same way (same or newer bokf version; older schemas migrate
+forward). SIE import is the alternative once the importer's CRLF/`#RAR`
+fixes land.
+
+## Local development
+
+```sh
+make -j"$(nproc)" && make test # no Docker required
+docker compose up --build # same image, local var/ data dir
+```
+
+`scripts/deploy.sh` always runs the tests before shipping. `var/`, `.env`,
+`*.db` and `*.se` are gitignored — real books never enter the repository.