From 737402b5d2836078d79d0eb71f6f7529a75a90ac Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 1 Jul 2026 18:14:38 +0200 Subject: [PATCH] docs(deployment): note install.sh commit injection + HTTPS reachability check --- docs/systems/deployment.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/systems/deployment.md b/docs/systems/deployment.md index f98c049d..a93807d5 100644 --- a/docs/systems/deployment.md +++ b/docs/systems/deployment.md @@ -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=`. `--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]`