# Branch preview sites — `.contract.knownelement.com` Goal: every branch of this repo gets a browsable Grav rendering of the contract at `https://.contract.knownelement.com`, with `https://contract.knownelement.com` serving the release branch. Reviewers get a URL instead of a diff. ## Architecture One Cloudron **LAMP** app with a wildcard alias: - App location: `contract.knownelement.com` - Wildcard alias: `*.contract.knownelement.com` (Cloudron provisions the wildcard Let's Encrypt certificate via DNS-01) - Apache maps the `Host` header to a per-branch docroot under `/app/data/branches//` (mass virtual hosting via mod_rewrite) - The root docroot `/app/data/public/` serves the release site Each branch site is a **complete, standalone Grav install** whose `user/pages` and `user/themes` come from the repo branch via the Grav **Git Sync** plugin: - Per-site Git Sync config: same repository, per-site branch - Auto-pull on a cron schedule (every 5 minutes is plenty); gitea webhooks can replace cron later - Theme config (including e-sign endpoints) lives in the repo, so branch sites inherit the right e-sign wiring automatically ## Branch name → hostname Branch names are not DNS-safe in general, so the mapping is mechanical: lowercase, every run of characters outside `[a-z0-9-]` becomes `-`. Examples: `feature/grav-native-ci` → `feature-grav-native-ci`; `v0.1` → `v0-1`. The wildcard covers every mapping — no per-branch DNS or certificate work. ## Provisioning a new branch site (one-time per branch) 1. `composer create-project getgrav/grav /app/data/branches/` 2. Copy `user/pages` and `user/themes/knel-contract` from the branch checkout into the new install 3. Install + configure the Git Sync plugin (repository URL, the branch, cron pull) 4. Reload Apache — the rewrite rule picks the site up by Host header Deprovisioning is the reverse: delete the site directory when the branch is deleted. Provisioning/deprovisioning is agent work (docker exec into the LAMP app container is a permitted access path); the procedure is scriptable and should be automated in CI once proven by hand. ## Migration runbook (production change — GLPI CR required first) 1. File the GLPI CR + Uptime Kuma maintenance window (evening, low traffic) 2. Back up the current Grav app (app id 757c60ba) appdata off-box 3. Uninstall the Grav app; install the LAMP app at the same location and add the `*.contract.knownelement.com` wildcard alias 4. Restore the Grav content as the release site in `/app/data/public/` (full Grav install; Git Sync → release branch) 5. Lay down the Apache branch-rewrite config in `/app/data/apache-app.conf` 6. Smoke test: home/services/terms/sign/execution all 200 on the root host; e-sign flow still POSTs through apigw.knownelement.com/contract-sign (unchanged — endpoints are in the theme config); provision one test branch site and confirm `.contract.knownelement.com` serves that branch 7. Post verification evidence on the CR and the Redmine ticket Rollback: reinstall the Grav app at the same location from the backup taken in step 2.