doxa-maps/deployment/cloud
Doxa Maps 14a25b6b59 README: Security controls (bundle integrity with the real manifest fields; the v1 backend posture), and PocketBase hooks switched off
Bundle integrity: all 25 bundles carry bundles[].integrity (sha384) and every profiles[].embed.script
already includes integrity + crossorigin=anonymous (checked on main); the section shows the snippet,
what the browser does with it, the CORS header it needs, and check:sri. The container's PocketBase:
locked-by-default collections, the prod dashboard blocked and the dev one admin-only, and hooks OFF,
now true in code: both instances get --hooksDir pointing at a path that never exists, so a pb_hooks/
folder added to the repo can't run. Per-plugin locks and isolated edge functions are stated as not
built.
2026-09-27 21:43:05 +00:00
..
admin.mjs Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
apply-exclusions.sh Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
build-cdn.sh Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
Dockerfile Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
entry.sh README: Security controls (bundle integrity with the real manifest fields; the v1 backend posture), and PocketBase hooks switched off 2026-09-27 21:43:05 +00:00
public-exclusions.txt Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
README.md Cloud container v1: PocketBase dev + prod, a public CDN of the last good build, and an admin-only build trigger 2026-09-27 21:32:46 +00:00
vite.cloud.config.mjs Cloud dev server recipe in the repo, with an optional OIDC sign-in 2026-09-27 07:50:45 +00:00

Run dx-maps in the cloud: one shared dev server for a team

One container runs the Plugin Studio with hot reload for everyone. It clones the repo, pulls the branch every few seconds, and Vite hot-reloads whatever changed, so every change an agent lands on main shows up in everyone's browser within seconds. No rebuild; bun run build is only for publishing.

With no OIDC settings it runs open, like bun run dev on a laptop. With OIDC settings a sign-in page ("Sign in with your provider") sits in front of everything: the Studio, every map, and the hot-reload websocket. Only people in the allowed groups get in.

Deploy

  1. Build the image from Dockerfile. It's a small bootstrap (Bun, git, and the oauth2-proxy binary); at start it clones the repo and runs entry.sh from it, so the recipe updates with the repo. Paste it into any container host (Coolify's "Dockerfile" build pack, Railway, docker build).
  2. Set the environment below and expose the container's PORT over HTTPS.
  3. Give it about 2 GB of memory (Vite plus the dependency install).
Variable Needed Meaning
REPO_URL yes the dx-maps git URL to clone
BRANCH no (main) the branch to follow
PULL_INTERVAL no (15) seconds between pulls
GIT_TOKEN private repos a read token for the clone and pulls
PORT no (5173) the port the container serves on
HMR_CLIENT_PORT, HMR_PROTOCOL behind HTTPS 443 and wss, so browsers reach the hot-reload websocket

Sign-in (optional)

Set all of these to turn the gate on. Register a confidential client at your OIDC provider with the redirect URL https://<your-host>/oauth2/callback; PKCE (S256) is used as well.

Variable Meaning
OIDC_ISSUER the provider's issuer URL (its /.well-known/openid-configuration must resolve)
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET the registered client
OIDC_REDIRECT_URL https://<your-host>/oauth2/callback
OIDC_ALLOWED_GROUPS comma-separated groups allowed in; required (the server refuses to start without it, because many providers allow open sign-up)
OIDC_DISPLAY_NAME the name on the button, e.g. "Sign in with Acme"
OIDC_GROUPS_CLAIM the token claim holding groups (default groups)
OIDC_COOKIE_SECRET 32 characters; keeps people signed in across restarts (random if unset)

Secrets reach oauth2-proxy through its environment, never its command line.

Backend and production (v1, experimental)

With the sign-in on, the container also runs PocketBase and can publish a production build:

Route Who What
/ signed-in the dev server (Plugin Studio, hot reload)
/pb/ signed-in (its /_/ dashboard: OIDC_ADMIN_GROUPS only) PocketBase dev
/api-prod/api/ public PocketBase prod's API; its dashboard is never exposed
/cdn/dx-maps/ public the last published build: manifest.json at a stable URL, CORS open for embeds
POST /admin/build · GET /admin/status OIDC_ADMIN_GROUPS only build and publish the pulled commit

A build runs one at a time (a second gets 409), on a snapshot of the pulled commit, with Node's heap capped. It removes everything in public-exclusions.txt before building, checks the output again afterwards, and refuses to publish if anything excluded is still there. Only then does it swap the new build in.

Variable Meaning
OIDC_ADMIN_GROUPS comma-separated groups allowed to build and to open PocketBase's dashboard
PB_SUPERUSER_EMAIL, PB_SUPERUSER_PASSWORD PocketBase's superuser, set on start in both instances
PUBLIC_API_BASE the public API URL written into the build's runtime-config.js (e.g. https://<host>/api-prod)
BUILD_MAX_OLD_SPACE_MB Node heap cap for a build (default 1536)

Mount a persistent volume at /data (PocketBase data and published builds). Memory measured on the running container: about 0.45 GB idle, up to about 1.5 GB more during a build, so give it 2.5 GB.