Operations and hosting
The reference deployment uses GitHub Actions, Docker, Caddy, external production and demo MySQL databases, and disposable per-PR MySQL databases.
Repository operators should follow the concise hosting runbook for DNS, runtime configuration, delivery, backups, and recovery.
GitHub environments match public hostnames: booking.title.dk, demo.booking.title.dk, and pr-<number>.booking.title.dk. A deployment status records a release attempt, not current route health; a failed blue/green release leaves the previous verified containers active.
Admin access decision
Admin authentication is an operational environment concern, not a feature-spec concern. The feature specs describe the product contract—email magic links, sessions, account selection, and authorization—while HOSTING.md is the authoritative runbook for deployment secrets, provider wiring, preview databases, and operator access. This guide summarizes that decision without duplicating the secret-bearing setup.
- Production at
/admin/requires a delivered email magic link. It hasDEMO_AUTH_ENABLED=false, so no email-free login button is expected. - Same-repository PR previews have isolated MySQL databases, seeded demo data,
DEMO_AUTH_ENABLED=true, and mocked email/payment boundaries. Their supported shortcut is Open demo studio. - Local development uses the same demo configuration when the command in the repository README is used.
/demo/is the attendee-facing demo and is separate from/admin/.
The reference VPS has two application containers: the production container serves https://booking.title.dk, while a separate booking-demo-<release> container serves the full demo environment at https://demo.booking.title.dk. The demo has its own database and seeded Harbour Yoga data. Production does not route or expose /demo; the attendee demo is available only at https://demo.booking.title.dk/demo/.
The demo hostname exposes the same route families as production: /admin, /api, /api/docs, /api/openapi.json, and /docs; its /demo/ path is the attendee-facing reference consumer. It uses mocked email and payment adapters, while production must use real providers.
The application selects the root index from the request hostname: the demo host shows a demo index linking to /demo/, while the production host shows the marketing placeholder. Production does not expose /demo; that route returns not found there.
The production application can be database-ready while email is still misconfigured. Confirm both public endpoints and the private environment file as described in HOSTING.md.
Release behavior
- CI must succeed before deployment.
- Pull-request CI runs from the
pull_requestevent; pushes run CI only formain. This keeps each PR revision on one verification path and avoids duplicate downstream feature-spec report uploads. - CI also supports manual dispatch for rerunning the current branch when a pull-request event was not enqueued by GitHub.
- In-progress PR verification is canceled when a newer revision is pushed. Feature-spec diff-report jobs are also serialized per PR, with obsolete jobs canceled before they can publish another report.
- The report publication transfer itself is owned by the reusable
feature-spec-mdworkflow. Repository-side workflow controls prevent duplicate and stale uploads; transfer-level parallelism or timeout changes belong in that upstream library. - Production is backed up before Drizzle migrations.
- The previous application remains active until the replacement passes database-backed readiness.
- PRs from the repository receive
pr-<number>environments and isolated schemas. - Closing a PR removes its route, container, user, credentials, and database.
Schema migrations must follow expand/contract rules. A container switch is non-disruptive, but destructive or locking SQL can still interrupt the old release.