# Edge Routing Fallback: Per-Project Subdomains > **Status:** Contingency plan. Only implement if subpath routing (#704 / #708) > proves unworkable. ## Context The primary approach routes services under subpaths of `.disinto.ai`: | Service | Primary (subpath) | |------------|--------------------------------------------| | Forgejo | `.disinto.ai/forge/` | | Woodpecker | `.disinto.ai/ci/` | | Chat | `.disinto.ai/chat/` | | Staging | `.disinto.ai/staging/` | The fallback uses per-service subdomains instead: | Service | Fallback (subdomain) | |------------|--------------------------------------------| | Forgejo | `forge..disinto.ai/` | | Woodpecker | `ci..disinto.ai/` | | Chat | `chat..disinto.ai/` | | Staging | `.disinto.ai/` (root) | The wildcard cert from #621 already covers `*..disinto.ai` — no new DNS records or certs are needed for sub-subdomains because `*.disinto.ai` matches one level deep. For sub-subdomains like `forge..disinto.ai` we would need to add a second wildcard (`*.*.disinto.ai`) or explicit DNS records per project. Both are straightforward with the existing Gandi DNS-01 setup. ## Pivot Decision Criteria **Pivot if:** - Forgejo `ROOT_URL` under a subpath (`/forge/`) causes redirect loops that cannot be fixed with `X-Forwarded-Prefix` or Caddy `uri strip_prefix`. - Woodpecker's `WOODPECKER_HOST` does not honour subpath prefixes, causing OAuth callback mismatches that persist after adjusting redirect URIs. - Forward-auth on `/chat/*` conflicts with Forgejo's own OAuth flow when both share the same origin (cookie collision, CSRF token mismatch). **Do NOT pivot if:** - Forgejo login redirects to `/` instead of `/forge/` — fixable with Caddy `handle_path` + `uri prefix` rewrite. - Woodpecker UI assets 404 under `/ci/` — fixable with asset prefix config (`WOODPECKER_ROOT_PATH`). - A single OAuth app needs a second redirect URI — Forgejo supports multiple `redirect_uris` in the same app. ## Fallback Topology ### Caddyfile Replace the single `:80` block with four host blocks: ```caddy # Main project domain — staging / landing .disinto.ai { reverse_proxy staging:80 } # Forgejo — root path, no subpath rewrite needed forge..disinto.ai { reverse_proxy forgejo:3000 } # Woodpecker CI — root path ci..disinto.ai { reverse_proxy woodpecker:8000 } # Chat — with forward_auth (same as #709, but on its own host) chat..disinto.ai { handle /login { reverse_proxy chat:8080 } handle /oauth/callback { reverse_proxy chat:8080 } handle /* { forward_auth chat:8080 { uri /auth/verify copy_headers X-Forwarded-User header_up X-Forward-Auth-Secret {$FORWARD_AUTH_SECRET} } reverse_proxy chat:8080 } } ``` **Current file:** `docker/Caddyfile` (generated by `lib/generators.sh:_generate_caddyfile_impl`, line ~596). ### Service Configuration Changes | Variable / Setting | Current (subpath) | Fallback (subdomain) | File | |----------------------------|------------------------------------------------|-------------------------------------------------|-----------------------------| | Forgejo `ROOT_URL` | `https://.disinto.ai/forge/` | `https://forge..disinto.ai/` | forgejo `app.ini` | | `WOODPECKER_HOST` | `http://localhost:8000` (subpath via proxy) | `https://ci..disinto.ai` | `lib/ci-setup.sh` line ~164 | | Woodpecker OAuth redirect | `https://.disinto.ai/ci/authorize` | `https://ci..disinto.ai/authorize` | `lib/ci-setup.sh` line ~153 | | Chat OAuth redirect | `https://.disinto.ai/chat/oauth/callback` | `https://chat..disinto.ai/oauth/callback` | `lib/ci-setup.sh` line ~188 | | `EDGE_TUNNEL_FQDN` | `.disinto.ai` | unchanged (main domain) | `lib/generators.sh` line ~432 | ### New Environment Variables (pivot only) These would be added to `lib/generators.sh` `_generate_compose_impl()` in the edge service environment block (currently line ~415): | Variable | Value | |------------------------------|----------------------------------------| | `EDGE_TUNNEL_FQDN_FORGE` | `forge..disinto.ai` | | `EDGE_TUNNEL_FQDN_CI` | `ci..disinto.ai` | | `EDGE_TUNNEL_FQDN_CHAT` | `chat..disinto.ai` | ### DNS No new records needed if the registrar supports `*.*.disinto.ai` wildcards. Otherwise, add explicit A/CNAME records per project: ``` forge..disinto.ai → edge server IP ci..disinto.ai → edge server IP chat..disinto.ai → edge server IP ``` The edge server already handles TLS via Caddy's automatic HTTPS with the existing ACME / DNS-01 challenge. ### Edge Control (`tools/edge-control/register.sh`) Currently `do_register()` creates a single route for `.disinto.ai`. The fallback would need to register four routes (or accept a `--subdomain` parameter). See the TODO in `register.sh`. ## Files to Change on Pivot | File | What changes | |-----------------------------------|-----------------------------------------------------------------| | `docker/Caddyfile` | Replace single host block → four host blocks (see above) | | `lib/generators.sh` | Add `EDGE_TUNNEL_FQDN_{FORGE,CI,CHAT}` env vars to compose | | `lib/ci-setup.sh` ~line 153 | Woodpecker OAuth redirect URI → `ci.` subdomain | | `lib/ci-setup.sh` ~line 188 | Chat OAuth redirect URI → `chat.` subdomain | | `tools/edge-control/register.sh` | Register four routes per project instead of one | | `tools/edge-control/lib/caddy.sh`| `add_route()` gains subdomain support | | forgejo `app.ini` | `ROOT_URL` → `https://forge..disinto.ai/` | Estimated effort for a full pivot: **under one day** given this plan.