diff --git a/docs/deploy.md b/docs/deploy.md
index 1146a8b..6524dbb 100644
--- a/docs/deploy.md
+++ b/docs/deploy.md
@@ -4,27 +4,51 @@
> container to follow a few consistent rules and deploying is mostly handing us a
> `compose.yaml`.
-!!! tip "What can run here"
+!!! success "What can run here"
APIs, websites, applets, bots, monitors.
## Preparing for Deploy
-Get these three right and your repo is deployable — you don't run any deploy commands
-yourself, the ops tooling takes it from git. Each tab is one piece.
+!!! abstract "The whole flow"
+ You build and test locally — leaning on our libraries, AI, and this handbook —
+ then push a branch and merge to `main`. From there the ops tooling takes over: it
+ builds the Docker image and runs it on the fleet. **You don't run any deploy
+ commands** — your only job is the three pieces in the tabs below.
+
+```mermaid
+flowchart LR
+ subgraph build ["you build"]
+ direction TB
+ local["local testing"]
+ libs["libs
(rethink-public, pip)"]
+ ai["AI-assisted
(Claude Code)"]
+ docs["reading the
handbook"]
+ local --- libs
+ libs --- ai
+ ai --- docs
+ docs --- local
+ end
+
+ build -->|branch| git["git
(Gitea)"]
+ git --> main["main"]
+ main --> deploy["deployment
(ops tooling)"]
+ deploy --> docker["docker
(image built + run on the fleet)"]
+
+ classDef ship fill:#061541,stroke:#569bcc,color:#eef1f6;
+ classDef work fill:#0e1530,stroke:#294274,color:#eef1f6;
+ class git,main,deploy,docker ship;
+ class local,libs,ai,docs work;
+```
!!! danger "Your compose names nothing repo-specific"
- **No `container_name`. No hardcoded names, workspace, or `/srv` paths.** The
- deploy layer injects identity and host paths at deploy time — your compose stays
- generic so it can't collide with any other service on the fleet.
+ **No `container_name`, no hardcoded names/paths.** The deploy layer injects
+ identity and host paths so your compose can't collide with any other service on
+ the fleet:
- the service key is **always `svc`** — it never changes, in any repo
- named volumes use **bare names** (`cache`, not `myapp_cache`)
- host paths come from **`${LOGS_DIR}` / `${CONFIG_DIR}`**
- Naming things after your repo used to cause **container-name collisions** at
- fleet scale — two repos shipping the same name clashed. The generic compose in
- the **Compose** tab fixes that.
-
=== "Compose"
The `compose.yaml` your repo ships. Copy it verbatim — it names nothing
@@ -49,38 +73,22 @@ yourself, the ops tooling takes it from git. Each tab is one piece.
cache: # bare name — deploy auto-prefixes it per service
```
- **The rules:**
-
- - **Service key is always `svc`** — never a repo-specific name, never a
- `container_name`. The ops tooling keys off `svc`; this is what makes services
- collision-proof on the fleet.
- - **`user: "1337:1337"` and `restart: unless-stopped` are required** — the shared
- `services` account (uid/gid 1337, fixed fleet-wide), and Docker's restart policy
- recovering a crashed container (the host unit is oneshot).
- - **Host paths come from `${...}` variables, never hardcoded.** Write to
- `${LOGS_DIR}` and `${CONFIG_DIR}`; `${MOUNTS_DIR}` is optional.
- - **Named volumes use bare names** (`cache`, not `myapp_cache`) — deploy
- auto-prefixes them per service so they can't collide.
-
- !!! tip "The container path is where your code reads and writes"
+ !!! example "The container path is where your code reads and writes"
`WORKDIR` is `/app`, so the **right-hand side** of each volume line is the path
- your code targets. If your app writes to `./cache` (i.e. `/app/cache`), that's
- the `cache` mount; logs go to `/app/logs`, config is read from `/app/config`.
+ your code targets. Write to `./cache` (i.e. `/app/cache`) → the `cache` volume;
+ logs go to `/app/logs`, config is read from `/app/config`.
- **Three tiers of storage:**
+ **Storage, three tiers:**
- - **`logs`** (output) and **`config`** (input you edit) — **we handle these**:
- injected and managed at deploy time.
- - **`mounts`** (optional) — a host dir for arbitrary read/write data. The deploy
- layer **injects `MOUNTS_DIR` and auto-creates the dir**, but the mount line is
- **opt-in**: uncomment it and choose the container path yourself (it's
- app-specific, so it can't be auto-mounted).
- - **Named volumes** (`cache`, …) — **yours**: anything else your service persists.
- Docker owns them, no host paths to manage.
+ - **`logs` + `config`** — we inject and manage these at deploy time.
+ - **`mounts`** (optional) — host dir for arbitrary data. We inject `MOUNTS_DIR` and
+ create the dir; you **uncomment** the line and pick the container path.
+ - **Named volumes** (`cache`, …) — yours; Docker owns them, no host paths to manage.
- Locally, `docker compose up` needs nothing set — the `${VAR:-./default}` fallbacks
- use `./logs`, `./config` (and `./mounts` if you enable it). When deployed, the ops
- tooling sets the real values, so the same file works both places.
+ !!! success "Same file, both places"
+ Locally, `docker compose up` needs nothing set — the `${VAR:-./default}`
+ fallbacks use `./logs` / `./config`. When deployed, the ops tooling sets the
+ real values. One `compose.yaml` works everywhere.
**Subprocess and browser workloads** (bots that spawn Chrome, Xvfb, ffmpeg) need
three extra knobs:
@@ -173,35 +181,3 @@ yourself, the ops tooling takes it from git. Each tab is one piece.
**Rotating a secret** is a host-side edit — update the file and the service picks
it up on restart. No rebuild, and nothing you run: flag it and we handle the
restart.
-
-## The path to deploy
-
-You build and test locally — leaning on our libraries, AI, and this handbook — then
-push to git. From there it's staging (if you're running gitflow) and, once it's good,
-deployment: the ops tooling builds the Docker image and starts it on the fleet.
-
-```mermaid
-flowchart LR
- subgraph build ["you build"]
- direction TB
- local["local testing"]
- libs["our libraries
(rethink-public)"]
- ai["AI-assisted
(Claude Code)"]
- docs["reading the
handbook"]
- local --- libs
- libs --- ai
- ai --- docs
- docs --- local
- end
-
- build -->|push| git["git
(Gitea)"]
- git -.->|develop, if gitflow| stage["staging"]
- git --> deploy["deployment
(ops tooling)"]
- stage --> deploy
- deploy --> docker["docker
(image built + run on the fleet)"]
-
- classDef ship fill:#061541,stroke:#569bcc,color:#eef1f6;
- classDef work fill:#0e1530,stroke:#294274,color:#eef1f6;
- class git,stage,deploy,docker ship;
- class local,libs,ai,docs work;
-```