Skip to content

DEP-142: show connection credentials in tunnel ls - #33

Merged
racerxdl merged 2 commits into
mainfrom
dep-142
Sep 4, 2026
Merged

racerxdl merged 2 commits into
mainfrom
dep-142

Conversation

@racerxdl

@racerxdl racerxdl commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Description

hsh tunnel ls now prints each connection's fixed credentials as a ready-to-paste client command, sourced from the daemon.

Requires the daemon-side change in hoophq/hoop#1722, which adds username/password to /v1/connections and — more importantly — makes those credentials actually work. Until that lands, the fields come back empty and ls renders as it does today.

Changes

  • src/commands/tunnel.ts — renderConnections prints a connect command per row, plus a one-line note that the credentials are local to the tunnel. The note is suppressed when no listed connection has credentials, so a list of tcp connections doesn't advertise a login that doesn't exist.
  • src/dashboard/commands.ts — renderCommand takes the daemon's credentials instead of guessing. It previously interpolated the shell-level username (psql -U $(whoami)), which produced commands that could not connect. Now shared by tunnel ls and the dashboard's "Copy command" button.
  • src/dashboard/server.ts — /api/commands/:subtype looks the connection up in the daemon's snapshot and renders from that record; removed the now-dead userName plumbing. Added httpproxy to KNOWN_SUBTYPES (it was already tunnelable server-side).
  • src/tunnel/types.ts — username/password on Connection; httpproxy added to ConnectionSubtype.

Example output

Tunneled connections

  pg-prod.hoop      postgres  port  5432  fd5a:1b2c:3d4e::a1b2
                    PGPASSWORD=noop psql -h pg-prod.hoop -U noop
  mysql-stg.hoop    mysql     port  3306  fd5a:1b2c:3d4e::e5f6
                    mysql -h mysql-stg.hoop -u noop -pnoop
  mongo-an.hoop     mongodb   port 27017  fd5a:1b2c:3d4e::0708
                    mongosh "mongodb://noop:noop@mongo-an.hoop/?directConnection=true"
  redis-cache.hoop  tcp       port   any  fd5a:1b2c:3d4e::3344
                    nc -v redis-cache.hoop <port>

  Credentials above are fixed and local to this tunnel — not your database's.

How to test

bun run typecheck
bun test

tests/dashboard.test.ts pins the per-client credential syntax (each tool takes them differently — PGPASSWORD=, -p<pass> glued, -P <pass>), that /api/commands renders from the daemon record rather than the shell user, and that tcp carries no credentials.

Full suite: 456 pass, 1 fail — ssh-askpass integration: non-Hoop passthrough, which fails identically on a clean tree and is unrelated.

Notes

Those credentials are placeholders, not secrets: the agent's protocol proxy accepts them locally and re-authenticates upstream with the connection's stored credentials. Printing them is the point — the user should never have to hunt for real ones.

Automated by MisterMal

`hsh tunnel ls` prints each connection's fixed credentials as a
ready-to-paste client command, sourced from the daemon.

The dashboard shares those templates and now renders them from the
daemon's record too, replacing a shell-username guess that produced
commands which could not connect.

�� Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

DEP-142: Show tunnel credentials in connection commands

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Displays daemon-supplied tunnel credentials as ready-to-paste client commands.
• Reuses credential-aware command templates across CLI and dashboard.
• Supports HTTP proxy commands and validates protocol-specific syntax.
Diagram

sequenceDiagram
  participant D as Tunnel Daemon
  participant C as Tunnel CLI
  participant A as Dashboard API
  participant R as Command Renderer
  participant U as Dashboard UI
  C->>D: List connections
  D-->>C: Records and credentials
  C->>R: Render each record
  R-->>C: Client commands
  U->>A: Request command
  A->>D: Find connection
  D-->>A: Record and credentials
  A->>R: Render daemon record
  R-->>A: Client command
  A-->>U: Return command
Loading
High-Level Assessment

The centralized, typed command renderer is the appropriate approach because it prevents CLI and dashboard templates from diverging while preserving exhaustive subtype handling. Client-side templates or daemon-rendered shell commands were considered implicitly but would duplicate credential logic or couple the daemon to specific client executables and shell syntax.

Files changed (5) +180 / -95

Enhancement (2) +44 / -7
tunnel.tsDisplay client commands beneath tunneled connections +25/-4

