Skip to content

feat: CLI backend auth and same-account iroh auto-trust#1064

Draft
ital0 wants to merge 87 commits into
mainfrom
italomenezes/thunderbolt-cli-auth
Draft

feat: CLI backend auth and same-account iroh auto-trust#1064
ital0 wants to merge 87 commits into
mainfrom
italomenezes/thunderbolt-cli-auth

Conversation

@ital0

@ital0 ital0 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 2 of the Thunderbolt CLI: backend auth + same-account iroh auto-trust, implementing the approved design in docs/superpowers/specs/2026-07-03-cli-auth-same-account-trust-design.md. Stacked on #1032 (italomenezes/thunderbolt-cli-mvp).

  • thunderbolt login — RFC 8628 device-authorization grant via Better Auth (deviceAuthorization plugin): prints verification link + user code, best-effort terminal QR, polls for the signed bearer, stores it in the CLI config. PAT via THUNDERBOLT_TOKEN env (apiKey plugin, x-api-key auth) is the zero-human CI / self-host escape hatch.
  • /device app route — approval page for the CLI login (mirrors ApproveDeviceDialog); requires a signed-in session, and the user_code now survives the login redirect (stash + replay).
  • Same-account auto-trust at the iroh bridge — the bridge fetches the account's trusted NodeIds from a new bearer-scoped REST endpoint (GET /devices/allowlist), caches it, and refreshes every 45s. The connection gate becomes: account allowlist OR the manual iroh allow file (which stays, and remains the only path in Standalone mode).
  • 45s heartbeat — refreshes the allowlist, tears down open connections of revoked peers, and tears down the bridge's own account trust when the bridge itself is revoked (revoke nulls node_id, so the bridge's own id disappearing from a populated allowlist is the self-revocation signal).
  • Transparent enrollment — adding an ACP agent or MCP-via-bridge self-enrolls the app's dialer NodeId (POST /devices/me/node-id, no canary) and registers the bridge as a device (device_type='bridge', reserved bridge- id namespace). The canary-gated attestation route is unchanged.
  • device_type synced column on devices + bridge badge in device settings.

Deploy notes

  • device_type synced-column ordering: the backend column commit (b6f1cb65) is first and separable. Run migration 0022 and refresh the PowerSync Cloud dashboard rules before relying on device_type cross-device; until then it stays null cross-device and reads as a normal device (null-tolerant, so same-PR deploy is safe). Migrations 0022/0023 have their backend/drizzle/meta/_journal.json entries.
  • The devices sync rule is SELECT * — no config.yaml change needed.

Beyond-spec hardening (disclosed)

  • isSecureCloudUrl HTTPS-or-loopback policy on the CLI cloud URL (login + bridge), aligned with RFC 8628 §3.1.
  • Server-managed device_type: reserved bridge- id namespace and a PowerSync upload deny so clients can't self-label a device a bridge.

Review

Two-axis review (spec compliance + repo standards) plus a simplify-and-validate pass ran on this branch; the gaps found were fixed here: PAT now works end-to-end (env credential reaches the bridge with x-api-key), D8 self-revocation teardown is wired, user_code survives the login redirect, /device is lazy-loaded, and sizing/comment nits are aligned with CLAUDE.md.

Testing

  • CLI: 281 tests, tsc clean.
  • Backend: full suite green, including --rerun-each 5; device-grant, self-enroll (own-id only), and allowlist (trusted + non-revoked, account-scoped) routes covered per backend/docs/testing.md.
  • Frontend: full suite green, including --rerun-each 5; /device approval logic and transparent-enrollment triggers covered per docs/development/testing.md.
  • bun run check (type-check, eslint, prettier, license headers) clean.

🤖 Generated with Claude Code

ital0 added 30 commits June 28, 2026 20:16
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Semgrep Security Scan

No security issues found.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Metrics

Metric Value
Lines changed (prod code) +1780 / -46
JS bundle size (gzipped) 🟢 632.6 KB → 635.6 KB (+3.0 KB, +0.5%)
Test coverage 🟢 77.59% → 77.80% (+0.2%)
Performance (preview) Preview not ready — Render deploy may have timed out
Accessibility
Best Practices
SEO

Updated Thu, 09 Jul 2026 19:21:43 GMT · run #2252

@ital0 ital0 self-assigned this Jul 8, 2026
ital0 added 16 commits July 8, 2026 13:37
…olt-cli-mvp

# Conflicts:
#	bun.lock
#	package.json
#	src/hooks/use-add-server-form.test.ts
#	src/hooks/use-mcp-sync.tsx
#	src/settings/mcp-servers.test.tsx
#	src/settings/mcp-servers.tsx
…' into italomenezes/thunderbolt-cli-auth

# Conflicts:
#	cli/src/iroh/bridge.test.ts
#	cli/src/iroh/bridge.ts
Base automatically changed from italomenezes/thunderbolt-cli-mvp to main July 13, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant