diff --git a/docs/how-to/heph-pwa.md b/docs/how-to/heph-pwa.md index dbc19ef..2a158e9 100644 --- a/docs/how-to/heph-pwa.md +++ b/docs/how-to/heph-pwa.md @@ -112,6 +112,7 @@ Search (🔍 or `/`) runs full-text search across tasks and docs. ## Related +- [[host-heph-pwa]] — serve this app from the hub (indri) with OIDC, in the hub/spoke deployment - [[set-up-sync-hub]] — stand up the server-mode hub the app talks to - [[run-the-daemon]] — run `hephd` as a managed service - [[v1-prototype-tech-spec]] — data model, RPC API, quick-add spec diff --git a/docs/how-to/host-heph-pwa.md b/docs/how-to/host-heph-pwa.md new file mode 100644 index 0000000..0be1d59 --- /dev/null +++ b/docs/how-to/host-heph-pwa.md @@ -0,0 +1,127 @@ +--- +title: Host heph-pwa from the hub +modified: 2026-06-04 +tags: + - how-to +--- + +# Host heph-pwa from the hub + +How to serve the [[heph-pwa]] mobile app from the canonical **hub** (`indri`) in +the hub-and-spoke deployment, with OIDC auth — the production counterpart of the +unauthenticated single-machine demo. Assumes the `heph-pwa` work is **merged and +released**, so the installed `hephd` already has `--web-root` and CORS. + +> Read [[set-up-sync-hub]] first — this builds directly on the hub it stands up +> (server mode, Authentik OIDC, Tailscale transport). + +## What the app needs from the hub + +The PWA is a thin, online-only client: it loads its static shell over HTTP and +makes JSON-RPC calls to the hub's `/rpc`. So the hub must (1) serve the shell +files and (2) accept the app's authenticated RPC calls. Both are already in +`hephd --mode server`: + +- `--web-root ` serves the shell for any non-API path (with an `index.html` + SPA fallback). The shell is unauthenticated — it is only HTML/JS; all data + still flows through the OIDC-gated `/rpc`. +- Every response carries permissive CORS headers and answers the `OPTIONS` + preflight, so the shell may instead be hosted anywhere and still call the hub + cross-origin. + +## 1. Put the shell on the hub + +The release does not yet bundle the app, so fetch the `heph-pwa/` directory at +the **same version tag** the hub runs (keeping shell and hub in lockstep matters +— see *Upgrades* below), and copy it to a stable path: + +```bash +# on indri, matching the running hephd version (e.g. v1.4.0) +git clone --depth 1 --branch v1.4.0 \ + https://forge.ops.eblu.me/eblume/hephaestus.git /tmp/heph-src +sudo mkdir -p /var/lib/heph/web +sudo cp -r /tmp/heph-src/heph-pwa/. /var/lib/heph/web/ +``` + +> **Future improvement:** have the release workflow package a `heph-pwa-.tar.gz` +> asset (as it already does for docs), so this step becomes "download + extract" +> and the lockstep is automatic. Until then, pin the clone to the hub's tag. + +## 2. Add `--web-root` to the hub service + +Extend the hub invocation from [[set-up-sync-hub]] with `--web-root` (everything +else — issuer, audience, db — unchanged): + +```bash +hephd --mode server \ + --http-addr 0.0.0.0:8787 \ + --db /var/lib/heph/heph.db \ + --web-root /var/lib/heph/web \ + --oidc-issuer https://authentik.ops.eblu.me/application/o/heph/ \ + --oidc-audience +``` + +In the systemd unit (or launchd plist), add the two `--web-root` arguments and +`systemctl restart hephd`. Self-update is compatible now that the release ships +the flag — just refresh the web-root on each upgrade (next section). + +## 3. Terminate TLS (recommended) + +Serve the app over **HTTPS** so it is a *secure context*: only then do the +service worker (offline launch), proper PWA install, and the Web Speech mic +work. (On iOS, "Add to Home Screen" and keyboard dictation work over plain HTTP +too, so HTTPS is a polish step, not a blocker.) Two good options: + +- **Tailscale serve** — tailnet-only, automatic MagicDNS cert, no public + exposure: + + ```bash + tailscale serve --bg --https=443 http://127.0.0.1:8787 + # app is then at https://indri..ts.net/ + ``` + + Bind `hephd` to `127.0.0.1:8787` in this case and let Tailscale be the only + thing exposing it. + +- **Reverse proxy** (Caddy / nginx) terminating a real cert, if the hub should + be reachable beyond the tailnet. Proxy all paths (`/`, `/rpc`, `/sync/*`) to + `hephd`. + +Either way the app is same-origin with the hub, so no CORS is involved and the +app defaults its hub URL to its own origin. + +## 4. Connect a phone + +1. Ensure the phone is on the tailnet (or can reach the proxy). +2. Open the hub URL (`https://indri..ts.net/`) and **Add to Home Screen**. +3. The app defaults its **Hub URL** to the origin it loaded from — no typing. +4. **Token:** the hub requires an OIDC bearer token, and the PWA does **not yet + implement the in-app device-code login** — paste a token into Settings → + Token for now. Obtain one via the device-code flow against the Authentik + client (the same flow the CLI uses; e.g. reuse the access token a logged-in + spoke cached, or run a one-off device-code grant). Tap **Test** to confirm. + +> **Known gap / next step:** wire the RFC 8628 device-code flow into the PWA's +> Settings so login is in-app (open the verification URL, poll for the token, +> store it, and refresh it) — removing the manual paste. Tracked as follow-up +> work for `heph-pwa`. + +## Upgrades + +On each hub upgrade, refresh the shell so it matches the running `hephd`: + +```bash +git -C /tmp/heph-src fetch --depth 1 origin v1.5.0 && git -C /tmp/heph-src checkout v1.5.0 +sudo cp -r /tmp/heph-src/heph-pwa/. /var/lib/heph/web/ +``` + +The service worker is versioned (`CACHE = "heph-pwa-vN"`), so an updated shell +evicts the old cache on next load. Hard-refresh once if a phone seems stuck on a +stale version. + +## Related + +- [[heph-pwa]] — the app itself (features, quick-add, voice, triage) +- [[set-up-sync-hub]] — stand up the hub + Authentik OIDC this doc extends +- [[run-the-daemon]] — run `hephd` as a managed service +- [[v1-prototype-tech-spec]] — RPC API and auth model diff --git a/docs/how-to/how-to.md b/docs/how-to/how-to.md index 62c0173..c20c904 100644 --- a/docs/how-to/how-to.md +++ b/docs/how-to/how-to.md @@ -22,3 +22,4 @@ Task-oriented guides for common operations. - [[import-todoist]] — Seed a heph store from your Todoist projects + tasks (`mise run import-todoist`) - [[self-update]] — Opt-in `hephd` self-update: poll the forge for new releases and auto-update - [[heph-pwa]] — The mobile app: an installable PWA mirror of heph-tui (browse, triage, fast quick-add, voice) +- [[host-heph-pwa]] — Serve the mobile app from the hub (indri) with OIDC, in the hub/spoke deployment