docs(deployment): add runbooks for store subdomains, custom domains, and new platforms

- Update origin cert config: wildcards for omsflow.lu, rewardflow.lu, hostwizard.lu
- Add wildcard Caddy blocks to production Caddyfile example
- Replace "Future" section with actionable runbooks:
  - Add a Store Subdomain (self-service, no infra changes)
  - Add a Custom Store Domain (Cloudflare + Caddy + DB)
  - Add a New Platform Domain (full setup)
- Document wizard.lu exception (no wildcard due to git.wizard.lu DNS-only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 12:31:56 +01:00
parent 169a774b9c
commit 183f55c7b3

View File

@@ -758,7 +758,7 @@ Then restart Gitea:
cd ~/gitea && docker compose up -d gitea cd ~/gitea && docker compose up -d gitea
``` ```
### Future: Multi-Tenant Store Routing ### Multi-Tenant Store Routing
Stores on each platform use two routing modes: Stores on each platform use two routing modes:
@@ -769,49 +769,204 @@ Both modes are handled by the `StoreContextMiddleware` which reads the `Host` he
#### Wildcard Subdomains (for store subdomains) #### Wildcard Subdomains (for store subdomains)
When stores start using subdomains like `acme.omsflow.lu`, add wildcard blocks: Each non-main platform uses a wildcard Caddy block so any store subdomain (e.g. `acme.omsflow.lu`, `parcelproxy.hostwizard.lu`) is automatically routed without per-store Caddy changes.
The wildcard blocks use the same origin cert as the platform root domain. The origin cert must include `*.<platform_domain>` — see [Step 21.4](#214-generate-origin-certificates) for how to generate it.
```caddy ```caddy
*.omsflow.lu { *.omsflow.lu {
tls /etc/caddy/certs/omsflow.lu/cert.pem /etc/caddy/certs/omsflow.lu/key.pem
reverse_proxy localhost:8001 reverse_proxy localhost:8001
} }
*.rewardflow.lu { *.rewardflow.lu {
tls /etc/caddy/certs/rewardflow.lu/cert.pem /etc/caddy/certs/rewardflow.lu/key.pem
reverse_proxy localhost:8001 reverse_proxy localhost:8001
} }
*.wizard.lu { *.hostwizard.lu {
tls /etc/caddy/certs/hostwizard.lu/cert.pem /etc/caddy/certs/hostwizard.lu/key.pem
reverse_proxy localhost:8001 reverse_proxy localhost:8001
} }
``` ```
!!! warning "Wildcard SSL requires DNS challenge" !!! warning "No wildcard for wizard.lu"
Let's Encrypt cannot issue wildcard certificates via HTTP challenge. Wildcard certs require a **DNS challenge**, which means installing a Caddy DNS provider plugin (e.g. `caddy-dns/cloudflare`) and configuring API credentials for your DNS provider. See [Caddy DNS challenge docs](https://caddyserver.com/docs/automatic-https#dns-challenge). `wizard.lu` cannot use a wildcard block because `git.wizard.lu` is DNS-only (grey cloud in Cloudflare) and uses a Let's Encrypt cert. A wildcard origin cert would conflict. For wizard.lu subdomains, add each one explicitly (same pattern as `api.wizard.lu`, `flower.wizard.lu`).
#### Custom Store Domains (for premium stores) **Cloudflare DNS**: Add a wildcard DNS record for each platform:
When premium stores bring their own domains (e.g. `acme.lu`), use Caddy's **on-demand TLS**: - `*.omsflow.lu` → A `91.99.65.229` (proxied, orange cloud)
- `*.rewardflow.lu` → A `91.99.65.229` (proxied, orange cloud)
- `*.hostwizard.lu` → A `91.99.65.229` (proxied, orange cloud)
With this in place, adding a new store subdomain only requires a database entry (via admin UI) — no DNS or Caddy changes needed.
#### Runbook: Add a Store Subdomain
When a merchant creates a store with subdomain `acme` on the OMS platform:
1. **Database**: Create the store via admin UI — set `subdomain = "acme"` and link to the platform. The `StoreContextMiddleware` will resolve `acme.omsflow.lu` automatically.
2. **DNS**: Already covered by the wildcard `*.omsflow.lu` record in Cloudflare.
3. **Caddy**: Already covered by the `*.omsflow.lu` block.
4. **SSL**: Already covered by the wildcard origin cert.
5. **Verify**: `curl -I https://acme.omsflow.lu/`
No infrastructure changes needed — it's fully self-service.
#### Runbook: Add a Custom Store Domain
When a premium store brings their own domain (e.g. `wizamart.com`), the domain must be added to **your** Cloudflare account. This ensures you control SSL, WAF, and DNS — critical since you are responsible for the infrastructure.
**Step 1: Add domain to Cloudflare**
1. In [Cloudflare Dashboard](https://dash.cloudflare.com), click **Add a site** > enter `wizamart.com`
2. Select the Free plan
3. Cloudflare assigns NS records — give these to the store owner to update at their registrar
4. Wait for NS propagation (can take up to 24h, usually minutes)
**Step 2: Configure DNS in Cloudflare**
Add A records (proxied, orange cloud):
| Type | Name | Content | Proxy |
|------|------|---------|-------|
| A | `wizamart.com` | `91.99.65.229` | Proxied |
| A | `www` | `91.99.65.229` | Proxied |
**Step 3: Generate origin certificate**
1. In Cloudflare: **SSL/TLS** > **Origin Server** > **Create Certificate**
2. Hostnames: `wizamart.com, www.wizamart.com`
3. Validity: 15 years
4. Download cert and key (key shown only once!)
**Step 4: Install cert on server**
```bash
sudo mkdir -p /etc/caddy/certs/wizamart.com
sudo nano /etc/caddy/certs/wizamart.com/cert.pem # paste certificate
sudo nano /etc/caddy/certs/wizamart.com/key.pem # paste private key
sudo chown -R caddy:caddy /etc/caddy/certs/wizamart.com
sudo chmod 600 /etc/caddy/certs/wizamart.com/key.pem
```
**Step 5: Add to Caddyfile**
```bash
sudo nano /etc/caddy/Caddyfile
```
Add:
```caddy ```caddy
https:// { # ─── Custom store domain: wizamart.com ────────────────────────
tls { wizamart.com {
on_demand tls /etc/caddy/certs/wizamart.com/cert.pem /etc/caddy/certs/wizamart.com/key.pem
} reverse_proxy localhost:8001
}
www.wizamart.com {
tls /etc/caddy/certs/wizamart.com/cert.pem /etc/caddy/certs/wizamart.com/key.pem
redir https://wizamart.com{uri} permanent
}
```
**Step 6: Reload Caddy**
```bash
sudo systemctl reload caddy
sudo systemctl status caddy
```
**Step 7: Configure Cloudflare settings**
In Cloudflare dashboard for `wizamart.com`:
| Setting | Location | Value |
|---|---|---|
| SSL mode | SSL/TLS > Overview | Full (Strict) |
| Always Use HTTPS | SSL/TLS > Edge Certificates | On |
| Bot Fight Mode | Security > Settings | On |
**Step 8: Register domain in database**
Via admin UI: create a `StoreDomain` record linking `wizamart.com` to the store and platform.
**Step 9: Verify**
```bash
curl -I https://wizamart.com/
```
The `StoreContextMiddleware` will detect `wizamart.com` as a custom domain, look it up in the `store_domains` table, and route to the correct store.
#### Runbook: Add a New Platform Domain
When adding an entirely new platform (e.g. `newplatform.lu`):
**Step 1: Cloudflare**
1. Add `newplatform.lu` as a site in Cloudflare
2. Configure NS at registrar
3. Add DNS records (all proxied):
- `newplatform.lu` → A `91.99.65.229`
- `www.newplatform.lu` → A `91.99.65.229`
- `*.newplatform.lu` → A `91.99.65.229` (for store subdomains)
**Step 2: Generate origin certificate**
In Cloudflare: **SSL/TLS** > **Origin Server** > **Create Certificate**
Hostnames: `newplatform.lu, www.newplatform.lu, *.newplatform.lu`
**Step 3: Install cert and update Caddyfile**
```bash
sudo mkdir -p /etc/caddy/certs/newplatform.lu
sudo nano /etc/caddy/certs/newplatform.lu/cert.pem
sudo nano /etc/caddy/certs/newplatform.lu/key.pem
sudo chown -R caddy:caddy /etc/caddy/certs/newplatform.lu
sudo chmod 600 /etc/caddy/certs/newplatform.lu/key.pem
```
Add to Caddyfile:
```caddy
# ─── Platform: NewPlatform (newplatform.lu) ───────────────────
newplatform.lu {
tls /etc/caddy/certs/newplatform.lu/cert.pem /etc/caddy/certs/newplatform.lu/key.pem
reverse_proxy localhost:8001
}
www.newplatform.lu {
tls /etc/caddy/certs/newplatform.lu/cert.pem /etc/caddy/certs/newplatform.lu/key.pem
redir https://newplatform.lu{uri} permanent
}
*.newplatform.lu {
tls /etc/caddy/certs/newplatform.lu/cert.pem /etc/caddy/certs/newplatform.lu/key.pem
reverse_proxy localhost:8001 reverse_proxy localhost:8001
} }
``` ```
On-demand TLS auto-provisions SSL certificates when a new domain connects. Add an `ask` endpoint to validate that the domain is registered in the `store_domains` table, preventing abuse: ```bash
sudo systemctl reload caddy
```caddy
tls {
on_demand
ask http://localhost:8001/api/v1/internal/verify-domain
}
``` ```
!!! note "Not needed yet" **Step 4: Cloudflare settings**
Wildcard subdomains and custom domains are future work. The current Caddyfile handles all platform root domains and service subdomains.
Same as other platforms — see [Step 21.6](#216-cloudflare-settings-per-domain).
**Step 5: Database**
Create the platform record via `init_production.py` or admin UI with `domain = "newplatform.lu"`.
**Step 6: Verify**
```bash
curl -I https://newplatform.lu/
curl -I https://teststore.newplatform.lu/
```
## Step 15: Gitea Actions Runner ## Step 15: Gitea Actions Runner
@@ -2007,15 +2162,15 @@ Disable DNSSEC at the registrar before switching NS — re-enable later via Clou
Cloudflare Origin Certificates (free, 15-year validity) avoid ACME challenge issues when traffic is proxied: Cloudflare Origin Certificates (free, 15-year validity) avoid ACME challenge issues when traffic is proxied:
1. In Cloudflare: **SSL/TLS** > **Origin Server** > **Create Certificate** 1. In Cloudflare: **SSL/TLS** > **Origin Server** > **Create Certificate**
2. Generate for each domain with **specific subdomains** (not wildcards): 2. Generate for each domain:
- `wizard.lu`: `wizard.lu, api.wizard.lu, www.wizard.lu, flower.wizard.lu, grafana.wizard.lu` - `wizard.lu`: `wizard.lu, api.wizard.lu, www.wizard.lu, flower.wizard.lu, grafana.wizard.lu` (**specific subdomains, no wildcard**)
- `omsflow.lu`: `omsflow.lu, www.omsflow.lu` - `omsflow.lu`: `omsflow.lu, www.omsflow.lu, *.omsflow.lu` (wildcard for store subdomains)
- `rewardflow.lu`: `rewardflow.lu, www.rewardflow.lu` - `rewardflow.lu`: `rewardflow.lu, www.rewardflow.lu, *.rewardflow.lu` (wildcard for store subdomains)
- `hostwizard.lu`: `hostwizard.lu, www.hostwizard.lu` - `hostwizard.lu`: `hostwizard.lu, www.hostwizard.lu, *.hostwizard.lu` (wildcard for store subdomains)
3. Download the certificate and private key (private key is shown only once) 3. Download the certificate and private key (private key is shown only once)
!!! warning "Do NOT use wildcard origin certs for wizard.lu" !!! warning "Do NOT use wildcard origin certs for wizard.lu"
A `*.wizard.lu` wildcard cert will match `git.wizard.lu`, which needs a Let's Encrypt cert (DNS-only, not proxied through Cloudflare). Use specific subdomains instead. A `*.wizard.lu` wildcard cert will match `git.wizard.lu`, which is DNS-only (grey cloud) and uses a Let's Encrypt cert. A wildcard origin cert would conflict. Use specific subdomains instead. For new wizard.lu subdomains, add them explicitly to this cert and to the Caddyfile.
Install on the server: Install on the server:
@@ -2090,6 +2245,22 @@ www.hostwizard.lu {
redir https://hostwizard.lu{uri} permanent redir https://hostwizard.lu{uri} permanent
} }
# ─── Store subdomains (wildcard — all except wizard.lu) ──────
*.omsflow.lu {
tls /etc/caddy/certs/omsflow.lu/cert.pem /etc/caddy/certs/omsflow.lu/key.pem
reverse_proxy localhost:8001
}
*.rewardflow.lu {
tls /etc/caddy/certs/rewardflow.lu/cert.pem /etc/caddy/certs/rewardflow.lu/key.pem
reverse_proxy localhost:8001
}
*.hostwizard.lu {
tls /etc/caddy/certs/hostwizard.lu/cert.pem /etc/caddy/certs/hostwizard.lu/key.pem
reverse_proxy localhost:8001
}
# ─── Services (wizard.lu origin cert) ─────────────────────── # ─── Services (wizard.lu origin cert) ───────────────────────
api.wizard.lu { api.wizard.lu {
tls /etc/caddy/certs/wizard.lu/cert.pem /etc/caddy/certs/wizard.lu/key.pem tls /etc/caddy/certs/wizard.lu/cert.pem /etc/caddy/certs/wizard.lu/key.pem