| 1 | # Caddy in front of Steid. |
| 2 | # |
| 3 | # This is the reference copy, for the manual installation path in README.md. |
| 4 | # install.sh writes the same file to /etc/caddy/Caddyfile with the domain |
| 5 | # substituted — if you change one, change the other. |
| 6 | # |
| 7 | # WHY A PROXY IS MANDATORY, not a nicety: Topcoat 0.5 has no TLS support at all — |
| 8 | # no rustls, no ACME, no HTTPS listener. Steid authenticates git over HTTP Basic, |
| 9 | # so on a plain-HTTP instance a personal access token is sent in cleartext on |
| 10 | # every clone and every push. Caddy is used because obtaining and renewing a |
| 11 | # certificate is automatic and needs no configuration beyond the hostname below. |
| 12 | # |
| 13 | # Requirements for the certificate to issue: the domain's A/AAAA record must |
| 14 | # already point at this machine, and ports 80 and 443 must be reachable. |
| 15 | |
| 16 | git.example.com { |
| 17 | # Steid listens on loopback only (HOST=127.0.0.1 in /etc/steid/steid.env), |
| 18 | # so this proxy is the only way in. |
| 19 | reverse_proxy 127.0.0.1:3000 { |
| 20 | # Git's smart HTTP protocol streams: the client sends ref negotiation and |
| 21 | # waits on a response that is generated as it goes. Buffering either |
| 22 | # direction turns a clone into a long silence and can stall negotiation |
| 23 | # outright, so flush every write straight through. |
| 24 | flush_interval -1 |
| 25 | } |
| 26 | |
| 27 | # Deliberately no `encode`. Git packfiles are already compressed and the git |
| 28 | # client sets its own Accept-Encoding; re-compressing them costs CPU on the |
| 29 | # hot path and buys nothing. |
| 30 | } |
| 31 | |
| 32 | # --- OPTIONAL: only on the instance that DISTRIBUTES Steid ------------------- |
| 33 | # |
| 34 | # Most people running Steid do not want this. It belongs on the one instance that |
| 35 | # publishes releases for everyone else — the host baked into install.sh as |
| 36 | # RELEASE_BASE_URL. That instance's site block looks like this instead: |
| 37 | # |
| 38 | # git.example.com { |
| 39 | # handle_path /jamesgill/repos/steid/releases/* { |
| 40 | # root * /srv/steid-dist |
| 41 | # file_server |
| 42 | # } |
| 43 | # handle /install.sh { |
| 44 | # root * /srv/steid-dist |
| 45 | # file_server |
| 46 | # } |
| 47 | # handle { |
| 48 | # reverse_proxy 127.0.0.1:3000 { flush_interval -1 } |
| 49 | # } |
| 50 | # } |
| 51 | # |
| 52 | # This is live on jpgill.dev; `./deploy.sh --publish` puts the files there. |
| 53 | # |
| 54 | # Four things to know, and the first two were each found the hard way: |
| 55 | # |
| 56 | # 1. `handle_path`, NOT `handle`, for the releases block. handle_path strips the |
| 57 | # matched prefix; handle does not, so a plain `handle` sends file_server |
| 58 | # looking for /srv/steid-dist/jamesgill/repos/steid/releases/v0.4.0/… and every |
| 59 | # download 404s while the configuration validates perfectly. |
| 60 | # |
| 61 | # 2. NOT /var/lib/steid/dist, which is the obvious place and is wrong twice. That |
| 62 | # directory is the instance's state and its entire backup surface — deploy.sh |
| 63 | # tars it on every deploy, so release artefacts there would be copied into |
| 64 | # every snapshot forever. And it is mode 750 steid:steid while Caddy runs as |
| 65 | # `caddy`, so serving from inside it means loosening the one permission that |
| 66 | # keeps steid.db away from other local users. |
| 67 | # |
| 68 | # 3. THESE PATHS SHADOW STEID. Caddy answers them before the application sees |
| 69 | # them. Nothing lives at /{handle}/repos/{name}/releases today, so nothing |
| 70 | # breaks — but the day Steid grows a real release feature at that URL, Caddy |
| 71 | # silently keeps winning and the feature will look broken. Delete these blocks |
| 72 | # then. The URL is deliberately the one that feature will use, so links |
| 73 | # published now survive the change. |
| 74 | # |
| 75 | # 4. /install.sh at the root is safe permanently, not by luck: OrgName allows |
| 76 | # only [a-z0-9-], so no handle can ever contain a dot and none can collide |
| 77 | # with it. A root-level /releases would NOT be safe for the same reason — it |
| 78 | # is a valid handle shape — which is why the releases path is scoped. |
| 79 | # |
| 80 | # The trailing `handle { reverse_proxy … }` is not decoration: an unwrapped |
| 81 | # reverse_proxy runs for every request, including the two above. |
| 82 | # |
| 83 | # Artefacts go into /srv/steid-dist/v<version>/ to match the URL install.sh |
| 84 | # builds: ${RELEASE_BASE_URL}/v${VERSION}/steid-<version>-<target>.tar.gz |