docs(deployment): note install.sh commit injection + HTTPS reachability check

This commit is contained in:
Jannis Braun
2026-07-01 18:14:38 +02:00
parent 20ba7b42f9
commit 737402b5d2
+12 -2
View File
@@ -36,7 +36,12 @@ Backspace ships as a single application container fronted by Caddy. Everything i
The server is run through `tsx` (no separate transpile step); TypeScript is executed directly at runtime.
**AGPL § 13 commit injection.** The runtime stage declares `ARG BACKSPACE_COMMIT` + `ENV BACKSPACE_COMMIT=$BACKSPACE_COMMIT` so the running build's git commit is baked into the image and read by `config.commit` (exposed via `GET /api/instance/info`). `docker-compose.yml` forwards it through `build.args: { BACKSPACE_COMMIT: ${BACKSPACE_COMMIT:-} }`, and `deploy.sh` captures `git rev-parse --short HEAD` locally (the remote has no `.git` after rsync) and exports it inline before the remote `docker compose up -d --build`. Empty/unset → `config.commit` is `null` (local dev, or git unavailable). The source URL itself is `config.sourceCodeUrl` (env `BACKSPACE_SOURCE_URL`, default upstream) — operators running a modified build MUST set it to their fork.
**AGPL § 13 commit injection.** The runtime stage declares `ARG BACKSPACE_COMMIT` + `ENV BACKSPACE_COMMIT=$BACKSPACE_COMMIT` so the running build's git commit is baked into the image and read by `config.commit` (exposed via `GET /api/instance/info`). `docker-compose.yml` also declares it under `build.args: { BACKSPACE_COMMIT: ${BACKSPACE_COMMIT:-} }` (so a bare `docker compose build` picks it up from the environment). Both first-party build paths capture `git rev-parse --short HEAD` and feed it to the build:
- **`install.sh`** (the public first-time path) reads the commit from the checkout the operator cloned and passes it explicitly as `docker compose build --build-arg BACKSPACE_COMMIT=<sha>`. `--build-arg` is used instead of an exported env var because it survives the sudo/non-sudo `$COMPOSE` split (an exported var would be stripped by `sudo`). A tarball install (no `.git`) yields an empty arg → `null`.
- **`deploy.sh`** captures the commit locally (the remote has no `.git` after rsync) and exports it inline before the remote `docker compose up -d --build`.
Empty/unset → `config.commit` is `null` (local dev, tarball install, or git unavailable). The source URL itself is `config.sourceCodeUrl` (env `BACKSPACE_SOURCE_URL`, default upstream) — operators running a modified build MUST set it to their fork.
### Run: `docker compose up -d --build`
@@ -60,7 +65,12 @@ Caddy provisions and renews TLS certificates automatically for `DOMAIN`; the per
### First-time setup: `install.sh`
`./install.sh` is the interactive installer for a fresh Linux host. It prompts for the domain (or reads `DOMAIN=… ./install.sh`), generates a `JWT_SECRET`, writes `.env`, optionally configures LiveKit (`livekit.yaml` + `COMPOSE_PROFILES=voice`), and brings the stack up with `docker compose up -d --build`.
`./install.sh` is the interactive installer for a fresh Linux host. It prompts for the domain, whether to enable voice, and an instance name (each skippable via the `DOMAIN` / `ENABLE_VOICE` / `INSTANCE_NAME` env vars for a non-interactive run), generates a `JWT_SECRET`, writes `.env`, optionally configures LiveKit (`livekit.yaml` + `COMPOSE_PROFILES=voice`), and brings the stack up (`docker compose build` then `up -d`).
**Post-install HTTPS reachability check.** The container healthcheck only proves the app is up *inside* Docker — not that `https://DOMAIN` actually works, which additionally requires Caddy to have obtained a publicly-trusted certificate (DNS pointing here **and** ports 80/443 reachable from the internet). After the stack is healthy, the installer verifies this and reports it honestly instead of always printing success:
- It polls `curl -fsS --resolve DOMAIN:443:127.0.0.1 https://DOMAIN/api/health` for ~30 s. Using `--resolve` connects to the **local** Caddy while presenting the real SNI/Host and performing full certificate verification, so a pass proves a valid public cert is installed *and* the app answers over TLS. This is deliberately **hairpin-safe**: many self-hosted boxes cannot reach their own public address (router NAT hairpin), so a plain external self-request would false-negative even when the site is fine for everyone else.
- **Live** → the summary shows `HTTPS: Live`. **Not live yet**`HTTPS: Not live yet` plus guidance (point DNS here, open/forward ports 80/443, watch `docker compose logs -f caddy`); Caddy keeps retrying and HTTPS comes up automatically once both are in place.
### Redeploy: `deploy.sh [pi|vm|all]`