doc(heph-pwa): production runbook — host the app from the hub (indri) with OIDC
All checks were successful
Build / validate (pull_request) Successful in 6m18s

Add host-heph-pwa.md: a deployment how-to for serving the PWA from the canonical
hub in the hub/spoke OIDC setup (post-release) — fetch the shell at the hub's
tag, add --web-root, terminate TLS (tailscale serve / reverse proxy), and the
token-paste caveat with the device-code-login follow-up. Cross-linked from
heph-pwa and the how-to index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-06-04 17:17:25 -07:00
commit 936c2635ef
3 changed files with 129 additions and 0 deletions

View file

@ -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

View file

@ -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 <dir>` 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-<version>.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 <heph-client-id>
```
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.<tailnet>.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.<tailnet>.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

View file

@ -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