Display client commands beneath tunneled connections

• Renders a protocol-appropriate command for every listed connection using the shared dashboard command generator. Shows the fixed-credential explanation only when at least one connection supplies credentials.

src/commands/tunnel.ts

types.tsModel tunnel credentials and HTTP proxy connections +19/-3

Model tunnel credentials and HTTP proxy connections

• Adds fixed username and password fields to daemon connection records. Extends the supported connection subtype union with 'httpproxy'.

src/tunnel/types.ts

Bug fix (2) +73 / -71
commands.tsGenerate commands from daemon-provided credentials +55/-49

Generate commands from daemon-provided credentials

• Replaces shell-user guesses and password placeholders with daemon-supplied fixed credentials using client-specific syntax. Adds expected-port handling for Oracle, HTTP proxy rendering, and MongoDB direct-connection mode.

src/dashboard/commands.ts

server.tsResolve command inputs from daemon connection records +18/-22

Resolve command inputs from daemon connection records

• Looks up requested connections in the daemon snapshot and returns 404 when they are unavailable. Removes operating-system username plumbing and recognizes HTTP proxy connections.

src/dashboard/server.ts

Tests (1) +63 / -17
dashboard.test.tsVerify daemon credentials and client command syntax +63/-17

Verify daemon credentials and client command syntax

• Updates fixtures for the expanded connection contract and verifies daemon-backed endpoint rendering. Adds coverage for missing connections, HTTP proxy commands, protocol-specific credential syntax, and credential-free TCP commands.

tests/dashboard.test.ts

@qodo-code-review

qodo-code-review Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Missing credentials render invalid commands ✓ Resolved 🐞 Bug ≡ Correctness
Description
renderCommand interpolates empty or omitted daemon credentials unconditionally, producing commands
such as PGPASSWORD=undefined ... -U undefined (omitted fields) or empty username/password
arguments (empty fields). This breaks both tunnel ls and the dashboard command endpoint when used
with a daemon that does not yet return credentials, rather than retaining usable legacy output.
Code

src/dashboard/commands.ts[R62-64]

+      // psql has no password flag; PGPASSWORD is the only way to avoid an
+      // interactive prompt.
+      return `PGPASSWORD=${pass} psql -h ${host} -U ${user}`;
Evidence
The IPC client returns daemon JSON directly without normalizing newly introduced fields. Both
callers forward those values unchanged, while every database template immediately interpolates them;
the CLI's username !== "" check also treats an omitted (undefined) username as present.

src/tunnel/ipc-client.ts[148-151]
src/dashboard/commands.ts[56-83]
src/dashboard/server.ts[239-253]
src/commands/tunnel.ts[780-805]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Credential-bearing templates currently interpolate missing or empty daemon credentials, causing invalid commands during compatibility with older daemon responses. Avoid rendering credential-dependent commands unless both credential fields are available, and ensure omitted JSON fields are treated consistently with empty fields.

## Issue Context
`TunnelClient.connections()` returns parsed daemon objects without runtime validation or defaults. TypeScript-required fields can therefore still be `undefined` when an older daemon omits them.

## Fix Focus Areas
- src/dashboard/commands.ts[56-83]
- src/commands/tunnel.ts[780-805]
- src/dashboard/server.ts[239-253]
- src/tunnel/ipc-client.ts[148-151]
- tests/dashboard.test.ts[187-220]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Lowercase hoop naming added ✓ Resolved 📘 Rule violation § Compliance
Description
New comments refer to the product as lowercase hoop instead of the approved name Hoop. This
introduces inconsistent product naming across source and test artifacts.
Code

src/dashboard/commands.ts[R91-92]

+      // An opaque user-defined upstream: hoop cannot know which client the
+      // user needs, and any credentials are their own.
Evidence
PR Compliance ID 2219341 requires the product to be called Hoop in comments and text artifacts.
The added comments use lowercase hoop as the product name in three locations.

Rule 2219341: Use only approved product naming in code and text artifacts
src/dashboard/commands.ts[91-92]
src/tunnel/types.ts[65-66]
tests/dashboard.test.ts[279-280]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New comments use lowercase `hoop` when referring to the Hoop product, contrary to the approved product naming requirement.

## Issue Context
Keep `.hoop` host suffixes and repository identifiers unchanged; only capitalize prose references to the product.

