2.5 KiB
Hosting the static build
Tera's map view is a static bundle that needs a file server and nothing else.
No API, no keys, no account: the sun and moon are computed locally by
src/engine/solar.ts, the traffic is simulated, the minimap is drawn from the
city pack, and the office is a data file.
It does make two requests at boot, and both are meant to fail on a plain
static host: GET /api/v1/health and GET /api/v1/session, which is how
src/access.ts works out whether this deployment has accounts at all. Nothing
answering means nothing to sign in to, so the visitor gets the full public
experience and no sign-in link — see that file's header for why an unreachable
API and an API that answered 5xx are deliberately not the same case. Your
server log will show two 404s per load; that is the zero-config path working,
not a misconfiguration.
npm ci
npm run build # -> dist/
Serve dist/ from anything. A Content-Security-Policy of default-src 'self'
is sufficient; the build references no external fonts, CDNs or images.
Caddy
tera.example.com {
root * /var/www/tera
encode zstd gzip
try_files {path} {path}/index.html /index.html
file_server
header {
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; object-src 'none'"
# Screen sharing remains a per-action browser prompt. Webcam capture stays
# disabled until that separate feature is deliberately deployed.
Permissions-Policy "display-capture=(self), camera=(), microphone=(), geolocation=(), payment=(), usb=()"
}
}
try_files … /index.html matters if you add client-side routes later: without
it a route that exists only in JavaScript 404s for anyone who types it or
refreshes on it.
img-src needs data: and blob: because every texture in the asset library
is drawn on a canvas at runtime rather than shipped as a file — see
ARCHITECTURE.md §3.1 for why that is a licensing decision and not a technical
one.
Serving the API too
Only needed for live weather, real ADS-B, or markers from an external source.
The map runs fully without it. See deploy/tera-api.service,
deploy/Caddyfile.snippet and deploy/docker-compose.yml.
Base path
Deployed at a subdomain root, no configuration is needed. Under a subpath, set
TERA_BASE at build time (TERA_BASE=/tera/ npm run build) — the trailing
slash matters, since every asset URL resolves against it.