> ## Documentation Index
> Fetch the complete documentation index at: https://hoopdev-docs-control-plane-owns-listeners.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hoop Tunnel

> Reach any Hoop-managed database or TCP service by name — psql -h pg-prod.hoop — with authentication, review, and audit still enforced by the gateway.

Hoop Tunnel makes your Hoop connections reachable as local hostnames
under the `.hoop` domain. Once it's running, connecting to a production
database is just:

```bash theme={null}
PGPASSWORD=noop psql -h pg-prod.hoop -U noop
mysql -h mysql-staging.hoop -u noop -pnoop
```

No proxies to start, no ports to remember, no tokens to paste, and no
database credentials to look up — every connection uses the same fixed
`noop`/`noop` placeholder ([see below](#credentials)). Any TCP client —
`psql`, `mysql`, DBeaver, DataGrip, your application code — works
unmodified.

<Note>
  Everything the gateway already enforces still applies. Each TCP flow
  through the tunnel is an ordinary Hoop client session: authentication,
  access control, session recording, data masking, and webhooks all work
  exactly as they do with `hoop connect`.
</Note>

## How it works

The tunnel is powered by `hsh-tunneld`, a small daemon bundled with the
[`hsh` CLI](/clients/hsh). It creates a virtual network interface on
your machine, answers DNS for `*.hoop` names, and forwards each TCP
connection to the Hoop gateway over gRPC — the same transport the
`hoop` CLI uses.

```
  your app                   hsh-tunneld                    hoop gateway
  ─────────                  ────────────                   ────────────
  psql -h pg-prod.hoop  ──▶  virtual interface + DNS
                             each TCP flow        ──gRPC──▶ ordinary client
                                                            session
                                                                 │
                                                                 ▼
                                                            hoop agent → database
```

Every connection gets a pair of stable virtual addresses (IPv6 and
IPv4 — the resolver answers both `AAAA` and `A`), and host DNS routing
for `.hoop` is configured automatically — via `systemd-resolved` on
Linux and `/etc/resolver` on macOS.

### Which connections are tunnelable?

TCP-based connection types — **PostgreSQL, MySQL, SQL Server, MongoDB,
Oracle, and raw TCP** — plus
[**HTTP proxy**](/quickstart/web-applications/http-proxy) connections.

HTTP proxy connections are served on port **80** only: your client
speaks plain HTTP to the tunnel (`curl http://api-prod.hoop/path`) and
the Hoop agent terminates TLS to the connection's upstream. Because the
traffic crosses the agent in cleartext, guardrails and data masking
still apply. `https://<name>.hoop` can never work — the tunnel has no
certificate for `*.hoop` — so connections to port 443 are rejected.

Everything else is intentionally excluded from the tunnel's resolver —
SSH, Kubernetes, RDP, SSM, and command-line connections all need
protocol-specific clients. Use the
[`hsh` shell plugins](/clients/hsh) for SSH and Kubernetes, and the
`hoop` CLI or the Web App for the rest.

Oracle connections appear only when native Oracle access is enabled for
your organization; otherwise the agent refuses the session, so the
tunnel hides them rather than offering a resource that cannot connect.

## Credentials

Every database reached through the tunnel uses the same fixed
credentials: **`noop` / `noop`**.

These are not your database's credentials, and there is nothing to
rotate or copy from the Web App. The Hoop agent completes your client's
login locally, then authenticates to the real database itself using the
credentials stored on the connection. Your client never sees them.

`hsh tunnel ls` prints a ready-to-paste command per connection, so you
normally don't type these by hand:

```
Tunneled connections

  pg-prod.hoop        postgres  port  5432  fd5a:1b2c:3d4e::a1b2:c3d4
                      PGPASSWORD=noop psql -h pg-prod.hoop -U noop
  mysql-staging.hoop  mysql     port  3306  fd5a:1b2c:3d4e::e5f6:0708
                      mysql -h mysql-staging.hoop -u noop -pnoop

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

For a GUI client (DBeaver, DataGrip, TablePlus), enter the `.hoop`
hostname, the protocol's normal port, and `noop` for both username and
password.

Two connection types are exceptions and show no credentials:

* **Raw TCP** — Hoop cannot parse the protocol, so bytes are relayed
  untouched and your client authenticates to the upstream itself, with
  whatever credentials that service expects.
* **HTTP proxy** — authorization is injected by the agent as HTTP
  headers; there is nothing for you to present.

<Warning>
  **`noop`/`noop` works only through a local Hoop listener** — the
  tunnel, or `hoop connect`. It grants no access on its own: what
  authorizes the session is your `hsh login`, and every flow is still a
  normal gateway session with the usual access control and audit.

  It is **not** the credential for native database access, where you
  point a client straight at the gateway with no local Hoop process
  running. That path issues a per-user secret key from the Web App —
  it expires, it can be revoked, and it rejects `noop`.
</Warning>

<Note>
  Because any local process that can reach a `.hoop` name uses your
  tunnel session, treat a machine running the tunnel as belonging to
  one person. On a shared host, other local accounts would inherit your
  access to every tunneled resource.
</Note>

## Installation

The daemon ships inside the `hsh` release archives from [GitHub
Releases](https://github.com/hoophq/hsh/releases/latest) — there is
nothing separate to download. Registration as a system service makes
the tunnel start at boot and stay up in the background.

<Tabs>
  <Tab title="Linux">
    ```bash theme={null}
    tar xzf hsh-linux-x64.tar.gz            # or hsh-linux-arm64.tar.gz
    cd hsh-*/
    sudo install -m 0755 hsh /usr/local/bin/   # the CLI
    ./install.sh                                # registers the systemd service
    ```

    The install script registers `hsh-tunneld` as a systemd unit and
    adds you to the `hsh` group, which gates access to the daemon's
    control socket. Log out and back in (or run `newgrp hsh`) for the
    group membership to take effect.
  </Tab>

  <Tab title="MacOS">
    ```bash theme={null}
    tar xzf hsh-darwin-arm64.tar.gz         # or hsh-darwin-x64.tar.gz
    cd hsh-*/
    sudo install -m 0755 hsh /usr/local/bin/   # the CLI
    sudo ./hsh-tunneld install                 # registers the LaunchDaemon
    ```

    `hsh-tunneld install` registers the daemon as a system-wide
    LaunchDaemon and adds you to the `hsh` group, which gates access to
    the daemon's control socket. Open a new terminal session for the
    group membership to take effect.

    <Note>
      If you installed `hsh` via Homebrew, both binaries are already on
      your PATH — skip the download and just register the service:
      `sudo hsh-tunneld install`. The manual step is needed because the
      daemon runs as a system-wide LaunchDaemon (root), which
      `brew services` doesn't manage.
    </Note>
  </Tab>
</Tabs>

Windows support is not yet available.

<Note>
  The daemon requires root: it opens a TUN device and configures host
  DNS routing. Installation is the only step that needs `sudo` — all
  day-to-day `hsh tunnel` commands are unprivileged.
</Note>

## Getting started

<Steps>
  <Step title="Authenticate">
    ```bash theme={null}
    hsh login
    ```

    When the daemon is installed and reachable, `hsh login` also
    authenticates it in the same step, and the tunnel comes up
    automatically as soon as the daemon holds a valid token. If the
    daemon leg fails, `hsh login` prints a warning — run
    `hsh tunnel login` to authenticate the daemon explicitly. To skip
    the daemon entirely, use `hsh login --no-tunnel`.

    <Tip>
      If the daemon points at the wrong gateway, set it first:
      `hsh tunnel config set api-url https://your-company.hoop.dev`.
    </Tip>
  </Step>

  <Step title="See what's reachable">
    ```bash theme={null}
    hsh tunnel connections
    ```

    ```
    Tunneled connections

      pg-prod.hoop        postgres  port  5432  fd5a:1b2c:3d4e::a1b2:c3d4
                          PGPASSWORD=noop psql -h pg-prod.hoop -U noop
      mysql-staging.hoop  mysql     port  3306  fd5a:1b2c:3d4e::e5f6:0708
                          mysql -h mysql-staging.hoop -u noop -pnoop

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

    Each row carries the exact command to connect — copy and paste it.
  </Step>

  <Step title="Connect">
    ```bash theme={null}
    PGPASSWORD=noop psql -h pg-prod.hoop -U noop
    ```

    Use the `.hoop` hostname anywhere a regular hostname works — CLI
    clients, GUI tools like DBeaver or TablePlus, ORMs, and local
    development configs. The username and password are always
    [`noop`/`noop`](#credentials).
  </Step>
</Steps>

## Command reference

| Command                                           | What it does                                                                                          |
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `hsh tunnel status`                               | Show daemon state, auth state, and the last error (if any).                                           |
| `hsh tunnel connections` (alias `ls`)             | List the `*.hoop` hostnames currently served, with credentials and a ready-to-paste command for each. |
| `hsh tunnel refresh`                              | Re-fetch the connection list from the gateway now.                                                    |
| `hsh tunnel up`                                   | Bring the tunnel online (reuses the daemon's existing login).                                         |
| `hsh tunnel down`                                 | Take the tunnel offline without logging out.                                                          |
| `hsh tunnel login [--no-browser] [--timeout <s>]` | Authenticate the daemon (OIDC browser flow or local-auth prompt).                                     |
| `hsh tunnel logout`                               | Clear the daemon's token and tear the tunnel down.                                                    |
| `hsh tunnel config get`                           | Show daemon configuration.                                                                            |
| `hsh tunnel config set <key> <value>`             | Update `api-url`, `grpc-url`, or `log-level`.                                                         |
| `hsh tunnel daemon-path`                          | Print the resolved daemon binary, socket, and token paths (debug helper).                             |

`hsh tunnel up` and `down` pause and resume traffic routing without
touching authentication — useful when you want `*.hoop` traffic stopped
temporarily without re-running a login flow afterwards.

The daemon *process* itself is owned by the operating system's service
manager, not by `hsh`. To start, stop, or inspect the service, use the
platform tools:

```bash theme={null}
# Linux
sudo systemctl status hsh-tunneld
sudo journalctl -u hsh-tunneld -n 50

# Any platform
sudo hsh-tunneld status
```

## Limitations

* **TCP only.** No UDP.
* **HTTP proxy connections are plain HTTP on port 80.** The agent
  terminates TLS to the upstream; `https://<name>.hoop` is not
  supported.
* **`ping` is not a valid connectivity test.** Use
  `nc -z pg-prod.hoop 5432` or the actual database client instead.
* **Connections requiring access review** fail fast on the tunnel —
  there is no per-TCP-connection approval prompt. Run
  `hoop connect <name>` once to request access out-of-band, then use
  the tunnel.
* **Linux and macOS.** Windows is not yet supported.

## Troubleshooting

<AccordionGroup>
  <Accordion title="hsh tunnel status says 'Tunnel daemon socket not found'">
    The daemon isn't installed or isn't running. Install it following
    the [installation steps](#installation) above, or start the
    existing service: `sudo systemctl start hsh-tunneld` on Linux,
    `sudo hsh-tunneld start` on macOS.
  </Accordion>

  <Accordion title="Status shows 'authenticated' but connections fail with 401">
    The daemon holds a token the gateway no longer accepts.
    `hsh tunnel status` detects this and prints the fix — run
    `hsh tunnel login` to re-authenticate the daemon.
  </Accordion>

  <Accordion title="A connection is missing from `hsh tunnel connections`">
    Only TCP-based connection types are tunnelable (see above). Oracle
    additionally requires native Oracle access to be enabled for your
    organization. If neither applies, run `hsh tunnel refresh` to
    re-fetch the list, and confirm your user has access to that
    connection in the Web App.
  </Accordion>

  <Accordion title="The database rejects my login">
    Use `noop` for both the username and the password — not your
    database credentials, and not the secret key issued for native
    database access. `hsh tunnel ls` prints the exact command per
    connection; copy it rather than composing one by hand.

    Raw TCP connections are the exception: those relay your bytes
    untouched, so the upstream service expects its own credentials.
  </Accordion>

  <Accordion title="Names don't resolve (`could not translate host name`)">
    Host DNS routing is configured automatically on bring-up. Check
    `hsh tunnel status` shows the daemon running, then verify the
    resolver wiring: on Linux, `resolvectl status` should list a `~hoop`
    routing domain on the TUN interface; on macOS,
    `cat /etc/resolver/hoop` should exist. Restarting the service
    (`sudo systemctl restart hsh-tunneld`) re-applies the DNS setup.
  </Accordion>

  <Accordion title="Cannot read the control token / permission denied">
    The daemon's IPC control token is readable by members of the `hsh`
    group. The installer adds you to it, but group membership only
    takes effect on a new login session — log out and back in (or run
    `newgrp hsh`).
  </Accordion>
</AccordionGroup>
