An honest look at what else solves this problem, where hi.d is genuinely different, and where another tool is the better one. Written for someone deciding whether to use hi.d, not to sell it.
You have a shell you have spent years tuning, and you spend your day on
machines that are not yours: production boxes, a colleague’s server, a jump
host, a container that will not exist in an hour. There you get sh-4.4$ and
no ll.
There are two families of answer.
Install your config there. Dotfile managers — chezmoi, yadm, GNU Stow,
dotbot, rcm, homeshick — or config management like Ansible. These are excellent, and
hi.d does not compete with them: they assume the machine is yours, that you’ll
be back, and that leaving files behind is fine. That fails for a shared
production host, a box you touch once, or a container. The line blurs at the
edge — chezmoi’s --one-shot applies dotfiles to an ephemeral machine then
deletes chezmoi, and VS Code devcontainers can clone a dotfiles repo into every
container — but both need the target to reach your repo over the network,
both leave the files behind, and neither does anything per-session. hi.d pushes
from the client, needs no network on the target, and cleans up.
Carry your config with you, per session. The tool ships your config over the connection, uses it for that session, and gets out. That is the family hi.d is in, and everything below is a member of it.
A third thing that looks similar but is not: terminal emulators that help
with ssh, like kitty’s ssh kitten and wezterm’s ssh domains (which go
further, with an optional persistent wezterm-mux-server on the remote). Those
solve the adjacent and very real terminfo/shell-integration problem — kitty’s
copies the xterm-kitty terminfo database, enables shell integration, and can
copy files you list. If your pain is “backspace is broken over ssh”, that is
the fix, and it composes with hi.d rather than competing. hi.d handles the
terminfo half itself (_hi_remote_preamble probes the target’s terminfo tree,
falling back to xterm-256color) precisely so it doesn’t depend on your
terminal.
| hi.d | sshrc | xxh | kyrat | sshdot | |
|---|---|---|---|---|---|
| Written in | POSIX/bash shell | shell | Python | bash | shell |
| Client needs | bash 3.2+, base64 |
bash, ssh | a Python install (pip/pipx/conda) or the portable binary | bash ≥ 4.0, GNU coreutils |
shell, ssh |
| Target needs | base64; bash for the full session |
shell | Linux x86_64 only | shell | shell |
| Target OS | Linux (glibc + musl), macOS/BSD, Windows via WSL/Git Bash | broad | Linux x86_64 | Linux, macOS | broad |
| Installs on target | nothing | nothing | a portable shell + plugins under ~/.xxh |
nothing | nothing |
| Cleans up on exit | yes, automatically | leaves /tmp dir |
no — delete ~/.xxh yourself |
yes, automatically | leaves files |
| Size ceiling | ~40KB gzipped, enforced by CI | ~64KB and the server may block you | large — it uploads whole shells | small | none (that is its point) |
| Non-ssh targets | docker, podman, nomad, k8s | no | no | no | no |
| Can give you a shell the host lacks | no | no | yes | no | no |
| Maturity | pre-1.0, not yet published to any channel | original deleted from GitHub; cdown’s fork is the maintained line, argv ceiling inherited | mature, active | quiet | quiet |
hi.d is a fork of sshrc (via cdown’s and danrabinowitz’s lines), and the core idea is unchanged: tar your config, base64 it, hand it to the login shell, source it on the far side. Russell Stewart’s original repository was deleted from GitHub outright — not archived — so the links here point at cdown’s fork, the self-described maintained continuation, which carries the design (64KB argv ceiling included) unchanged.
Where sshrc still wins: it is smaller and simpler, and simplicity is a real
feature in something that runs on every host you touch. If you just want your
.bashrc and .vimrc over there, sshrc does it in a fraction of the code, and
you can read all of it in one sitting.
Where hi.d went further, and why:
ARG_MAX, and sshrc’s own README
warns that past ~64KB “the server may block your sshrc attempts”. hi.d writes
it over stdin of the first of two calls multiplexed on one ssh
connection, removing that ceiling as a design constraint rather than
documenting it as a caveat./tmp and leaves it. hi.d’s load.sh traps
on exit, strips its lines back out of the host’s rc files and removes the
tree, so a machine you visited looks untouched.xxh’s pitch is different and more ambitious: it uploads a portable build of the shell itself, so you can use fish or zsh on a host that has neither.
Where xxh wins outright: that capability. hi.d cannot give you a shell the
target lacks — its no-bash ladder (fish > zsh > mksh > ksh > dash > ash > sh) picks the
best of what is installed and says so. If you need your shell on a
locked-down box that ships only sh, xxh is the answer and hi.d is not; its
plugin model is also more principled than copying dotfiles blind.
Where hi.d wins:
base64, and its suite runs
real Debian, Alpine/musl and bash-3.2 targets every time.~/.xxh stays until you
delete it. hi.d removes itself when the session ends.kyrat is the nearest neighbour: a bash ssh wrapper, base64+gzip through the
command line, cleanup on exit, KYRAT_SHELL to pick bash/zsh/sh. If the table
above looks like a description of hi.d, that is because it nearly is.
The differences are narrow and concrete. kyrat requires bash ≥ 4.0, ruling
out macOS’s system bash — the exact constraint hi.d contorts itself to respect
(no mapfile, no associative arrays, no namerefs, enforced by a lint grep and
a real bash-3.2 container in CI). kyrat spawns bash, zsh or sh; hi.d styles
bash, zsh and fish, and gives the POSIX tiers a colored prompt and —
for ksh/mksh — a live git segment. And kyrat is ssh-only.
sshdot is sshrc without the size limit, achieved by not squeezing through the command line. Narrower in scope than hi.d; the honest summary is that it solves the one problem it names.
homeshick is a git dotfiles synchronizer written in bash, and it is the tool
whose constraints look most like hi.d’s: “provided that at least Bash 3 and
Git 1.5 are available you can use homeshick” — no Ruby, no Python, no root, no
package manager. hi.d holds the same bash 3.2 floor for the same reason. That
is where the resemblance stops, because it answers the other half of the
problem. You homeshick clone a repo — a castle — into
~/.homesick/repos/, and homeshick link symlinks that castle’s home/
directory into $HOME; a line in your rc file sources homeshick.sh (or
.csh/.fish), and track/pull/refresh keep the castle and the machine in
step. Several castles compose, which is how people run oh-my-zsh beside their
own config.
So it is not a competitor, and it is not in the table above. It is the tool for
a machine you own and will come back to: the checkout stays, the symlinks
stay, and the next login is already configured with no client involved. hi.d is
for the machine you will not come back to — it pushes from the client, needs no
git and no network on the target, and takes the tree away when the session
ends. The failure modes are mirror images: homeshick on a production box you
touch once leaves a ~/.homesick and an edited rc file behind for the next
person; hi.d on your own laptop re-sends a payload every session to give you
what a symlink would have given you for free.
Where homeshick wins outright: the machine is yours; you want your config
there when you arrive rather than when hi says so; you want your dotfiles under
plain git with plain symlinks and nothing clever in between. The two compose,
too — install hi.d permanently on that box (scripts/install.sh) and let
homeshick manage everything else.
None of these are alternatives — they touch the same session from a different side. Listed because people conflate them with the family above, or because the composition has a wrinkle worth knowing.
hi cannot ride them.
The composition that works: install hi.d permanently on the target
(scripts/install.sh), then mosh in — and note hi_copy over mosh needs
mosh ≥ 1.4, its first release with OSC 52.~/.warp* plus a hook
line in the remote’s rc files. It ships Warp’s features, not your config.
The two coexist — hi.d touches only its own marker-delimited lines.Ctrl-R hi’s session lands you
at, the framework e2e suite boots a real atuin (plus fzf, zoxide, direnv,
mise) target and asserts its hooks survive hi’s session.~/.config/hi.d/. Keep it in your dotfile manager and the two compose
cleanly: chezmoi versions it, hi ships it to every target, per-session.$HOME, so hi into one
grafts into the same rc files your host shells read. The exit trap strips
them as everywhere else; an uncleanly killed session is the one case where
graft lines outlive their tree in a file you care about, which is why every
graft is wrapped in a tree-exists guard that stands down silently
(load.sh’s configure_files, proven by the load suite).Two things, and it is worth being precise because the rest is degree, not kind.
1. It is not an ssh tool. Every alternative above is an ssh wrapper. hi
resolves a name through a ladder — ssh host, docker container, podman, nomad
allocation, kubernetes pod — and gives the same session on whichever it
finds. hi web-1 is your shell whether web-1 is a Host in ~/.ssh/config
or a pod in the namespace your kubectl points at. For anyone moving between a
server and the containers on it that is the feature, and nothing else in this
space does it.
2. It degrades in stated tiers rather than failing or lying. The compatibility tables answer three questions — can hi land a session here, what must your login shell survive, what do you end up in — and mark every cell proven-by-a-suite, expected, reduced, or unsupported. A target with no bash gets aliases, a colored prompt, and a warning saying so; a Windows OpenSSH host with no POSIX shell gets a plain PowerShell session rather than an error. That stance is why the honest cells (🟡 “nobody has proven it”) are in the table at all.
Secondary but real: a per-user config overlay (settings, colors, packages,
aliases) that rides along without dirtying the tree, hi --doctor for when
something is slow, --tmux so a dropped connection detaches instead of losing
work, and detecting a permanent ~/hi.d on the target to use in place.
$_HI_SHELL_TREE the target has.