A spoke could be silently failing to sync (expired token → 401, or hub
unreachable) with the only signal buried in the daemon log. Now:
- hephd tracks SyncHealth (last attempt/success time, last error, auth-failure
flag) from the background sync loop and sync.now, classifying a 401 as an auth
failure. sync.status returns it plus the pending merge-conflict count.
- heph-tui shows a live status-line indicator (spoke only): '⟳ <age>' since the
last good sync, red '⚠ auth' when re-login is needed, '⚠ offline' when the hub
is unreachable, and '⚠ N conflicts' when conflicts are pending. The event loop
polls on a 2s tick so the age advances and failures appear while idle.
- docs: recommended Authentik access/refresh token validity to stop frequent
re-logins (with the iOS PWA localStorage-eviction caveat).
Closes the 'Add hub connection status to heph-tui' and 'Spoke sync health:
surface unhealthy state instead of silent 401 spam' backlog items.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document the PKCE 'Login with Authentik' flow, the hub /config zero-config
discovery, and the redirect-URI prerequisite on the Authentik heph provider.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Document serving the app from the hub (--web-root), connecting (hub URL +
optional token), quick-add syntax, voice, triage, and the deliberate
design choices (PWA over native iOS; online-only; token paste vs device flow)
with their known limitations to revisit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the eight Mikado scaffolding cards (+ goal card) into one
user-facing how-to, docs/how-to/self-update.md: what self-update is and
how to enable it. The per-card breakdown was build-time scaffolding, not
documentation. Keeps the changelog fragment; updates the how-to index.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Public repo => anonymous HTTPS clone, no credentials (the SSH/canonical
premise was wrong: that was the access-restricted cargo registry, not git
clone). Install URL points at the canonical public host (verified end to
end); the service template bakes cargo onto PATH. Card rewritten to
reflect what actually happened.
The repo is public, so self-update needs no credentials: cargo install
--git is a plain anonymous clone (NOT the access-restricted Forgejo cargo
registry, which is what required forge.ops.eblu.me). Point INSTALL_GIT_URL
and the releases poll at the canonical public host over HTTPS — verified
end-to-end (cargo install --git https://forge.eblu.me/... --tag v1.0.3
builds a working hephd with zero auth).
Make the headless service able to run the apply path: 'heph daemon
start --self-update' (default off) generates a launchd/systemd service
that passes --self-update and bakes a PATH (incl ~/.cargo/bin) + HOME so
the minimal service env can find cargo. restart preserves the setting.
Default (no flag) services are byte-identical to before. Template + URL
behavior covered by unit tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restarter + ProcessRestarter wired: install then exit(0) so the service
manager respawns the new binary; restart only on a successful install.
Unit-tested via injection.
Installer trait + CargoInstaller + apply_update landed and unit-tested
via injection. Real cargo execution is gated on the deployment env
(service-env-forge-access).
Self-restart works by exiting cleanly and letting the service manager
respawn the new binary. launchd already does this (KeepAlive=true), but
the systemd user unit was Restart=on-failure, which ignores a clean
exit (code 0). Switch to Restart=always + RestartSec=1, update the unit
test, and note in run-the-daemon that existing Linux installs must
`heph daemon restart` once to regenerate the unit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kick off the C2 Mikado chain for an opt-in (default-off) hephd
self-update mode (forge-poll -> cargo install from tag -> self-restart).
Goal card plus eight prerequisite cards, indexed from how-to.md:
release-poll-version-check, self-update-opt-in-flag (leaves)
-> self-update-poll-loop (notify-only core)
service-env-forge-access (leaf, the cargo/forge blocker)
+ self-update-poll-loop -> cargo-install-from-tag
service-respawn-on-clean-exit (leaf, systemd Restart=always)
+ cargo-install-from-tag -> self-restart-after-update
verify-hub-dropout-resilience (leaf, lock in the base-case guarantee)
Grounded in research of hephd's sync loop, daemon lifecycle, the
launchd/systemd service templates, and the forge releases API.
Captured from Hephaestus task 01KTA2NSNRYT902HC3VRW00S1J.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the pre-release 'install from feature/v1-prototype' instructions with
`--tag v1.0.0` as the default, and document `--branch main` as the track-
unreleased-work alternative.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Documents the desired end state before the code change (C1 docs-first):
- release.yaml bumps the workspace version into a commit only the tag points
at, keeping main at 0.0.0
- heph/hephd --version will report the release version plus the build SHA
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cheap "seam" that keeps the single-owner hub from calcifying, ahead of
the gilbert -> indri bring-up:
- Replace the single-tenant gate `Store::authorize_owner_sub(sub) -> bool`
with `resolve_owner(sub) -> Option<owner_id>`. The hub auth middleware now
resolves the token's identity to the owner it may act as (Some -> allow,
None -> 403). Behavior is identical for the single-owner hub (claim-on-first;
strangers still 403), but the contract no longer assumes one global owner, so
serving N owners later is additive, not a rewrite. The per-request owner is
marked at the exact line where downstream scoping wires through.
- New how-to docs/how-to/set-up-sync-hub.md: stand up the hub and connect an
existing device as an offline-capable spoke, the data-safe way (Path A: the
hub adopts the device's identity rather than rewriting the device).
The decision (cheap seam now, defer full multi-tenancy + adoption rewrite) is
recorded in the Adoption + multi-tenant task's context doc. Two enabler gaps
the how-to surfaced (heph daemon hub/spoke service flags; Path-A seeding tool)
are filed as Hephaestus tasks.
Green: 228 tests, clippy -D warnings + fmt + prek clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Neovim plugin now lives at eblume/hephaestus.nvim (plugin at the repo
root). Remove heph.nvim/ from the monorepo and the build/test wiring that
referenced it:
- Dagger: drop the test_nvim function + the pinned-Neovim NVIM_VERSION
- build.yaml: drop the `dagger call test-nvim` step
- drop the mise run test-nvim task and .stylua.toml + the stylua prek hook
(no Lua remains in the monorepo)
- install-heph.md: install via a plain lazy.nvim spec pointing at the
plugin repo over SSH (no more local-dir checkout hack)
- README / AGENTS / heph-nvim.md: note the surface lives in its own repo
The CLI/TUI -> nvim integration is unchanged (they shell out to `nvim`
expecting the heph plugin installed). The v1-prototype tech-spec §14 build
record and prior changelog fragments are left as frozen history.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1 reached Todoist feature-parity, so remaining/future work is now tracked
in heph itself — tasks in the Hephaestus project (heph view ondeck) — not in
a doc. Renamed docs/reference/tech-spec.md -> v1-prototype-tech-spec.md and
rewrote all 27 [[tech-spec]] wiki-links + README/changelog path refs (docs
checks green). Retitled + bannered the spec as a historical v1 build record
and froze its §14 tracker. AGENTS.md gains a "Planning future work" section
(capture via `heph task --project Hephaestus`, triage in heph-tui On Deck);
README status reflects parity + the three daily-driver surfaces. The design
doc remains the living rationale.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A surface-owned, auto-spawned daemon can't be shared once the CLI is also a
first-class client — so drop auto-spawn and manage the daemon as an OS service.
- design §4: daemon lifecycle = explicit OS service; surfaces connect-only
- heph-nvim.md: rewrite the daemon-lifecycle section (connect-only) + history
- new how-to/run-the-daemon.md (heph daemon start/stop/restart/status); indexed
- install-heph.md: post-install is `heph daemon start`; plugin no longer spawns
- tech-spec §14: mark the managed-daemon entry superseded
- changelog fragment
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Turn the one-off Todoist importer into a documented, repeatable mise task.
Self-contained (spawns its own hephd), dry-run by default into a throwaway
store, `-- --commit` writes into the real store after backing it up. Auth via
TODOIST_TOKEN or TODOIST_OP_REF (op://). Mapping per design §6.2.1:
project hierarchy (+ Inbox→unfiled), priority→attention by meaning, due→do-date,
NL recurrence, descriptions + sub-tasks→canonical-context doc.
- mise-tasks/import-todoist
- docs/how-to/import-todoist.md (+ how-to index, reference mise-tasks table)
- changelog fragment
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- mise-tasks/dev runs the working-tree hephd on isolated .dev/ paths (gitignored)
so in-repo development never touches the installed daemon's data; point a dev
nvim at it via HEPH_SOCKET/HEPH_DB.
- docs/how-to/install-heph.md: install heph/hephd from the forge (build from
source), the lazy.nvim `dir` setup for the subdir plugin, and dev isolation.
- gitignore .dev/ and the transient .claude scheduled-task state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>