Skip to content

feat(tunnel): add up/down commands + unify login/logout [RD-209] - #28

Merged
racerxdl merged 3 commits into
mainfrom
teske/rd-tunnel-up-down-unified-login
Jun 3, 2026
Merged

racerxdl merged 3 commits into
mainfrom
teske/rd-tunnel-up-down-unified-login

Conversation

@racerxdl

@racerxdl racerxdl commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Two UX fixes for the tunnel CLI surface.

1. Tunnel lifecycle commands

  • hsh tunnel up — bring the netstack online without re-authenticating
  • hsh tunnel down — take it offline while staying logged in

Drives the new POST /v1/tunnel/{up,down} daemon endpoints. up maps a 409 not_logged_in to an actionable 'run hsh login' hint via the new TunnelApiError.isNotLoggedIn(); both surface the already_up/already_down no-op states.

2. Unified login/logout

Previously hsh login (CLI keychain auth) and hsh tunnel login (daemon auth) looked redundant and confused users. Now:

  • hsh login logs the CLI in, then — best-effort — drives the daemon's own login flow if the daemon is installed/running/configured. If not, it silently no-ops, so tunnel-less setups are unaffected.
  • hsh logout mirrors this.
  • --no-tunnel opts out of the daemon step on both.

Trust model preserved: loginDaemon() routes credentials through the daemon's IPC login flow (the daemon does its own gateway round-trip) — we never hand it the CLI's keychain token. The daemon remains the sole writer of its token.

hsh tunnel login/logout remain as explicit daemon-only entry points, now thin wrappers over the shared loginDaemon/logoutDaemon.

Testing

  • tests/tunnel-ipc-client.test.ts: up/down request shape, already_up/already_down flags, and the 409-vs-401 error distinction.
  • Full suite: 437 pass, typecheck clean.

Dependency

Requires the daemon-side endpoints in hoophq/hoop#1507. Against an older daemon the new endpoints return 501 and the CLI renders a clean 'not implemented in this daemon build' message — no crash.

Automated by MisterMal

Two UX fixes for the tunnel CLI:

1. Tunnel lifecycle commands
   - hsh tunnel up    bring the netstack online without re-auth
   - hsh tunnel down  take it offline while staying logged in
   Drives the new POST /v1/tunnel/{up,down} daemon endpoints. `up`
   maps a 409 not_logged_in to an actionable 'run hsh login' hint via
   the new TunnelApiError.isNotLoggedIn() helper; both surface the
   already_up/already_down no-op states.

2. Unified login/logout
   Previously `hsh login` (CLI keychain auth) and `hsh tunnel login`
   (daemon auth) looked redundant. Now `hsh login` logs the CLI in and
   then, best-effort, drives the daemon's own login flow if the daemon
   is installed/running/configured — otherwise it silently no-ops, so
   tunnel-less setups are unaffected. `hsh logout` mirrors this.

   The daemon still owns its token: loginDaemon() routes credentials
   THROUGH the daemon's IPC login flow (it does its own gateway
   round-trip); we never hand it the CLI's keychain token. This
   preserves the daemon-as-sole-token-writer trust model.

   --no-tunnel opts out of the daemon step on both commands.

`hsh tunnel login`/`logout` remain as the explicit daemon-only entry
points (now thin wrappers over the shared loginDaemon/logoutDaemon).

Tests: tunnel-ipc-client.test.ts covers up/down request shape, the
already-up/down flags, and the 409-vs-401 error distinction.

Requires the daemon-side endpoints (hoophq/hoop). Against an older
daemon the new endpoints return 501 and the CLI renders a clean
'not implemented in this daemon build' message.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
On-demand counterpart to the daemon's new periodic auto-refresh:

  hsh tunnel refresh   re-fetch the connection list from the gateway

Drives POST /v1/connections/refresh. Reports the active connection
count on success and a clear 'tunnel is down — bring it up first' hint
when the daemon has no live tunnel (running: false).

Adds TunnelClient.refreshConnections() + RefreshConnectionsResponse,
with IPC client tests covering the request shape and the down no-op.

Pairs with the daemon-side endpoint in hoophq/hoop.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
@racerxdl

racerxdl commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Update: added hsh tunnel refresh

Pairs with the daemon's new periodic + on-demand connection-list refresh (hoophq/hoop#1507). hsh tunnel refresh forces an immediate re-fetch; the daemon also auto-refreshes every 60s now.

Automated by MisterMal

1.86.0 is the hoop release that ships the tunnel up/down lifecycle
endpoints and the connection-list refresh that the new
`hsh tunnel up|down|refresh` commands depend on.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
@racerxdl
racerxdl merged commit 4548783 into main Jun 3, 2026
3 checks passed
@racerxdl
racerxdl deleted the teske/rd-tunnel-up-down-unified-login branch June 3, 2026 17:01
racerxdl added a commit that referenced this pull request Jul 6, 2026
…eychain tests [DEP-37]

Review follow-ups:

- macOS backend now writes the token through `security -i` with the
  command on stdin instead of `-w <token>` on argv, closing the brief
  window where any same-user process could read the secret via ps. The
  quoting rules (backslash and double-quote escapes) were verified
  empirically against the security CLI; values with control characters
  are rejected up front since the -i protocol is line-oriented. The
  code comments previously claimed stdin was used while the
  implementation inlined the value — both now match reality.

- MacOSKeychain accepts injectable service/account (defaults unchanged)
  so integration tests can target a scratch keychain item. Six new
  darwin-only tests exercise the real keychain: round-trip, in-place
  update, quoting edge cases, absent item, idempotent delete, and
  control-character rejection.

- README documents token storage per platform, the
  HSH_KEYCHAIN_BACKEND override, the deliberate decision to keep the
  fallback file plaintext-0600 (an encryption key stored next to the
  file adds nothing), and the downgrade caveat after migration.

Also rebased onto main (0.3.2); logout keeps the --no-tunnel daemon leg
from #28 with the now-async CLI logout.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
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.

2 participants