feat(train): the instance's own map tiles, drawn by Train's style (ADR 0030, D50)

Protomaps' basemap cut for Europe and served by pmtiles beside Train's server
(compose.maps.yaml), drawn by Train's own style, light and dark, which the
server serves. Only people signed in get tiles: map.source hands a key that
Caddy has the server check before each tile; nothing logs a tile.

Also fixes .dockerignore, which left apps/train/ out of the build context, so
Train's server's and BRouter's images couldn't have been built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 16:04:05 +01:00
commit 190c3872f4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -20,6 +20,7 @@ site changes.
| Jellyfin, which people watch and listen to learn from | `compose.jellyfin.yaml` | `jellyfin.caddy` |
| LanguageTool, which checks spelling and grammar | `compose.languagetool.yaml` | none |
| Halkyon Train's server, with BRouter for its routes | `compose.train.yaml` | `train.caddy` |
| Train's own map tiles, beside Train | `compose.maps.yaml` | `train-maps.caddy` (in Train's site) |
Both together: `COMPOSE_FILE=compose.yaml:compose.jellyfin.yaml:compose.languagetool.yaml`.
@ -225,3 +226,49 @@ Steps, in order; ✱ marks a step as root.
7. **Check it**: `https://train.halkyon.eu/` answers; in Train, signed in with a Learn
account, a route plans between two points of the region.
## Train's map
The instance's own map for Halkyon Train (Halkyon Train's ADR 0030, D50), beside Train
(`compose.train.yaml` first): Protomaps' basemap cut for Europe from Protomaps' daily
build (the planet's was 139 GB on 29 Oct 2026; Europe's cut is estimated at some 45 GB,
to be measured), served by `pmtiles serve` and drawn by Train's own style, which Train's
server serves. Only people signed in to Train get tiles: `map.source` hands the app a
key, which Caddy has Train's server check before each tile. No tile is logged. The cut
is renewed monthly where Protomaps has a newer build, written beside the old one and
moved over it. Off, Train's maps are OpenFreeMap's (or `MAP_STYLE_URL`'s) again.
Steps, in order; ✱ marks a step as root.
1. **Room on the disk** (the operator, on the instance's server): `df -h ~deploy`.
A cut takes some 45 GB, and twice that while a new one is cut beside it.
2. ✱ **The tiles' user and folder** (the operator, as a user with sudo): the user and
group ID 61178 are the tiles' alone, as the compose file runs them.
```sh
sudo groupadd --system --gid 61178 maptiles
sudo useradd --system --uid 61178 --gid 61178 --no-create-home --shell /usr/sbin/nologin maptiles
sudo mkdir -p ~deploy/halkyon-learn/deploy/maps/tiles
sudo chown -R maptiles:maptiles ~deploy/halkyon-learn/deploy/maps/tiles
```
3. **Switch it on** (the operator, as `deploy`): in `deploy/.env`, `compose.maps.yaml`
added to `COMPOSE_FILE` after Train's, and the secret the keys are made with:
```sh
COMPOSE_FILE=compose.yaml:compose.languagetool.yaml:compose.train.yaml:compose.maps.yaml
MAP_KEY_SECRET=<openssl rand -hex 32>
```
`MAP_STYLE_URL` is then not used. Changing `MAP_KEY_SECRET` refuses every key handed
out before, until the apps ask for the source again (as each opens).
4. **Start it** (the operator, as `deploy`): `docker compose up -d --build`. Caddy and
Train's server are recreated with it. The first cut takes hours
(`docker compose logs -f map-tiles-refresh` says when it is done); until then Train's
maps stay empty.
5. **Check it**: in Train, signed in, a workout's map is drawn and names the instance as
its source; `curl -I https://train.halkyon.eu/tiles/europe/0/0/0.mvt` is refused
with 401. Not yet tried on the server (29 Oct 2026): whether `pmtiles serve` reads a
new cut moved over the old one without a restart (else `docker compose restart
map-tiles` after each cut), and Caddy's `import` of an empty folder where the map is
off.