The launchagent and ansible run without mise activation, so a bare cargo/rustc shim falls back to rustup's default toolchain — which lagged heph's rust-version floor (1.89) on both indri (1.87) and gilbert (1.84), silently failing the build. Pin the channel explicitly in the bootstrap env and the plist. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 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 |
|---|---|
| URL | https://heph.ops.eblu.me (PWA + sync endpoint) |
| 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 https://heph.ops.eblu.me \
--oidc-issuer https://authentik.ops.eblu.me/application/o/heph/ \
--oidc-client-id heph
heph auth login --hub-url https://heph.ops.eblu.me \
--issuer https://authentik.ops.eblu.me/application/o/heph/ --client-id heph
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.