Skip to main content

Prerequisites

To get the most out of this guide, you will need to: hsh is a small companion CLI that wraps ssh and kubectl so they work against resources behind your Hoop gateway without any workflow changes. You keep typing the commands you already know; hsh handles authentication, finds the right Hoop resource role, and routes the call through the gateway. For databases and other TCP services, hsh also ships the Hoop Tunnel — a background daemon that makes every connection reachable by name, e.g. psql -h pg-prod.hoop.
hsh is separate from the hoop CLI. They can be installed side-by-side and don’t conflict. Use hoop for administrative and automation tasks (managing resource roles, running hoop exec, configuring policies). Use hsh for day-to-day terminal work where you want ssh/kubectl to “just work”.

Installation

Through homebrew:
To upgrade it:
The formula also installs the hsh-tunneld daemon for the Hoop Tunnel. To use the tunnel, register the daemon as a system service once: sudo hsh-tunneld install — see the tunnel installation steps.

Initial setup

1

Point hsh at your gateway

The configuration is stored in ~/.hsh/config.json. Use hsh config list to view it.
2

Authenticate

hsh automatically detects whether your gateway uses an OIDC identity provider or local-auth (email + password) and prompts accordingly:
  • OIDC (most managed gateways) \u2014 your browser opens to your identity provider; once you sign in, the JWT is written to ~/.hsh/auth.json (mode 0600).
  • Local-auth (self-hosted with AUTH_METHOD=local) \u2014 hsh prompts for email and password directly in the terminal; no browser involved.
Run hsh status at any time to see which gateway you’re pointed at and whether your token is still valid.If the Hoop Tunnel daemon is installed and reachable, hsh login also attempts to authenticate it in the same step. If that step fails, hsh prints a warning — run hsh tunnel login to authenticate the daemon explicitly, or skip it entirely with --no-tunnel.
3

Activate the shell plugins

Add a single line to your shell startup file. This is what tells your shell to route ssh and kubectl through hsh:
Restart your shell (or source the file you just edited). From now on, plain ssh and kubectl commands are intercepted by hsh.
hsh only intercepts commands that match a Hoop resource role. Anything else falls straight through to your native ssh/kubectl binary, untouched.

SSH

Once the shell plugin is active, the workflow for connecting to a Hoop-managed host is just:
hsh recognizes production-db as a Hoop resource role, mints a short-lived access token, and feeds it to ssh via the standard SSH_ASKPASS mechanism. You don’t see a token, you don’t paste anything \u2014 the password prompt is answered for you.
Automatic token injection requires OpenSSH 8.4 or newer (which honors SSH_ASKPASS_REQUIRE=force). On older clients, hsh falls back to the legacy flow: it prints the token in a box and you paste it at the password prompt. To disable injection on a single invocation, run HSH_SSH_ASKPASS=0 ssh <host>.
If the host doesn’t match any Hoop resource role (e.g. a personal VPS, your CI runner, GitHub), ssh runs unchanged \u2014 hsh doesn’t get in the way.

What hsh does behind the scenes

  1. Parses your ssh arguments (user@host, -p, -l, -i, etc.).
  2. Verifies your ~/.hsh/auth.json is still valid; if not, exits with a clear “run hsh login” message.
  3. Searches your Hoop resource roles for one matching the host you typed.
  4. Creates a session and asks the gateway for a one-time access token.
  5. Writes a small SSH askpass shim, runs ssh with SSH_ASKPASS pointing at it, and lets ssh authenticate normally.

Kubernetes

For kubectl, the integration is even more transparent:
hsh looks at your current kubectl context (or --context=<name>), finds the matching Hoop resource role, requests a short-lived proxy URL + token, and merges them into your kubeconfig before running kubectl. The original kubeconfig is restored when the command exits. If you want a long-lived kubeconfig you can use from any tool (kubens, k9s, lens, etc.), generate one explicitly:
hsh kubeconfig prints a complete kubeconfig on stdout \u2014 every status message goes to stderr, so it’s safe to redirect.

Useful commands

Troubleshooting

When something doesn’t behave the way you expect \u2014 hsh falls through to native ssh/kubectl when you wanted it routed through Hoop, or vice versa \u2014 set HSH_DEBUG to see exactly what hsh decided:
Output goes to stderr only, so it never pollutes the program’s stdout. Common things to look for:
  • match: level=null \u2014 the host or context didn’t match any Hoop resource role at any matching level. hsh runs the native command unchanged.
  • api: fetch failed reason=timeout \u2014 the gateway is unreachable. hsh falls open to native ssh/kubectl after \u22483s. Run hsh status to confirm the API URL.
  • cache: hit expire_at=... \u2014 hsh reused a cached credential. Run hsh logout (clears every cache file) to force a fresh issue.
HSH_DEBUG accepts 1, true, yes, or on (case-insensitive). Tokens and passwords are filtered out of debug output by design. For deeper details, see the hsh README and the troubleshooting guide in the source repository.