92 lines
4.1 KiB
Markdown
92 lines
4.1 KiB
Markdown
# 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.
|
|
|
|
```bash
|
|
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 and webcam faces remain contextual, per-action browser
|
|
# prompts. Same-origin permission enables those prompts; it does not grant
|
|
# capture. Microphone capture is deliberately unavailable.
|
|
Permissions-Policy "display-capture=(self), camera=(self), 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.
|
|
|
|
### Capture-policy acceptance check
|
|
|
|
Webcam faces require a secure context and `camera=(self)` on both the Tera and
|
|
Office hostnames. That policy only permits the application to ask: capture must
|
|
still begin inside the explicit face or screen action, display a persistent
|
|
active indicator, and stop on the in-product control or `pagehide`. Keep
|
|
`microphone=()` because neither webcam faces nor office screens request audio.
|
|
|
|
After changing the live Caddy site, reload Caddy through the normal reviewed
|
|
deployment procedure and verify both hostnames before enabling the UI:
|
|
|
|
```bash
|
|
curl -fsSI https://tera.example.com/ | grep -i '^permissions-policy:'
|
|
curl -fsSI https://office.example.com/ | grep -i '^permissions-policy:'
|
|
```
|
|
|
|
Both responses must contain `display-capture=(self)`, `camera=(self)`, and
|
|
`microphone=()`. In browser developer tools,
|
|
`document.permissionsPolicy?.allowsFeature("camera")` should be true on the
|
|
top-level same-origin page; denying the browser prompt must leave the generated
|
|
face and every office screen placeholder intact. The CSP needs no camera/media
|
|
host exception: webcam and display tracks are caller-owned `MediaStream`s, not
|
|
network media URLs, and signaling remains under same-origin `connect-src`.
|
|
|
|
The production policy may contain deployment-specific sources, but every one is
|
|
an operator-owned exception to this repository minimum. Audit and remove stale
|
|
font/CDN/identity origins; do not cargo-cult a broader live header back into this
|
|
template. A same-origin Tera build needs no Google Fonts or jsDelivr source.
|
|
|
|
## 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.
|