feat: main's public hostnames, and drop the retired legacy stack

Cloudflare's free Universal SSL covers kucial.com and *.kucial.com only, so a
hostname published through the tunnel cannot sit under dev.kucial.com's extra
level — it fails TLS at the edge whatever the tunnel says. main is therefore
reached at gig-api.kucial.com publicly and gig-platform.dev.kucial.com on the
LAN (gig-web.kucial.com for gig-app), while the LAN-only instances keep their
deeper names, which Caddy serves with its own DNS-01 certificates.

sites/legacy-gig.caddy is removed: the old preview stack is retired, and its
gig-api.kucial.com block would now collide with main's.

`caddy validate` passes against the custom Cloudflare-DNS build.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LpmavGWZa9nKFwpeLLW4D3
This commit is contained in:
2026-09-17 19:31:13 +08:00
co-authored by Claude Opus 5
parent 6379e8c147
commit 8e919c74d0
4 changed files with 24 additions and 58 deletions
+11 -19
View File
@@ -21,9 +21,8 @@ LAN: Client → split DNS → Caddy :443 → (edge) instance conta
- `docker-compose.yml`: Runs `caddy`, the infra tunnel `cloudflared`, and the preview tunnel `cloudflared-preview`. Caddy and `cloudflared-preview` join the external `edge` network shared with project instances; `cloudflared-preview` joins nothing else.
- `Caddyfile`: Global options (ACME email, client-IP trust), the `cloudflare_tls` snippet, standalone sites, and `import sites/*.caddy`.
- `sites/`: One file per project, mounted read-only at `/etc/caddy/sites`.
- `gig-platform.caddy`: gig-platform instances — `main` at `gig-platform.dev.kucial.com`, `e2e` / `pr-<N>` by hostname label, and main's permission gates on `studio.` / `minio.` / `horizon.`.
- `gig-app.caddy`: gig-app web instances — `main` at `gig-app.dev.kucial.com`, `pr-<N>` by label.
- `legacy-gig.caddy`: the old gig preview on host ports. **Delete it at gig-platform's main cutover.**
- `gig-platform.caddy`: gig-platform instances — `main` at `gig-api.kucial.com` (public name) and `gig-platform.dev.kucial.com` (LAN alias), `e2e` / `pr-<N>` by hostname label, and main's permission gates on `studio.` / `minio.` / `horizon.`.
- `gig-app.caddy`: gig-app web instances — `main` at `gig-web.kucial.com` and `gig-app.dev.kucial.com`, `pr-<N>` by label.
- `.env`: Stores `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_TUNNEL_TOKEN` and `CLOUDFLARE_PREVIEW_TUNNEL_TOKEN`.
- `data/`: Persistent volume storing Caddy's certificates.
@@ -80,11 +79,13 @@ Split DNS on the router must resolve both the apex and the wildcard of each proj
| Public Hostname | Service Type | URL |
| :--- | :--- | :--- |
| `gig-platform.dev.kucial.com` | `HTTP` | `gig-platform-main-nginx:80` |
| `studio.gig-platform.dev.kucial.com` | `HTTP` | `gig-platform-main-db-studio-gate:80` |
| `minio.gig-platform.dev.kucial.com` | `HTTP` | `gig-platform-main-minio-console-gate:80` |
| `horizon.gig-platform.dev.kucial.com` | `HTTP` | `gig-platform-main-horizon-gate:80` |
| `gig-app.dev.kucial.com` | `HTTP` | `gig-app-main-web:80` |
| `gig-api.kucial.com` | `HTTP` | `gig-platform-main-nginx:80` |
| `gig-studio.kucial.com` | `HTTP` | `gig-platform-main-db-studio-gate:80` |
| `gig-minio.kucial.com` | `HTTP` | `gig-platform-main-minio-console-gate:80` |
| `gig-horizon.kucial.com` | `HTTP` | `gig-platform-main-horizon-gate:80` |
| `gig-web.kucial.com` | `HTTP` | `gig-app-main-web:80` |
**One label each, and not by preference:** the free plan's Universal SSL covers `kucial.com` and `*.kucial.com` only, so a published `something.dev.kucial.com` fails TLS at the edge however the tunnel is configured — that level needs Advanced Certificate Manager. The `dev.kucial.com` names stay LAN-only, where Caddy issues its own DNS-01 certificates and depth does not matter.
- Route the internal surfaces only to their **gates**, never to `db-studio`, `minio:9001` or anything else. The gates enforce the platform permission on every request.
- Never publish `*.gig-platform.dev.kucial.com` or `*.gig-app.dev.kucial.com`: PR and e2e instances are LAN-only, reached through split DNS.
@@ -103,15 +104,6 @@ docker compose up -d --build
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
```
### 5. gig-platform main cutover
### 5. Reloading
When gig-platform's main instance is migrated (gig-platform `docs/deploy/instances.md`):
```bash
rm sites/legacy-gig.caddy
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
```
Then remove the `gig-api` / `gig-static` / `gig-db` / `gig-web` `.kucial.com` public hostnames from the infra tunnel.
A new `sites/*.caddy` file only needs a reload. Changing the `sites` mount or the networks in `docker-compose.yml` needs `docker compose up -d caddy`. The preview tunnel's hostnames change in the dashboard only; `cloudflared-preview` picks them up without a restart.
A new or edited `sites/*.caddy` file only needs a reload. Changing the `sites` mount or the networks in `docker-compose.yml` needs `docker compose up -d caddy`. The preview tunnel's hostnames change in the dashboard only; `cloudflared-preview` picks them up without a restart.
+3 -1
View File
@@ -2,7 +2,9 @@
# the infra Caddy's sites/ directory. Same shape as gig-platform.caddy: the
# apex is main, the wildcard routes pr-<N> by label.
gig-app.dev.kucial.com {
# gig-web.kucial.com is main's public name (one label, so free Universal SSL
# covers it); the dev apex stays as a LAN alias.
gig-web.kucial.com, gig-app.dev.kucial.com {
import cloudflare_tls
reverse_proxy gig-app-main-web:80
}
+10 -5
View File
@@ -4,11 +4,16 @@
# gig-platform-<instance>-nginx; so do main's permission gates. See
# docs/deploy/instances.md.
# main, at the apex. Its own site, not a case of the wildcard below: Caddy
# indexes host labels from the right, so {http.request.host.labels.4} is
# `pr-57` for pr-57.gig-platform.dev.kucial.com but empty for the apex, which
# would proxy to `gig-platform--nginx`.
gig-platform.dev.kucial.com {
# main. Its own site, not a case of the wildcard below: Caddy indexes host
# labels from the right, so {http.request.host.labels.4} is `pr-57` for
# pr-57.gig-platform.dev.kucial.com but empty for the apex, which would proxy
# to `gig-platform--nginx`.
#
# gig-api.kucial.com is main's canonical, public name (APP_URL), carried by the
# preview tunnel: Cloudflare's free Universal SSL covers kucial.com and
# *.kucial.com only, so a public name cannot sit under dev.kucial.com's extra
# level. The dev apex stays as a LAN alias.
gig-api.kucial.com, gig-platform.dev.kucial.com {
import cloudflare_tls
reverse_proxy gig-platform-main-nginx:80
}
-33
View File
@@ -1,33 +0,0 @@
# The old single gig-platform preview stack and gig-app web, on host ports.
#
# DELETE THIS FILE when gig-platform's main instance is migrated
# (gig-platform docs/deploy/instances.md). The instance compose publishes no
# 8088 or 3101, so after cutover these upstreams no longer exist; their
# replacements are gig-platform.caddy and gig-app.caddy.
#
# Kept separate so the current preview keeps working until then, and cutover
# is one `rm` plus a reload.
gig-api.kucial.com {
import cloudflare_tls
reverse_proxy host.docker.internal:8088
}
# Replaced by $APP_URL/objects/<bucket> on each instance.
gig-static.kucial.com {
import cloudflare_tls
reverse_proxy host.docker.internal:8088
}
# Proxies straight to Studio's break-glass port, bypassing db-studio-gate.
# Replaced by studio.gig-platform.dev.kucial.com, which goes through the gate.
gig-db.kucial.com {
import cloudflare_tls
reverse_proxy host.docker.internal:3100
}
# Replaced by gig-app.dev.kucial.com.
gig-web.kucial.com {
import cloudflare_tls
reverse_proxy host.docker.internal:3101
}