hi <name> resolves one name through a ladder - an ssh host first, then four
container backends - and lands you in the same styled session either way. This
file is every level of support that session has: what hi can reach, what it does
once it gets to a given OS, and which shell it hands you when it lands.
The reasoning for what is not here - every runtime, shell, channel and feature weighed and left off, each with the argument against it - is UNSUPPORTED.md. A thing missing from this page is not an oversight; it has a row over there.
Legend: ✅ exercised by a suite on every run · 🟡 expected to work, nobody has proven it · ⚠️ works, reduced · ❌ not supported, see UNSUPPORTED.md.
A backend is not one function. Adding one touches seven places, and the last two are the ones that decide most of the verdicts below:
_HI_BACKENDS (hi.sh) - <name>|<what a target resolves
as>|<liveness probe>|<predicate>. One list, walked by the dispatch and by
scripts/doctor.sh, so a row reaches hi --doctor at the same moment it
reaches hi._hi_is_docker_container: one command -v guard,
one [ "$(<query>)" = <literal> ], all stderr swallowed._hi_container_cmds, filling probe/cp/attach - ask a
question, stream a file in, hand over a session. Everything past that point
is backend-agnostic.run_lister case and the usage line in common/targets.sh,
written in that file’s standalone-POSIX dialect: it is the only file all
three completions read and the only one fish can run.common/header.sh, whose _hi_probe_launch
hardcodes the backends on purpose - hi.sh is never sourced in a session,
and sharing the list would cost the ssh payload bytes for something that
changes about once a year. tests/hi/parse_test.sh greps the two against
each other so the copy cannot drift.tests/targets/, registered in test_runner.sh’s
_HI_TESTS table. A suite that can only ever skip is worth less than no
suite.Then the part that is paid by everyone else. _hi_resolve_backend runs
every predicate, in parallel, on every hi <target>; common/targets.sh
probes every backend on every TAB after hi (GLOSSARY: HI.26). Both costs
land on machines that have none of the runtime in question. The command -v
guard inside each predicate short-circuits before the CLI is executed, so the
marginal cost of a row is a fork rather than a daemon round-trip - but it is
still a fork, five times per keystroke instead of four.
That is the test a candidate has to pass: a row earns a yes by being something people actually sit in, not by being reachable.
| target | what a name resolves as | proven by |
|---|---|---|
| ssh host ✅ | a Host entry in ~/.ssh/config, or any name ssh will take |
tests/targets/ssh_test.sh, plus ssh_disconnect_test.sh (cleanup on an abrupt drop) and ssh_relay_test.sh |
| docker ✅ | a running container | tests/targets/docker_test.sh - six cases across bash, zsh, fish, dash and busybox sh |
| podman ✅ | a running container | tests/targets/podman_test.sh, the same six against podman’s own image store |
| nomad ✅ | a running allocation, or alloc/task |
tests/targets/nomad_test.sh, against a real nomad agent -dev |
| kubernetes ✅ | a running pod, or pod/container |
tests/targets/kube_test.sh, against a real kind cluster |
ssh is checked first and short-circuits the roster entirely, which is why a name that is both an ssh host and a container name resolves as the ssh host.
These come up as requests, and every one of them already works. They need collecting, not deciding.
| target | why no row is needed |
|---|---|
| distrobox and toolbx | they are podman (or docker) containers, so the existing rows reach them by name today. The wrinkle worth knowing is not the transport: these share your real $HOME, so hi’s rc grafts land in the same files your host shells read. That is what GLOSSARY HI.24’s tree-exists guard exists for, and ALTERNATIVES.md has the full account |
| remote docker contexts | the docker row shells out to whatever docker is on $PATH, so docker context use and DOCKER_HOST are transparent to hi - the daemon being on another machine changes nothing it looks at |
AWS SSM start-session, gcloud compute ssh, fly ssh console, Azure Bastion |
anything that terminates in an OpenSSH connection is a Host entry away from being an ordinary ssh target, usually a ProxyCommand one. The constraint to know is that hi’s ssh path multiplexes two calls over a single ControlMaster (_hi_ctl_open), which is exactly why mosh and Eternal Terminal cannot be ridden - but a ProxyCommand is still OpenSSH, so it can |
| devcontainers / VS Code dev containers | they are docker containers, on exactly the distrobox precedent, so the docker row finds them by name today. The name is the one docker gives them (vsc-<project>-<hash>-uid), not the one in devcontainer.json - which is the same ergonomic wrinkle the docker compose row below has, and the same answer |
docker compose services |
a compose service is a docker container, so hi myproject-web-1 works now. What compose adds is a nicer name for it, and docker compose ps -q <service> is the translation. That is a lister question, not a backend one: it would cost common/targets.sh an extra docker compose ls on every TAB to offer names that are one docker ps away from the ones already offered, so it stays unbuilt until somebody asks |
multipass, Vagrant, Codespaces |
all three end in a real OpenSSH connection, so they are the AWS SSM row again: a Host entry away. Vagrant and Codespaces will write it for you - vagrant ssh-config and gh codespace ssh --config both emit a paste-ready block. Multipass is the manual one: take the IP from multipass info and point IdentityFile at multipassd’s own key |
Can hi land a session there at all?
| target OS | result | proven by |
|---|---|---|
| Linux, glibc (Debian/Ubuntu/Fedora/Arch…) | ✅ full session | tests/targets/ssh_test.sh, on Debian bookworm |
| Linux, musl + busybox (Alpine…) | ✅ full session with bash installed, ⚠️ aliases-only without |
ssh_test.sh, on Alpine 3.24 |
| macOS | 🟡 full session — bash 3.2 is what it ships, and the suite runs a real bash 3.2 target; the client half (BSD sed/mktemp/base64) is unit-tested only |
ssh_test.sh bash-3.2 case; .github/workflows/macos-e2e.yml is written but has never run |
| WSL | 🟡 it is Linux, and the .deb installs into it unchanged |
— |
Windows, with Git Bash/Cygwin/MSYS2 on PATH |
🟡 full session, same code path as any ssh host | .github/workflows/windows-client.yml (client side) and windows-e2e.yml (target side), both written, neither run yet |
Windows, stock OpenSSH (cmd.exe/PowerShell) |
⚠️ plain PowerShell session, no hi styling — the fallback is deliberate, not a failure | windows-e2e.yml, the target-side half above, never run |
| *BSD, Solaris/illumos | 🟡 nothing in hi is Linux-specific past the header’s /proc probes, which degrade to ? |
— |
What hi hands you once it is on the target.
| session shell | result | note |
|---|---|---|
bash ≥ 3.2 |
✅ full: header, prompt, git status, aliases, editor configs | 3.2 is the floor because macOS still ships it |
zsh |
✅ full | shells/zsh.zsh |
fish |
✅ full | shells/config.fish |
sh/dash/ash (no bash on the target) |
⚠️ aliases and a colored user@host prompt, with a warning saying so |
no header and no git segment - those need bash |
nushell, elvish, xonsh, ion, oil/osh |
❌ decided against, not pending | see Shells hi does not style. You still get a session — hi lands you in the best of $_HI_SHELL_TREE the target has |
| PowerShell | ❌ | bash-only by design |
If you use a shell framework, hi lands you in your own login shell, so it
loads normally — that is what _HI_SHELL_PREFERENCE’s default (login, then
the styled head of $_HI_SHELL_TREE: fish zsh bash) means.
tests/targets/framework_test.sh tests oh-my-zsh, powerlevel10k, starship and
bash-it against hi, each asserting the session comes up with no shell errors
and that hi neither changed zsh’s array base under them nor dropped their
PROMPT_COMMAND.
Both tables above assume hi can reach the target in the first place, which
is what The five that ship and Already
covered answer. Everything weighed and left off
that roster - LXC/Incus, systemd-nspawn, WSL, nerdctl, jails, zones and the
rest - is in UNSUPPORTED.md.