hephd's sync client is plain-HTTP-only — a Caddy https hub-url fails with a confusing 'error sending request' (HTTP connector rejects the https scheme). Spokes sync over the direct tailnet URL; heph.ops.eblu.me is for the PWA only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.7 KiB
| title | modified | last-reviewed | tags | ||
|---|---|---|---|---|---|
| Hephaestus | 2026-06-04 | 2026-06-04 |
|
Hephaestus
hephaestus (heph) is the user's
self-hosted task + context/knowledge system. It is hub-and-spoke: each device
runs a full local SQLite replica (hephd --mode local) and background-syncs
against one canonical hub. Indri runs that hub.
Quick Reference
| Property | Value |
|---|---|
| PWA URL | https://heph.ops.eblu.me (browser PWA, Caddy TLS) |
| Spoke sync URL | http://indri.tail8d86e.ts.net:8787 (direct, tailnet) |
| Local Port | 8787 (hephd --mode server, bound 0.0.0.0) |
| Binary | ~/.cargo/bin/hephd (self-updating) |
| Data | ~/.local/share/heph/heph.db |
| PWA shell | ~/.local/share/heph/web |
| Logs | ~/Library/Logs/mcquack.heph.{out,err}.log |
| LaunchAgent | mcquack.eblume.heph |
| Ansible role | ansible/roles/heph (tag heph) |
What runs on indri
The launchagent runs the hub in server mode with three features enabled:
hephd --mode server --http-addr 0.0.0.0:8787 --db ~/.local/share/heph/heph.db
--web-root ~/.local/share/heph/web
--oidc-issuer https://authentik.ops.eblu.me/application/o/heph/
--oidc-audience heph
--self-update --self-update-interval-secs 600
- Server mode exposes the HTTP sync endpoint (
/rpc,/sync/*) that spokes reconcile their op-log against. - Self-update (10-minute poll) rebuilds
hephdfrom the forge when a newer release tag appears (cargo install --git https://forge.eblu.me/eblume/hephaestus.git). Indri's Rust toolchain (~/.cargo/bin) is on the agent'sPATHfor this, and the plist pinsRUSTUP_TOOLCHAIN=stable— the launchagent runs without mise, so a barecargoshim would otherwise fall back to rustup's default toolchain, which can lag behind heph'srust-versionfloor (1.89) and silently fail the build. - PWA (
--web-root) serves the heph-pwa mobile shell; Caddy terminates TLS atheph.ops.eblu.meso the PWA runs in a secure context (service worker, install-to-home-screen, voice capture).
The hub binds 0.0.0.0 so tailnet spokes can also sync directly
(http://indri.tail8d86e.ts.net:8787); access is gated by Authentik OIDC either
way — tailnet reachability alone is not enough.
Authentication (Authentik OIDC, device-code)
The hub verifies an OIDC bearer token on every sync. The heph application is a
public OAuth2 client using the device-code flow (RFC 8628), provisioned
in the authentik blueprint (argocd/manifests/authentik/configmap-blueprint.yaml):
- Issuer:
https://authentik.ops.eblu.me/application/o/heph/ - Audience / client id:
heph - Restricted to the
adminsgroup (single-owner, sensitive data).
Because no Authentik instance ships a device-code flow by default, the blueprint
also creates default-device-code-flow and binds it to the default brand's
flow_device_code. Devices obtain a token with heph auth login; the PWA
currently takes a pasted token (in-app device-code login is upstream follow-up).
Data seeding (Path A, one-time)
The hub was seeded from the existing gilbert device so no task history was
lost. heph's data-safe bring-up ("Path A") has the hub adopt the device's
identity rather than rewriting the device:
- Quiesce the seed device:
heph daemon stop(on gilbert). - Copy its store to indri:
scp ~/.local/share/heph/heph.db indri:~/.local/share/heph/heph.db. - Give the hub its own device origin (keeps gilbert's
owner_id+ data;hephdregenerates a freshoriginon next start when it is missing):ssh indri "sqlite3 ~/.local/share/heph/heph.db \"DELETE FROM meta WHERE key='origin';\"" mise run provision-indri -- --tags heph(installs hephd, stages the PWA, loads the launchagent → hub starts on the seeded store).
Only meta.origin changes; owner_id, nodes, op-log, and links are copied
untouched. A clean hephd --owner-id / seed command is tracked upstream as
hephaestus follow-up — until then this manual reset is the documented path.
Connecting a spoke (e.g. gilbert)
A device joins by running its local daemon with the hub URL + OIDC client and logging in once:
hephd --mode local --hub-url http://indri.tail8d86e.ts.net:8787 \
--oidc-issuer https://authentik.ops.eblu.me/application/o/heph/ \
--oidc-client-id heph
heph auth login --hub-url http://indri.tail8d86e.ts.net:8787 \
--issuer https://authentik.ops.eblu.me/application/o/heph/ --client-id heph
Use the direct
http://…:8787tailnet URL for sync, not the Caddy HTTPS URL. hephd's sync client is plain-HTTP-only; pointing--hub-urlathttps://heph.ops.eblu.mefails with a confusingerror sending request(the HTTP connector rejects thehttpsscheme before connecting). Tailscale encrypts the transport, and the OIDC bearer token still gates every request.heph.ops.eblu.me(Caddy TLS) exists only for the browser PWA, which needs a secure context. The cached token is keyed by the exact--hub-url, so use the same value forhephdandheph auth login.
Caveat:
heph daemoncannot yet bake hub/spoke flags into the generated launchd plist (upstream gap). On a spoke whose plist is managed byheph daemon, the hub/OIDC flags must be hand-added — and a laterheph daemon start/restartwill regenerate the plist and drop them. Avoidheph daemonsubcommands on a configured spoke until that gap is closed; reload vialaunchctlinstead.