fix(deploy): use single-level apess-api.redclaw.dev for the API host

api.apess.redclaw.dev is a multi-level subdomain that Cloudflare Universal
SSL and the *.redclaw.dev origin cert don't cover (edge TLS handshake fails).
Switch the API host to apess-api.redclaw.dev, which the existing edge + origin
certs cover with no paid Cloudflare features. Updates the client API base
default, the Traefik router host, and deploy docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-17 08:23:17 -07:00
co-authored by Claude Opus 4.8
parent 0eea6efdec
commit d58065d292
4 changed files with 13 additions and 12 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ Production web app for the **15th Asia-Pacific-Euro Summer School on Smart Struc
Guides 60+ PhD students across 15 teams from kit unboxing → ZeroClaw firmware deployment → Agent Design Document (ADD) submission in a single 5.5-hour session. Guides 60+ PhD students across 15 teams from kit unboxing → ZeroClaw firmware deployment → Agent Design Document (ADD) submission in a single 5.5-hour session.
- **Live URL** (planned): https://apess.redclaw.dev - **Live URL** (planned): https://apess.redclaw.dev
- **API** (planned): https://api.apess.redclaw.dev - **API** (planned): https://apess-api.redclaw.dev
- **Host node**: `zeroclaw-gw-03` (Architect) - **Host node**: `zeroclaw-gw-03` (Architect)
- **PRD**: `APESS-2026-Workshop-PRD-v1.0.docx` (separate, confidential) - **PRD**: `APESS-2026-Workshop-PRD-v1.0.docx` (separate, confidential)
+10 -9
View File
@@ -3,15 +3,16 @@
Drops `apess-web` (static SPA) and `apess-api` (collective backend) next to the existing ClawBooks stack on `zeroclaw-gw-03` (Architect). Reuses ClawBooks' Traefik v3.6 instance via the shared `clawbooks-net` Docker network. Drops `apess-web` (static SPA) and `apess-api` (collective backend) next to the existing ClawBooks stack on `zeroclaw-gw-03` (Architect). Reuses ClawBooks' Traefik v3.6 instance via the shared `clawbooks-net` Docker network.
- `apess.redclaw.dev``apess-web` (nginx static) - `apess.redclaw.dev``apess-web` (nginx static)
- `api.apess.redclaw.dev``apess-api` (Node/Express + SQLite + WebSocket) — powers `/admin` and `/judge` - `apess-api.redclaw.dev``apess-api` (Node/Express + SQLite + WebSocket) — powers `/admin` and `/judge`
## Prerequisites (one-time, on gw-03) ## Prerequisites (one-time, on gw-03)
1. **Cloudflare DNS** — add `A` records (proxied): 1. **Cloudflare DNS** — add `A` records (proxied):
- `apess.redclaw.dev``37.27.250.130` - `apess.redclaw.dev``37.27.250.130`
- `api.apess.redclaw.dev``37.27.250.130` - `apess-api.redclaw.dev``37.27.250.130`
2. **Cloudflare Origin Certificate**issue from CF dashboard: 2. **Cloudflare Origin Certificate**already in place from the web deploy:
- Hostnames: `*.redclaw.dev`, `redclaw.dev` (covers all subdomains`api.` included) - Hostnames: `*.redclaw.dev`, `redclaw.dev` covers single-level subdomains, so
`apess-api.redclaw.dev` is covered (a multi-level host like `api.apess.…` would NOT be).
- Validity: 15 years - Validity: 15 years
- Save: - Save:
- `/etc/ssl/redclaw/apess.pem` (root:root 644) - `/etc/ssl/redclaw/apess.pem` (root:root 644)
@@ -52,17 +53,17 @@ docker logs -f apess-api # verify "[apess-api] listening on :3000"
curl -fsS https://apess.redclaw.dev/healthz # → ok curl -fsS https://apess.redclaw.dev/healthz # → ok
# Backend REST # Backend REST
curl -fsS https://api.apess.redclaw.dev/healthz # → ok curl -fsS https://apess-api.redclaw.dev/healthz # → ok
curl -fsS -o /dev/null -w '%{http_code}\n' \ curl -fsS -o /dev/null -w '%{http_code}\n' \
https://api.apess.redclaw.dev/teams # → 401 (no code) https://apess-api.redclaw.dev/teams # → 401 (no code)
curl -fsS -X PUT https://api.apess.redclaw.dev/teams/smoke \ curl -fsS -X PUT https://apess-api.redclaw.dev/teams/smoke \
-H 'content-type: application/json' -d '{"name":"smoke","kit":"KIT-00"}' # → 204 -H 'content-type: application/json' -d '{"name":"smoke","kit":"KIT-00"}' # → 204
curl -fsS https://api.apess.redclaw.dev/teams \ curl -fsS https://apess-api.redclaw.dev/teams \
-H "X-Access-Code: $ADMIN_CODE" | head -c 200 # → JSON incl. the smoke team -H "X-Access-Code: $ADMIN_CODE" | head -c 200 # → JSON incl. the smoke team
# Backend WebSocket (run days before the event, not on July 27) # Backend WebSocket (run days before the event, not on July 27)
# from a browser console on https://apess.redclaw.dev: # from a browser console on https://apess.redclaw.dev:
# new WebSocket(`wss://api.apess.redclaw.dev/ws?code=${ADMIN_CODE}`).onmessage = e => console.log(e.data) # new WebSocket(`wss://apess-api.redclaw.dev/ws?code=${ADMIN_CODE}`).onmessage = e => console.log(e.data)
# → a {"type":"snapshot",...} frame on connect # → a {"type":"snapshot",...} frame on connect
``` ```
+1 -1
View File
@@ -6,7 +6,7 @@
http: http:
routers: routers:
apess-api: apess-api:
rule: "Host(`api.apess.redclaw.dev`)" rule: "Host(`apess-api.redclaw.dev`)"
entryPoints: entryPoints:
- websecure - websecure
service: apess-api service: apess-api
+1 -1
View File
@@ -9,7 +9,7 @@ import type {
} from '@/types' } from '@/types'
export const API_BASE = export const API_BASE =
(import.meta.env.VITE_API_BASE as string | undefined) ?? 'https://api.apess.redclaw.dev' (import.meta.env.VITE_API_BASE as string | undefined) ?? 'https://apess-api.redclaw.dev'
async function asJson<T>(res: Response, label: string): Promise<T> { async function asJson<T>(res: Response, label: string): Promise<T> {
if (!res.ok) throw new Error(`${label} ${res.status}`) if (!res.ok) throw new Error(`${label} ${res.status}`)