Deploy the dashboard¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.
The narrator dashboard is a static bundle published as GitLab Pages from nerthus.core.dashboard. Deploying it costs one pipeline per push to the default branch — four short Node jobs, a build, and a move — and nothing recurring: Pages serves the bytes, so there is no Worker invocation and no CI minute burned between deploys. It stands nothing up on the fleet either: the app is a client of the API the fleet already publishes — reads for nearly every screen, and writes through the same published routes for the editing plane (lore commits), the workflows and the moderation surface.
Two origins are served from the one deployment:
| Origin | Default API base | Fleet |
|---|---|---|
astral.nerthus.pl |
https://evocation.nerthus.pl/v1/api |
production |
dev-astral.nerthus.pl |
https://dev-evocation.nerthus.pl/v1/api |
dev |
Both are Pages custom domains on this project plus a DNS record, the same arrangement cognition.nerthus.pl uses for this site. Neither is a fleet hostname. There is no tunnel behind them, no host, and nothing for fleet:provision to reconcile. Both names are reserved in schema/reserved-names.txt in the fleet contract, so no town slug can ever claim them.
Prerequisites¶
- Maintainer on the dashboard project, to set CI/CD variables and Pages domains.
- Access to the DNS zone for
nerthus.pl. - The fleet's API already published at
evocation.nerthus.pl, per Publish the API. The dashboard is a client; it cannot be the first thing you stand up. - A checkout of the fleet contract, to add the origins to the CORS Worker.
1. Grant the two origins at the edge¶
The daemon sends no CORS headers and has no OPTIONS route, so a browser on astral.nerthus.pl reaches the API only if the edge grants it. Add both origins to ALLOWED_ORIGINS in worker/cors-worker.js in the fleet contract, then republish:
The allowlist is a closed set written out rather than pattern-matched, and CORS is granted per origin, so each name needs its own line. Skipping this step produces a dashboard that loads, shows sign-in, and fails every read with a CORS error the browser will not explain.
2. Set the build variable¶
The bundle bakes its fallback endpoint at build time. Set NERTHUS_API_BASE as a CI/CD variable on the project, or leave the value the build job already carries:
This fallback is used only where the hostname carries no default of its own — a local vite preview, a review app, or the raw Pages URL reached directly. The two published origins pick their fleet from window.location.hostname, which is why one bundle serves both. A user can repoint the app at runtime from Ustawienia regardless.
The job also exports NERTHUS_DASHBOARD_VERSION from CI_COMMIT_TAG or the short SHA. It is shown in the shell footer so a bug report can name the build it came from.
3. Add the Pages domains and the DNS records¶
In the project's Deploy → Pages settings add both astral.nerthus.pl and dev-astral.nerthus.pl, then create the DNS records GitLab prints for each, including the _gitlab-pages-verification-code TXT record. Let GitLab issue the certificates.
4. Push to the default branch¶
The pages job runs only on the default branch. It takes the build job's dist/ artifact and publishes it:
That single move is why vite.config.ts sets publicDir: false. A directory named public in the repository would turn the move into a nesting — public/dist/index.html, a site whose root is a 404.
Verify it worked¶
curl -sI https://astral.nerthus.pl/ | head -3
# expect 200 and content-type: text/html
curl -sD - -o /dev/null -X OPTIONS https://evocation.nerthus.pl/v1/api/entities \
-H 'Origin: https://astral.nerthus.pl' \
-H 'Access-Control-Request-Method: GET' \
-H 'Access-Control-Request-Headers: authorization'
# expect 204 with access-control-allow-origin: https://astral.nerthus.pl
Then open the site, paste an API key into Ustawienia, and load the Sesje tab. A populated listing means the whole path works: Pages, DNS, the certificate, the edge grant, and the daemon behind it.
When it did not work¶
| Symptom | Cause |
|---|---|
The site root is a 404 while /dist/ works |
a public/ directory reached the repository, so mv dist public nested the output |
| The page loads, every read fails, the console names CORS | the origin is missing from ALLOWED_ORIGINS, or the Worker was not republished after the edit |
Reads work on dev-astral and fail on astral |
only one origin was added to the allowlist |
| The app talks to the wrong fleet | the hostname is not in HOST_DEFAULTS in src/config.ts, so it fell back to the build variable |
| A response header you set is served as text | this Pages instance does not honour a _headers file. Response headers for these origins have to come from the edge in front of them |
| Sign-in through Margonem never completes | expected. Margonem's validate endpoint cannot be called from a Nerthus origin — see The dashboard for why, and use an API key or the in-game handoff |
See also¶
- The dashboard — the component, its routes and its API dependencies
- Publish the API — the CORS Worker and the edge this depends on
- Use the web dashboard — what the people downstream do with it