Remote shells over SSH¶
Over ssh, every key you type goes to the server and back before it shows. On a slow connection, typing lags, the cursor jumps, and a completion menu redraws one round trip at a time.
luish --ssh HOST splits the shell in two. The line editor runs on your
machine, and the shell that runs your commands runs on HOST. Typing, moving in
the line, searching the history, autosuggestions and the completion menu are
instant, however slow the connection. Commands, Tab completion, the prompt and
everything your startup files set up on HOST still come from HOST.
luish --ssh myserver # the first time, copies luish to myserver
luish --ssh -p 2222 me@myserver # ssh's options go before the host
luish uses ssh as it is, with your keys, your agent and ~/.ssh/config. It
needs no daemon on HOST and no extra ports, and luish needn’t be installed
there: luish --ssh copies itself (see below).
Only your machine needs a terminal; both ends must run Linux.
What runs where¶
On your machine |
On HOST |
|---|---|
Typing and moving in the line, key bindings |
Commands, and their jobs |
The history’s up-arrow, searches and autosuggestions |
The history file, |
Drawing the completion menu |
Finding what Tab completes (with HOST’s plugins and completers) |
Syntax highlighting |
The files and commands that highlighting looks up |
The prompt, startup files, |
So the session is configured by HOST’s config.toml, rc.d and plugins, not
by yours: its prompt, colours, key bindings (emacs or vi), aliases and
completion are HOST’s. Your machine only lends the editor.
Each command line costs one round trip, when you press Enter, and so does each
Tab. Highlighting asks HOST whether the words that look like paths are files,
and waits for the answer at most 20 ms; an answer that comes later shows the
next time the line is redrawn (at the next key). Commands are highlighted
without waiting: HOST sends the names of the commands on its PATH with each
prompt (only when they change).
While a command runs¶
Between command lines, your terminal is connected to a terminal (a pty) on
HOST, as with ssh -t: keys go to the command as you type them, and its output
comes back as it is printed. So full-screen programs (vim, less, top, tmux)
work as they do over ssh, and so do job control, ^C, ^Z and window size
changes.
Keys you type before the next prompt shows are not lost, nor read by the command that just finished: they start the next line. A line typed ahead with its Enter runs as soon as the prompt comes.
The output of background jobs shows as it comes, also while you edit a line.
Escapes¶
ssh’s escapes work as they do in ssh: the escape character, ~, at the start
of a line, then a key.
Escape |
What it does |
|---|---|
|
Closes the connection (even one that hangs), and luish exits with status 255, as ssh does |
|
Suspends luish, back to the shell you started it from ( |
|
Lists the escapes |
|
Types a single |
While a command runs, “the start of a line” is after Enter, as in ssh. At
the prompt, it is an empty line: ~ goes into the line as any other key, and
the key after it makes the escape. Any other key after ~ is typed as usual
(so ~/bin and ~user need nothing special), and so are ssh’s other escapes
(~C, ~#, ~R, ~B, ~V, ~v and ~&), which need ssh itself.
On a terminal where luish doesn’t edit lines (TERM=dumb), type the escape
as a line of its own, ended with Enter.
-o ssh.escape_char=C makes another printable character the escape
character, and -o ssh.escape_char=none turns the escapes off:
luish --ssh -o ssh.escape_char=% myserver
ssh’s own escape character (its -e, or EscapeChar in ~/.ssh/config)
does nothing here: ssh never sees your keys, only luish’s messages.
The shell on HOST¶
luish --ssh starts an interactive shell on HOST, but not a login shell (plain
ssh HOST starts a login shell). So it reads config.toml, rc.d, $ENV
and luishrc on HOST, but not login.d, /etc/profile or ~/.profile.
Settings that only a login shell makes, such as a PATH set in ~/.profile,
are missing: set them in config.toml instead (its [env] and [path]
tables apply to every interactive shell, see
Settings in config.toml), or start a login shell with --remote
(see below).
The variables that describe your terminal (TERM, COLORTERM, TERM_PROGRAM
and TERM_PROGRAM_VERSION) are passed to HOST, replacing its own (as ssh does
with TERM), so that programs there know what your terminal can do. So is the
locale (LANG, LANGUAGE and LC_*), but only where HOST hasn’t set it,
because HOST may not have your locale.
The session ends when the shell on HOST exits (with exit, ^D, or a command
it execed), and luish --ssh exits with its status. If the connection is
lost, luish says so and exits with status 255, as ssh does.
luish on the server¶
The first time you connect to a host, luish --ssh copies itself there, in the
same ssh connection (so you log in once):
$ luish --ssh myserver
luish: copying luish 0.7.0 to the server (5.6 MB)
The copy is kept on HOST as ~/.cache/luish/binaries/luish-VERSION-BUILD
(BUILD being the git commit it was built from), or under $XDG_CACHE_HOME if
the commands that ssh runs have it set. Later connections run that copy. Each
version (and build) of luish thus runs its own copy, whatever else is
installed on HOST, so both ends always speak the same protocol. When a new copy
is made, the copies more than 30 days old are removed.
The copy isn’t made, and the luish on HOST’s PATH is run instead, when:
HOST is another system or architecture (as
uname -smsays), such as anaarch64server for anx86_64laptop;the copy can’t run there, as with a build linked with a newer C library than HOST’s. The release builds run on any Linux from 2014 on (see Installation).
luish says why on standard error. The PATH that ssh gives commands is often
shorter than a login shell’s (without ~/.local/bin, for example), so if
luish is installed somewhere else, name it with --luish-path:
luish --ssh --luish-path='~/.local/bin/luish' myserver
luish --ssh --luish-path=luish myserver # the luish on the PATH, never a copy
--luish-path=PROGRAM (as rsync’s --rsync-path) runs PROGRAM on HOST and
copies nothing. HOST’s own shell runs it, so it may start with ~/. PROGRAM
must be the same version of luish as yours.
Two options control the copy (-o ssh.escape_char is above):
luish --ssh -o ssh.no_auto_copy myserver # never copy luish
luish --ssh --copy-luish myserver # copy luish, even if HOST has the copy
-o ssh.no_auto_copy runs the copy if HOST has it, and otherwise the luish
on HOST’s PATH, without copying anything (as on a slow or metered link).
Its name is matched as setopt’s: case and _ don’t matter
(-o ssh.NoAutoCopy), and -o ssh.auto_copy turns it back off.
--copy-luish copies luish to HOST again, replacing the copy there (if it was
damaged, say), and overrides -o ssh.no_auto_copy.
Other transports¶
luish --ssh [OPTION...] HOST is a short way to write
luish --remote ssh -T [OPTION...] HOST luish --serve
plus the copy.
To reach HOST with another command than ssh -T, name it with -e COMMAND
(or --rsh=COMMAND, as in rsync, or --ssh-command=COMMAND, as git’s
core.sshCommand). COMMAND is split into words at blanks, with
quotes as in the shell, and is run with ssh’s options (the rest of the
--ssh arguments) and HOST after it, then the command for HOST; it replaces
ssh -T as a whole:
luish --ssh -e 'ssh -T -F ~/.ssh/work-config' myserver
luish --ssh -e 'docker exec -i' mycontainer # anything that runs a command
ssh’s own -e (its escape character) isn’t available, as it would do
nothing: ssh’s input is a pipe to luish, not your terminal. luish has the
escapes instead (above).
luish --remote COMMAND... runs COMMAND with its standard input
and output connected to luish, and COMMAND must start luish --serve at the
other end. luish --serve takes the usual options after --serve, such as
-l for a login shell:
luish --remote ssh -T myserver luish --serve -l # a login shell on myserver
luish --remote luish --serve # both ends here, to try it out
With --remote, nothing is copied: the luish at the other end must be the
same version as yours, or the client stops with
luish: the server's luish (0.6.0) speaks another version of the protocol than this one (0.7.0)
Making it the way you log in¶
An alias in config.toml saves typing the option:
[alias]
myserver = "luish --ssh myserver"
or, for every host, a function in rc.d:
sshl() { luish --ssh "$@"; }
Messages¶
What HOST’s startup files print before luish starts (such as a message of the
day from ~/.bashrc) is shown on standard error. luish’s own messages are:
Message |
Meaning |
|---|---|
|
The first connection to HOST with this build; later ones reuse the copy |
|
HOST is another system or architecture, so the |
|
The copy didn’t run on HOST (an older C library), so the |
|
The copy couldn’t be written on HOST (a full or read-only disk) |
|
With |
|
ssh exited, or the network went away |
|
You typed |
|
|
|
Only |
|
|
|
|
Limitations¶
A dropped connection ends the session, as with ssh: unlike mosh, luish doesn’t reconnect. Run tmux or screen on HOST to keep work going across disconnections.
^Cdoesn’t interrupt a Tab that waits for HOST, and~.doesn’t close the connection then: the completion comes when HOST answers.Output while you edit: what background jobs print while you edit a line is written as it comes, and the line isn’t redrawn below it.
Highlighting of paths may lag on a slow connection: a late answer shows at the next key, not as soon as it comes.
Both ends must run the same version.
--sshmakes sure of it unless it is given--luish-path; a mismatch is an error, not a fallback.Linux only, on both ends, and the copy needs the same system and architecture (
uname -sm) on both.