Skip to content

Stand Up the Web Client

What this is: deploying the browser client — the alternative to the Android app for getting onto the WayPoint network. See System overview. When you'd do it: standing up a new deployment, or adding a browser entry-point to an existing one. How long it takes: an hour for the cloud path if your Directory and a server are already live; a few minutes for the local/Docker path.

The web client is an AdonisJS application that serves a real-time tactical map and handles user login. It needs two things to do its job: a Directory (to authenticate users and issue identity tokens) and a server (a Zenoh relay, reached over WSS, to actually carry traffic). Stand those up first — this runbook is Step 4 in the deployment order; follow Stand up a deployment for the right sequence.

Who can do this: a deployment engineer with access to the GCP project (for the cloud path) or a Docker host. The web client has no in-Directory registration step — you do not need an Admin for the provisioning itself, but users still need to be onboarded to the Directory before they can log in.

The shape of it

flowchart LR
    A["Engineer provisions<br/>the web VM<br/>(or Docker host)"] --> B["Point it at the<br/>Directory (OIDC)<br/>and a server (WSS)"]
    B --> C["Users browse<br/>to the site"]
    C --> D["Directory handles<br/>passkey login;<br/>issues session"]
    D --> E["User sees the<br/>live tactical map"]

Before you start

  • A running Directory — the web client uses it for OIDC login. The demo Directory is at https://auth.bedrockdefence.com.
  • At least one running server (relay) — users won't see map data without one. Note its WSS endpoint (e.g. wss://demo.bedrockdefence.com:7447).
  • For the cloud path: tofu (OpenTofu) and gcloud installed and authenticated.
  • For the Docker path: Docker installed and a PostgreSQL instance reachable.

Step 1 — Provision the web client

There are two paths. Use the cloud path for real deployments.

Production web environments are a COS VM running Caddy + AdonisJS + Cloud SQL Auth Proxy, backed by a Cloud SQL PG17 instance. This lives in the infrastructure repo. For a new environment, copy terraform/environments/web-demo/ to a new directory and adjust variables.tf. For the existing demo environment, apply from the existing stack:

cd terraform/environments/web-demo
tofu init && tofu apply

Before the first tofu apply, the web-demo stack requires Telesto AIS feed credentials in Secret Manager. Put TELESTO_HOST, TELESTO_PORT, TELESTO_CLIENT_CERT_B64, TELESTO_CLIENT_KEY_B64, and TELESTO_CA_CERT_B64 into a local .env file and run:

./scripts/upload-telesto-secrets.sh path/to/.env waypoint-servers

tofu apply creates the VM, a static IP, and a Cloud SQL PG17 instance. Web secrets (app-key, db-password, cloudsql-postgres) are auto-generated by Terraform and stored in Secret Manager — you don't mint them manually.

After the first apply, the web app also needs a service identity token so it can poll the Directory's /api/revoked-principals endpoint. This is a Directory-signed IdentityToken, issued the same way as any service device — there is no node ace command for it; you mint it from the Directory admin UI:

  1. Sign in to the Directory as an Admin, click DevicesCreate Device.
  2. Give it a callsign (e.g. web-demo), set Platform Type to gateway (a service platform, so it receives a Directory-signed identity), and add at least one coverage cell.
  3. On the Device Created page, download the Identity Token as a .bin file.

Then upload that file as the web service token:

./scripts/upload-web-service-token.sh ~/Downloads/web-demo-service-token.bin demo

The VM fetches the token from Secret Manager on every boot. Without it, the revocation cache fails to prime and logins fail closed.

DNS must resolve before Caddy can provision its TLS certificate:

app.bedrockdefence.com  →  static_ip from web-demo stack (tofu output)

Option B — Docker

For a local or lab environment, the web repo contains a docker-compose.yml that starts a PostgreSQL instance:

docker compose up -d

Then copy and configure the environment file:

cp .env.example .env
node ace generate:key   # paste the output into APP_KEY in .env

To run the app in a single container instead:

docker build -t waypoint-web .

docker run -d \
  --name waypoint-web \
  -p 3333:3333 \
  -e NODE_ENV=production \
  -e APP_KEY=<your-app-key> \
  -e DB_HOST=<db-host> \
  -e DB_PASSWORD=<db-password> \
  waypoint-web

For development, after docker compose up -d to start the database:

npm install
npm run dev

The app starts at http://localhost:3333. Migrations run automatically on dev startup.

Step 2 — Point the web client at your Directory and server

Directory (for login)

The web client authenticates users via OIDC against your Directory. Set the Terraform variable (cloud path) or environment variable (Docker path):

Config key What it does Example
directory_url (Terraform) / WAYPOINT_OIDC_ISSUER_URL (env) Directory OIDC issuer URL https://auth.bedrockdefence.com
WAYPOINT_OIDC_CLIENT_ID OIDC client ID registered in the Directory
WAYPOINT_OIDC_CLIENT_SECRET OIDC client secret

On the cloud path, the OIDC client secret is pulled from Secret Manager automatically (bedrock-<env>-webauth-client-secret). No manual realm/client setup is required — the auth stack provisions it and the web module consumes it.

To disable password login and rely on passkeys/OIDC only:

Variable Default Effect
WAYPOINT_DISABLE_PASSWORD_AUTH false Set true to hide the password login form
WAYPOINT_ENABLE_WEBAUTHN false Set true to enable passkey (FIDO2/WebAuthn) login

Server / relay (for map traffic)

The web client opens a Zenoh session over WSS to reach the relay network. Set:

Config key What it does Example
zenoh_router_endpoints (Terraform) Comma-separated Zenoh WSS endpoints the browser connects to wss://demo.bedrockdefence.com:7447

On the cloud path this is a Terraform variable in variables.tf (default: empty, which means the browser fails closed with no map data). Set it to the WSS address of your server before applying.

On the Docker path, set the equivalent environment variable as exposed by the running AdonisJS app (surface it via .env — check web/start/env.ts for the exact name the app validates at startup).

Step 3 — Users sign in

Once the web client is up and pointed at the Directory, users browse to the site (e.g. https://app.bedrockdefence.com). The login flow is handled by the Directory: the web client redirects to the Directory's OIDC endpoint, the user authenticates with their passkey, and the Directory issues a session back to the web client.

Users must already be onboarded in the Directory before they can log in. If a user isn't provisioned yet, see Onboard an operator.

How to know it worked

  • The site loads at the configured hostname without a certificate warning.
  • A user can click Log in, authenticate with their passkey on the Directory, and land on the tactical map.
  • The map shows live unit positions for the area covered by the connected server.
  • Other users connected to the same server appear on the map.

If something goes wrong

  • Can't log in at all — OIDC error or redirect loop. The WAYPOINT_OIDC_ISSUER_URL / directory_url is wrong, or the OIDC client secret hasn't been uploaded. Check that the Directory is reachable and that the bedrock-<env>-webauth-client-secret Secret Manager value exists. Also check that the web service identity token has been uploaded — without it, logins fail closed (the revocation cache can't prime).

  • Can't log in — "user not found" or access denied. The user hasn't been onboarded to the Directory yet. See Onboard an operator.

  • Site loads but the map is empty / no other users appear. The zenoh_router_endpoints variable is empty or pointing at the wrong address, or no server is running that covers the users' area. Check the server's WSS endpoint and make sure a server is live and covers the relevant geohash cells. See Add a server.

  • Site won't load — certificate error. DNS hasn't propagated yet, or Caddy couldn't provision a Let's Encrypt certificate. DNS must resolve to the static IP before Caddy can issue the cert. Check with dig app.bedrockdefence.com and compare against tofu output static_ip in the web-demo stack.

  • Database not reachable. On the cloud path, the Cloud SQL Auth Proxy sidecar handles the connection — check that the VM's service account has the roles/cloudsql.client role and that the Cloud SQL instance is running. Tail the serial console:

    gcloud compute instances get-serial-port-output bedrock-web-demo \
      --zone=europe-west2-a | tail -200
    

See also


Verified against directory@e8287cd / web@80e3ec2 on 2026-06-07 — provisioning: infrastructure repo terraform/environments/web-demo/; client config: web repo README.md. The web service token is a Directory-signed IdentityToken minted from the admin Devices flow (devices_service.ts, device_credentials.edge) — there is no directory:create-service-token ace command. Production containers have no node ace access (directory start/routes.ts, keys_controller.ts); node ace generate:key here is the dev/lab APP_KEY bootstrap only (web/directory README.md) — production app keys are auto-generated by Terraform.