## Fix Focus Areas
- src/dashboard/commands.ts[91-92]
- src/tunnel/types.ts[65-66]
- tests/dashboard.test.ts[279-280]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 15 rules
Review mode: ⚖️ Balanced: This behavior change handles daemon-sourced credentials across CLI and dashboard paths, alters multiple protocol command templates and API lookup behavior, creating genuine security and compatibility risks best assessed in one complete review.

Grey Divider

Tip of the day
💡 Did you know, you can route each action level your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/dashboard/commands.ts Resolved
Comment thread src/dashboard/commands.ts Resolved
@racerxdl
racerxdl requested a review from mtmr0x August 19, 2026 17:33
A daemon predating the credentials field omits username/password, and
TunnelClient casts the response without validating, so the fields are
undefined at runtime. The templates interpolated them directly, printing
"PGPASSWORD=undefined psql -U undefined".

Each subtype now falls back to its credential-free form, which prompts
instead. Also capitalise the product name in prose comments.

Reported by Qodo on the pull request.

�� Generated with Mister Maluco

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

Copy link
Copy Markdown
Contributor Author

Thanks Qodo — pushed c1c60ef.

Finding 2 (invalid commands without credentials) was a real bug. Confirmed before fixing: a daemon predating the credentials field omits username/password, and TunnelClient.connections() casts the response with no runtime validation, so the fields are undefined despite the types. The templates printed:

PGPASSWORD=undefined psql -h pg-prod.hoop -U undefined
mongosh "mongodb://undefined:undefined@m.hoop/?directConnection=true"

Each subtype now falls back to its credential-free form, which prompts instead of failing:

psql -h pg-prod.hoop
mysql -h pg-prod.hoop -p
mongosh "mongodb://pg-prod.hoop/?directConnection=true"

Guarded with Boolean(user) && Boolean(pass) so undefined and "" behave identically — the empty case came from tcp, which legitimately has no credentials. The same fix applies to the credentials note in tunnel ls, where c.username !== "" was true for undefined and would have advertised a login that was never printed.

Added 10 regression tests covering both shapes per subtype: no undefined in output, and no dangling -U/-u/-P flag or empty ://:@ in the URI form.

Finding 1 (product naming) fixed — the two prose references now say Hoop; .hoop hostnames and hoophq identifiers left alone.

Typecheck clean, 466 pass. The one failure (ssh-askpass integration: non-Hoop passthrough) is pre-existing and reproduces on a clean tree.

@qodo-code-review

Copy link
Copy Markdown

Thanks Qodo — pushed c1c60ef.

Finding 2 (invalid commands without credentials) was a real bug. Confirmed before fixing: a daemon predating the credentials field omits username/password, and TunnelClient.connections() casts the response with no runtime validation, so the fields are undefined despite the types. The templates printed:

PGPASSWORD=undefined psql -h pg-prod.hoop -U undefined
mongosh "mongodb://undefined:*********@m.hoop/?directConnection=true"

Each subtype now falls back to its credential-free form, which prompts instead of failing:

psql -h pg-prod.hoop
mysql -h pg-prod.hoop -p
mongosh "mongodb://pg-prod.hoop/?directConnection=true"

Guarded with Boolean(user) && Boolean(pass) so undefined and "" behave identically — the empty case came from tcp, which legitimately has no credentials. The same fix applies to the credentials note in tunnel ls, where c.username !== "" was true for undefined and would have advertised a login that was never printed.

Added 10 regression tests covering both shapes per subtype: no undefined in output, and no dangling -U/-u/-P flag or empty ://:@ in the URI form.

Finding 1 (product naming) fixed — the two prose references now say Hoop; .hoop hostnames and hoophq identifiers left alone.

Typecheck clean, 466 pass. The one failure (ssh-askpass integration: non-Hoop passthrough) is pre-existing and reproduces on a clean tree.

Thanks for the update. The fixes address both items: finding 1 is resolved with the corrected Hoop naming, and finding 2 now safely falls back when credentials are missing or empty, including the tunnel ls guidance. The regression coverage and clean typecheck look appropriate; the reported ssh-askpass integration: non-Hoop passthrough failure is noted as pre-existing.

@racerxdl
racerxdl merged commit af941f9 into main Sep 4, 2026
3 checks passed
@racerxdl
racerxdl deleted the dep-142 branch September 4, 2026 16:02
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