Commit graph

5 commits

Author SHA1 Message Date
8fe11c75cd feat(hephd): --owner-id flag — one-step Path-A hub seeding
All checks were successful
Build / validate (pull_request) Successful in 13m12s
A fresh hub started with an existing device's owner id rebuilds itself
entirely from that spoke's first full op-log push: no snapshot copy and
no origin reset (the new store mints its own). adopt_owner is idempotent
once adopted, so the flag is safe baked into a service unit.
[[set-up-sync-hub]] documents the recipe and drops the manual-seeding gap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 10:55:17 -07:00
9213a943f5 C2(hephd-self-update): impl verify hub-dropout resilience (+ client timeout)
Lock in the base-case guarantee that a self-updating hub (which restarts
under its spokes) relies on. New sync_http test: a spoke whose hub is
unreachable keeps serving + accepting writes, a sync attempt fails fast
(Err, not hang/panic), and when the hub returns the accumulated ops
reconcile with no special recovery.

The verification surfaced one non-graceful path — the daemon's shared
reqwest client had no timeout, so a black-hole hub (connects, never
replies) could stall the sync/self-update loop. Give it a 30s timeout so
'the hub can vanish at any moment' holds even mid-request.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 13:47:26 -07:00
f4db186234 hephd: OIDC client auth — device-code flow + token attach (auth 10b)
Some checks failed
Build / validate (pull_request) Failing after 9s
Close the auth loop: clients obtain a bearer token and present it to the
hub (tech-spec §13).

- oauth module: DeviceFlow (RFC 8628 — discover, start, poll handling
  authorization_pending/slow_down, refresh) + StoredToken + TokenStore
  (OS keyring via `keyring`, in-memory for tests) + current_bearer (loads
  and refreshes-on-expiry).
- heph auth login/logout: runs the device flow, prints the verification
  URL + user code, caches the token in the keyring.
- sync_once gains a bearer arg; the daemon (Daemon::spawn_sync_loop +
  sync.now) obtains it via current_bearer; RemoteStore attaches it to /rpc.
  --oidc-issuer/--oidc-client-id configure the spoke/client.
- Fix a latent panic: reqwest::blocking spins its own runtime and panics
  inside the daemon's spawn_blocking pool. All blocking auth/proxy HTTP
  (OidcVerifier JWKS, DeviceFlow, RemoteStore) now uses runtime-free `ureq`;
  async reqwest remains only for sync_once. (Caught by the new e2e test.)
- Tests (offline): device flow + refresh + token store vs a mock OAuth
  server; a full spoke->authenticated-hub loop (valid token accepted,
  missing token rejected) signed by a runtime-generated RSA key.

112 tests green; clippy -D warnings + fmt + prek clean. Slice 10 (auth)
complete; next is heph.nvim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:27:36 -07:00
497c62a988 hephd: OIDC hub authentication — verification side (auth 10a)
Some checks failed
Build / validate (pull_request) Failing after 3s
Authenticate op exchange at the network boundary (tech-spec §13). The hub
now requires a valid OIDC bearer token on /sync/* and /rpc; local mode is
unchanged (no auth).

- heph-core: Store::authorize_owner_sub — single-tenant gate that claims the
  owner's oidc_sub on first sight, then authorizes only that sub (403 for any
  other identity). LocalStore impl over users.oidc_sub; RemoteStore stub.
- hephd auth module: TokenVerifier trait (mockable seam) + OidcVerifier
  (jsonwebtoken, rust_crypto). Strict validation: RS256 pinned, exact iss +
  aud, exp/nbf, required sub; JWKS discovered + cached, refetched on unknown
  kid (rotation). Claims/AuthError.
- Hub router takes Option<verifier>; an axum middleware on every route
  extracts the Bearer token, verifies it off the async worker, and runs the
  owner gate — 401 missing/invalid, 403 wrong identity, 503 IdP-unreachable.
  Open (no auth) when unconfigured, for local dev.
- main: --oidc-issuer/--oidc-audience enable the hub verifier (server mode).
- Security tests, all offline: stub-verifier middleware (missing/bad/valid +
  owner gate) and an adversarial battery driving OidcVerifier against an
  in-process mock IdP — rejects expired, wrong iss/aud, unknown kid, tampered
  signature, alg confusion (HS256/none), and missing sub. The RSA key + JWKS
  are generated at runtime (rsa/rand/base64 dev-deps) so no key is committed.
- tech-spec: add an end-of-v1 dependency-refresh pass to the roadmap.

108 tests green; clippy -D warnings + fmt + prek clean. Next: client-side
device-code login + keyring (10b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:58:20 -07:00
8c25d114c4 hephd: network sync over HTTP — hub + spoke (sync 9a)
Some checks failed
Build / validate (pull_request) Failing after 2s
Wire the existing merge engine over the network so the everyday config
(local + hub_url) syncs through a hub. Transport ratified = axum HTTP/JSON
(tech-spec §6.1, §12).

- heph-core: SyncCursors model + Store::sync_state/record_sync over the
  sync_state table (per-peer push/pull HLC cursors). Incremental, so each
  exchange transfers only the tail.
- hephd::sync: the hub router (POST /sync/push, GET /sync/pull?after=<hlc>)
  served from the shared LocalStore, and sync_once — a spoke's pull-then-
  merge, then push-tail exchange, advancing the cursors. Idempotent: a
  re-pushed op the hub already has is a no-op.
- Daemon carries optional hub config; sync.now/sync.status handled at the
  daemon (they need the hub transport the store can't reach). conflicts.
  list/resolve now reachable over the unix socket too.
- main: --mode local|server, --hub-url, --http-addr. server mode binds the
  hub HTTP endpoint on the same store; a local+hub_url spoke background-
  syncs on a 30s interval.
- tests/sync_http.rs: two spokes converge through a real-HTTP hub on an
  ephemeral port — node propagation and a divergent-scalar conflict.

Unauthenticated/single-owner for now; OIDC + per-user scoping is slice 10,
client mode + RemoteStore is 9b. 100 tests green; clippy -D warnings + fmt
+ prek clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:14:20 -07:00