omassh

module
v0.14.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: MIT

README

Omassh

A keyboard-driven SSH client for the terminal — Termius's data model, lazygit's interaction model, running on top of real OpenSSH.

Omassh

Status

Hosts and groups with attribute inheritance, embedded sessions, SFTP and port forwarding, shared credentials, and snippets you can run across a whole group, plus theming, rebindable keys, reachability probes, and a text import/export for moving the list between machines or starting from ~/.ssh/config.

Keys

key
j/k, tab, 1/2 move and switch panel
click select a group, host or file, or focus the session pane
scroll move through whichever list is under the pointer
double click in sftp, enter the directory under the pointer
enter connect — ssh takes the whole terminal, exit returns here
/ fuzzy search every host by name, address or tag
n / e / d new / edit / delete, in a dialog over the list
paste fills the focused field; works in search and in a session too
↓ in a form pick what you already use — a host's jump host, group or tags, a group's parent or jump host
space in the Tags picker toggle a tag; the list stays open, ↵ finishes
p probe reachability of the hosts in this group
ctrl+l redraw, if the terminal cleared the screen underneath
t connect in the main pane instead, keeping the host list
s sftp: browse and transfer files
f port forwarding: tunnels that outlive the window
C credentials: a user and a way of proving it, shared by hosts
S snippets: a script to run on a host or a whole group
T pick a theme, previewing as you move
r reload the store from disk
? help, which names the running version

Design

Omassh does not reimplement SSH. Interactive sessions are genuine OpenSSH processes handed the real terminal via tea.Exec, so scrollback, SIGWINCH, mouse reporting and full-screen remote programs behave exactly as they would without Omassh in the picture. ProxyJump, certificates, Match blocks and agent config are honoured because OpenSSH itself is honouring them.

internal/sshx.Build is the single place any ssh invocation is constructed — sessions, probes and the native SFTP dialer all funnel through it, so connection behaviour cannot drift between them.

Omassh stores no secrets and never asks for a passphrase. A host names a key path, which becomes ssh -i; a passphrase-protected key is unlocked the usual way, with ssh-add or AddKeysToAgent yes in your ~/.ssh/config. Nothing here duplicates what ssh-agent already does.

enter hands the whole terminal to ssh. Omassh releases the terminal, the child gets the real stdin and stdout, and exiting drops you back into the list. That path is emulation-free by construction — correct SIGWINCH, your terminal's own scrollback, mouse and every escape sequence — and it is the default for exactly that reason.

Its stderr is the one thing Omassh listens in on, keeping the last few lines so that a session which fails can say why. Everything still reaches the terminal unchanged; it is only copied on the way. Without it, ssh wrote the reason to a screen the interface immediately painted over, and a connection refused, a key rejected, a host key that had changed and a timeout all read alike — exited 255, with the reason visible only after quitting. The exit code is still reported, since that is the certain part; the last line of stderr follows it as context rather than as a claim about the cause. Passphrase and host-key prompts are unaffected: ssh puts those on /dev/tty, not stderr.

t opens the session in the main pane instead, keeping the host list beside it. There is one such session at a time, so connecting somewhere else replaces it rather than leaving a connection nothing in the interface can reach; a green ● marks the host it is on.

While that pane has focus every key goes to the remote, so ctrl+\ w hands the keyboard back to the list — the session stays connected and visible — ctrl+\ d detaches and ctrl+\ X ends it for good.

The prefix works from the host list as well. Detaching and ending are facts about the session rather than about whichever panel holds the keyboard, and reaching them used to mean going back into the pane first — where the prefix was silently ignored on the way, so ctrl+\ d on the list opened the delete confirmation for the highlighted host. t on the host already connected returns to its pane rather than building a second session over the top.

Sessions are persistent where tmux is installed. A pty whose master belongs to Omassh dies with it, so each session is instead run as tmux new-session -A -s omassh-<host> ssh … on a private tmux server — closing Omassh detaches rather than disconnects, and reconnecting reattaches with the screen and shell state intact. A yellow ● beside a host means a session is waiting — press t to reattach. Without tmux, sessions are ephemeral as before. Those sessions live on their own server socket, so tmux ls in your shell is unaffected.

Scrollback is ctrl+\ k and ctrl+\ j to page, ctrl+\ G to return live; typing anything snaps back on its own, since a terminal that stayed scrolled while you typed would hide your own output. For persistent sessions the history belongs to tmux — 10000 lines, surviving restarts — and those keys drive its copy mode. Without tmux the emulator keeps 2000 lines itself.

A pty runs ssh, its output feeds a VT emulator, and keys go back the other way, so the remote gets a real terminal the size of the pane, SIGWINCH and all.

Once a session has focus every keystroke belongs to the remote, ctrl+c included — which is why the session commands sit behind a ctrl+\ prefix, the way tmux uses ctrl+b. Press the prefix twice to send a literal one through.

A jump host is named by picking one of your hosts, and the connection to it is spelled out rather than left to ssh -J. -J hands the hop only -l, -p and -v, so the jump host's own key would be silently ignored; Omassh emits the ProxyCommand that ssh would build internally, with that host's port and identity in it. A jump host that Omassh does not know — ops@edge.example.com — is passed through as a plain -J, since ssh already understands it.

Chains work: a jump host may sit behind another, and each hop is told the address of the next one rather than being left to work it out. ssh expands %h and %p across a whole ProxyCommand, nested levels included, so writing them would hand every hop in a chain the final destination — the first hop would dial it directly and the hosts in between would never be contacted. Omassh knows every address in the chain, so it writes each one.

SFTP needs no SSH client of its own. Omassh runs ssh -s <host> sftp and speaks the SFTP protocol over that child's stdio, so OpenSSH performs the connection exactly as it would for an interactive session — ProxyJump chains, ProxyCommand, certificates, Match blocks, IdentityAgent and known_hosts all apply, with no second implementation to keep in step. BatchMode is forced on, because the child's stdin carries the protocol and there is nowhere to prompt; ssh-add the key first if it has a passphrase.

Credentials

C lists the ways you log in, and a host or a group can name one instead of repeating a user and a key path on every machine it applies to. n, e and d make, change and remove them. There are three kinds: a key credential is a user and a path to a private key, which becomes ssh -i; an agent one is a user with the key left to ssh-agent; and a password one is a user and a machine that takes no key at all.

A credential fills in the same fields a group does, and by the same rule: the nearest thing that sets a value wins. A host's own user beats its credential, which beats its group's user, which beats its group's credential, and so on up the tree. The detail pane says where each value came from — ← Prod deploy beside a key, exactly as it writes ← Production beside one a group supplied.

Deleting a credential takes it off everything that named it, and says how many will lose it first. A host left pointing at one that had gone would resolve to nothing at all and give no sign why.

Only a password credential changes how omassh connects. It stores no key, so ssh is told not to walk the agent's keys first: on a host offering several, ssh can spend MaxAuthTries on keys it was never going to be let in with and be refused before it reaches the password at all.

The password itself is not here. The store is an ordinary file and the export is meant for a dotfiles repository, so a password goes where the operating system already keeps such things — your login keychain on macOS, libsecret on Linux — and omassh keeps a name for it. A machine with neither says so while you are typing one, rather than accepting a password it cannot store. It is the same argument omassh makes about ssh-agent: there is a facility for this already, and reimplementing it would be worse than using it.

It reaches ssh through SSH_ASKPASS, which is OpenSSH's own way of being answered by a program rather than a person — no sshpass, and nothing pretending to be a terminal. ssh runs omassh, omassh reads the keychain, and the answer comes back on a pipe between those two processes. It is never in the argument list, which every process on the machine can read, and never in an environment variable, which every child would inherit; only the credential's id travels that way.

Because a program answers, a password host works where nobody is watching — sftp, forwards and probes — and not only where you are sitting. That costs one thing worth knowing. BatchMode=yes is forced on for those connections precisely so that nothing can stop and wait, and it would refuse the askpass helper along with every other prompt. For a password credential it becomes NumberOfPasswordPrompts=1 instead: ssh asks once, the helper answers, and a password that is wrong fails immediately rather than looping on a prompt nobody can see. What must never happen there is a connection that waits, and neither of those waits.

omassh export carries the name, the kind and the user, and never the password. A list moved to another machine arrives knowing who it logs in as and wanting the password typed into that machine's own keychain — which is the same thing it would want for a key that machine does not have yet.

Snippets

S lists the scripts worth keeping, so a command you run often is typed once rather than remembered. n, e and d make, change and remove them. The row beside a name shows the command itself where there is one, and the size where there is more.

A one-line script is typed into the form. Anything longer goes to $EDITOR on ctrl+e — $VISUAL first, then $EDITOR, then vi — and comes back with whatever you saved. Omassh does not grow a text area for this, for the reason it does not implement SSH: there is an editor on the machine already, and it is better than the one that would be written here. A script too long for its field then reads 4 lines — ctrl+e to edit, and the field stops taking keystrokes rather than letting the description be saved as the script.

↵ runs it. The screen that opens shows the whole script and the ways of saying where — the host in front of you, the hosts beside it, or any set you tick off by hand — and nothing runs until y. That step is not ceremony: a long snippet is listed by its size, so ↵ on its own would be running something you cannot see on machines you have to be right about.

Results fill in as they arrive, one row per host, each carrying the last line that host said. ↵ on a row opens what it said in full. esc stops the run and keeps what has already come back, because the hosts that did finish are worth reading and closing the screen would take them away in the same moment. A second esc leaves without waiting, for a run whose results are not coming: killing ssh does not close a pipe a background process is still holding.

Four hosts at a time. A probe is a TCP connection and nothing else, and its limit scales with the size of the sweep; this is a whole ssh session plus whatever the script does at the far end, and thirty simultaneous package upgrades is not a thing to start by accident. Hosts past the limit are shown as queued rather than running, because they are: a table claiming forty open sessions when four are open is describing something that is not happening.

A run has no terminal. It goes through the same unattended path a forward and an sftp session do, so sudo asking for a password fails immediately instead of waiting on a keyboard that is not there — which is the right answer, since waiting is the one thing a connection nobody is watching must never do. A password credential still works, by the same NumberOfPasswordPrompts=1 route described above. What a run cannot do is anything interactive, and that is what the other half of this is for.

p pastes the snippet into the session in the main pane instead, and stops there. Omassh delivers it as a paste rather than as typing, which a shell that understands bracketed paste holds in its edit buffer for you to read, change and run yourself. One that does not understand it — macOS ships a bash whose readline predates the idea — runs each line as it arrives. Which of those happened cannot be known from here: the mode belongs to the shell at the far end, and with tmux in between it is tmux's own that reaches omassh. So it is not claimed. A one-line snippet always waits, because what would run it is the trailing newline omassh takes off; a longer one says to go and look, next to the pane it has just put in front of you.

omassh export carries a snippet's script exactly as written, which is also the warning: a password belongs in a credential, where the export does not carry it, and never in a script, where it does.

SFTP

s opens a two-pane file browser on the selected host: your machine on the left, the host on the right. tab switches panes, ↵ enters a directory and - goes back up, c copies what is highlighted to the other pane, m, r, M and d make a directory, rename, chmod and delete, and g re-reads the pane you are in.

The local pane opens where Omassh was launched rather than at your home directory, since the directory you were just working in is more often the one you want to send. The remote pane opens where the session lands, and the keyboard starts there — you came here to look at the host.

A transfer is written beside its destination and moved onto it at the end, so the destination is only ever the old file or the new one. Writing straight to it truncated the file before a byte had arrived: a transfer that then failed left neither what was there nor what was wanted, and since the listing refreshed only after a transfer that worked, it went on reporting the size the file used to be. What moved is counted from what actually crossed rather than from the row — a symlink's row gives the length of the path it holds while the copy follows it, so 3MB would arrive under a bar announcing "copied 90B".

Copying onto a name already taken asks first. A copy over a file destroys it as surely as deleting one does, and deleting asks — while the copy used to go quietly ahead and say "copied 16B". It asks rather than refuses because replacing what is there is very often the point, and refusing would mean deleting first, which leaves a moment with neither file.

c on a directory takes the whole tree. Onto a directory already there it merges: the names that clash are overwritten and everything else in there is left alone. That is why it asks to merge rather than to replace — replace would promise that whatever is not in the copy goes away. Onto a file of the same name it is refused rather than asked about, since no answer to that question leaves both of them.

The walk reads each directory's listing rather than following the names in it, which is what keeps it finite: a listing reports a symlink as the link and not as what it points at, so only real directories are descended, and a real directory cannot contain itself. Following instead would turn one link pointing at its own parent into a copy that never ends.

Nothing here can write a symlink, so a link is copied the way copying its own row is — followed, and the file it names moved. A link to a directory has no bytes to move, so it is counted and stepped over rather than failing a tree that is otherwise fine; one link is a poor reason to abandon ten thousand files. The count is said at the end, because a copy that quietly left something behind is the one nobody checks until it matters.

Each file still lands atomically; the tree as a whole does not, and cannot. A copy that fails partway names the file it stopped on and leaves what had already arrived, rather than pretending nothing happened. Progress names each file by its place in the tree — project/src/deep/blob.bin — and the end is counted in files with the size beside it, since "copied 400 files" alone does not say whether the wait moved a manual or a film archive.

esc stops a copy that is running, and leaves the browser when none is. The row reporting progress says so, because the one moment that key matters is the one moment the key list is not on screen — the progress has replaced it. A file that was interrupted goes back to how it was, the part file removed and the destination never touched; a tree keeps what had already crossed, and the strip says how much that was, since nothing puts those files back. Leaving with q calls off a copy on the way out rather than pulling the connection from under it, which used to report a deliberate stop as a transfer that had broken.

Deleting follows the same rule about links the other way round: a symlink is a name, so deleting one deletes the name rather than what it points at. Following it was quiet and expensive — deleting a link to a directory emptied that directory, and deleting a directory that merely contained such a link destroyed everything on the far side of it, files nobody had selected, while the confirmation said "this cannot be undone" about what the listing had shown as a single file.

A filename is not a message. One carrying an escape sequence went straight into the interface: the row took its colour from the file rather than from the theme, and a name holding a cursor move or an erase had the terminal act on it in the middle of a redraw. Widths were never the problem — escapes have no width, so the frame held while the terminal did as the remote asked. Remote names are stripped of escapes and control characters before anything draws one.

What the far side refuses is reported in its own words where it offers any. pkg/sftp maps the two status codes with os equivalents and hands the rest back as they came, so a full disk, a quota or a server that simply says no all arrived as sftp: "Failure" (SSH_FX_FAILURE) — a protocol constant, shown to someone who has just typed a directory name.

Port forwarding

f lists the tunnels belonging to a host, and ↵ starts or stops the highlighted one. Local (-L), remote (-R) and dynamic (-D) are all here, written the way ssh writes them — 5432 and db.internal:5432 are the two halves of -L 5432:db.internal:5432.

A tunnel runs as a detached session on the same private tmux server the interactive sessions use, so it outlives the window that started it. That is the whole point: a forward has nothing to look at, and the value of it is that it keeps running. Quitting Omassh leaves your database tunnel up; opening Omassh again shows it as up, because it is.

Everything about reaching the host is sshx.Build's answer, the same as for a session, so a tunnel to a host behind a bastion goes through the bastion and one whose group supplies a user connects as that user. On top of that a forward adds only what makes a connection a tunnel: -N, ExitOnForwardFailure so a port that cannot be bound is a failure rather than a connection carrying nothing, and ServerAliveInterval so a tunnel whose network went away is reported as stopped instead of holding its port and reading as up.

BatchMode and ExitOnForwardFailure are forced on, ahead of anything -o passes in, because nobody is watching: a passphrase or host-key prompt in a detached session waits for an answer that is never coming, and a connection that could not bind its port carries nothing. Either way "up" would mean something other than a tunnel — with -o BatchMode=no on omassh's own command line, a forward to a host that refused the key sat at a password prompt while the interface reported it running. They are not preferences competing with yours; they are what makes ▶ mean anything. ssh-add the key first, and connect once interactively to accept an unknown host key. The keepalives are a preference, and stay yours to tune.

The local port is bound here before ssh is asked to bind it, so a port already in use is a sentence naming the port rather than a tunnel that vanishes a moment after starting. A tunnel that stops for any other reason keeps its session, dead, so it can still say what ssh said — ✖ in the list, with the reason beneath it. Without that the failure would be indistinguishable from a tunnel nobody had started.

Editing a rule reaches nothing already running — the tunnel is named by the rule's id, so it goes on carrying the route it was started with. It used to go on being reported as up against the new route as well, which is the shape of mistake where repointing a tunnel at staging leaves every connection landing on production and the screen agreeing with you. Each tunnel now records the whole invocation it was started with, so one whose rule or host has changed underneath it shows ▷ rather than ▶ — editing the host's address moves a tunnel just as surely as editing the rule — and ↵ restarts it on what they say now.

A green ▶ beside a host in the list means one of its tunnels is up.

Moving between machines

The store is bbolt — one binary file, with ids minted locally. Copying it over another machine's replaces that machine's list rather than joining it, and two machines that each gained a host cannot be reconciled by copying in either direction. The text form is the way across:

omassh export > hosts.yaml                # or -o hosts.yaml, written 0600
omassh import hosts.yaml                  # stdin when given no file
omassh export | ssh other-machine omassh import

Records match by name, never by id. That is what makes the same list mergeable at both ends, and it means a host keeps the session history hanging off it across an import — the host you have connected to forty times is still that host afterwards. A field the document leaves out keeps the value already stored, so importing fills in and corrects but never blanks; clearing a field is the interface's job. -n reports what an import would do and writes nothing.

A key the format does not have is named with its line, in the words of the file rather than of the program reading it — "jump_host" is not something a host has — it takes name, addr, port, …, with the keys read off the format so the list offered is the list accepted. An import reports the same 2 added, 0 updated whether or not it understood every line, so jump_host where the field is jump would otherwise leave that host showing via — and nothing on screen to say a line was dropped — the mistake and the success read identically. More than one YAML document in the file is refused on the same grounds, since only the first would be imported; a file that merely opens with --- is still one document.

Forwarding rules travel with their host, nested under it, because a rule says how you work with a machine — "the database is on 5432 through there" — which is as true on a laptop as on a desktop. A rule has no name, so the whole of it is its identity: kind, what it binds, where it comes out. Nothing is updated in place and nothing is removed, which is what lets two rules bind the same port for different destinations and still both arrive.

Session history itself stays behind. "Last connected two hours ago" is a fact about the machine that connected, and carrying it across would let a laptop's history overwrite a desktop's on every import. A running tunnel stays behind for the same reason: the rule crosses, the process does not.

The file holds no secrets — an identity is a path to a key, never the key — so it belongs in a dotfiles repo as comfortably as anything else there. config.yaml is already text and travels the same way, on its own.

Starting from ~/.ssh/config

omassh import-ssh-config                  # ~/.ssh/config, or name a file
omassh import-ssh-config -group Work      # ... all into one group

Only what Omassh needs to list, probe and reach a machine is taken: the alias, the address behind it, and the user, port, key and jump host. Everything else in that file keeps working without being copied, because Omassh runs the real ssh, which reads the file itself. Values resolve the way ssh resolves them — settings under Host * reach every alias, first match wins — so an imported host carries what ssh -G reports for it.

Wildcard and Match blocks are settings rather than machines, and are not imported as hosts. Included files are followed, which is the whole story for a config that is one line pointing somewhere else.

HostName %h.internal — one block standing in for a whole estate — is expanded per alias as ssh expands it, since ssh does that to the setting and never to the destination it is finally handed. Imported literally, those hosts arrived with a %h in the address and could not resolve anything.

Reaching your hosts from everything else

omassh export-ssh-config                  # writes ~/.ssh/config, or -o FILE
omassh export-ssh-config -n               # what it would change, writing nothing

A host kept only in Omassh is reachable by Omassh. scp, rsync, git, Ansible and every editor's remote mode read ~/.ssh/config and know nothing about a database, so this writes the list there — after which scp file prod-web: and git clone prod-web:repo reach the same machines by the same names, through the same bastions.

Only between its own markers. Everything outside them comes back byte for byte, comments and blank lines included, and the file is replaced by a rename rather than truncated, since a half-written ~/.ssh/config is every machine at once. A symlinked config — the usual way a dotfiles repository keeps one — is followed rather than replaced. Running it twice changes nothing.

Markers that do not pair up stop the whole thing, naming the line. A start marker whose end had been deleted would otherwise read as if the rest of the file were Omassh's, and there is no getting a ~/.ssh/config back from that.

The block goes at the top, because ssh keeps the first value it finds for each setting: below a Host * of yours, every exported host would quietly take that block's user instead of its own.

An alias your config already declares — in the file or in anything it Includes — is left exactly as it is and reported, never written over: import treats your config as a read-only source, and this keeps that promise from the other side. So is a host whose jump host will not be in the file, since writing it without one would dial a machine meant to sit behind a bastion. A host whose name ssh could not use as a destination is reported the same way rather than written; a name with a space in it is refused by ssh however it is quoted, and one holding * or ? would be a pattern governing hosts Omassh knows nothing about.

Groups are flattened on the way out, since ssh config has no such thing: what a host inherits is written onto the host.

Which matters if that file ever comes back. Importing a config Omassh wrote turns everything those hosts inherited into settings of their own, and a host carrying its own user has stopped following its group: change the group to ubuntu afterwards and that host goes on connecting as deploy, because nothing it reads comes from the group any more. No value is wrong, and the only sign on screen is a quiet one — the ← Production that sat beside the value in the detail pane is simply not there. omassh export is the round trip that keeps its shape, because YAML has groups to keep: what a host inherits stays out of the file entirely, and importing the same one twice changes nothing the second time. ~/.ssh/config is somewhere to write the list, not somewhere to keep it.

Install

brew install --cask cuonggt/tap/omassh

macOS and Linux, amd64 and arm64. Or from source:

go install github.com/cuonggt/omassh/cmd/omassh@latest

Releases are built with GoReleaser for macOS and Linux on both amd64 and arm64. goreleaser release --snapshot --clean produces the archives, checksums and a Homebrew cask locally without publishing.

The demo above is recorded with VHS:

./hack/demo.sh

That starts a throwaway SSH server on loopback so the session pane shows real shells, seeds a database with sample infrastructure, and drives the UI.

Run

go run ./cmd/omassh

enter connects. ? lists keys. q quits.

-o passes an ssh option through to every connection, as ssh -o does:

go run ./cmd/omassh -o ConnectTimeout=5

Configuration

Everything is optional — Omassh runs with no configuration at all. The config file follows the platform convention, so it is ~/Library/Application Support/omassh/config.yaml on macOS and ~/.config/omassh/config.yaml on Linux — omassh -h prints the resolved path, and so does the first line of -print-config. Writing to the wrong one is silent, since a missing config is not an error, so let the shell work it out:

cfg=$(omassh -print-config | head -1 | cut -c3-)
mkdir -p "$(dirname "$cfg")" && omassh -print-config > "$cfg"

The database sits beside it, as omassh.db. More than one Omassh can use it at once: the file is held for the length of an operation rather than the length of the program, which matters because enter hands the whole terminal to ssh and leaves no interface to look the next host up in. Each window reads the store when something happens to it, so a host added in one appears in another on its next reload — r at any time.

Themes (tokyonight, gruvbox, nord, mono, or your own palette), key bindings and ssh options all live there. A malformed config is reported at startup rather than ignored, because settings that silently do nothing are worse than an error that says why — and so is a key that is not a setting. ssh_option without its s, or a palette with selected where it means selected_bg, is named with its line rather than skipped past, since skipping looks exactly like the file not being read at all. More than one YAML document in the file is refused for the same reason, since only the first would take effect. Arrow keys and ctrl+c are reserved and always work, so no config can trap you in the program.

T opens a theme picker that recolours the interface as you move through it, since a palette is something you judge by looking at it. Keeping one writes theme: into the config file above, creating it if there is none — the one line, leaving comments, custom palettes and every other setting exactly as they were. So there is one place a theme comes from: what you pick and what you write by hand are the same setting, and neither quietly outranks the other. A palette defined under themes: is offered alongside the built-ins.

Colours degrade automatically: on a terminal without truecolor the palettes render in 256 colours, and mono exists for terminals with less than that.

If the screen goes blank

Some terminals clear the screen without telling the application — iTerm2's cmd+K is the common one. Omassh's renderer still believes its last frame is on screen and writes only the differences, so nothing reappears on its own. ctrl+l forces a full repaint; from inside a session, where ctrl+l belongs to the remote shell, use ctrl+\ r.

Reachability

A group holds the hosts beneath it as well as its own, so selecting one shows everything in it and the number beside it says the same. A group whose machines all live in its children used to read as empty, which is the shape most people nest for.

p probes the hosts in the current group with a plain TCP connection — ● up, ✖ down. Hosts behind a jump host or a ProxyCommand show ◌ and are skipped rather than guessed at: their address means something only from the far side of the proxy, so dialling it from here would report on a different machine entirely.

License

MIT © Cuong Giang

Directories

Path Synopsis
cmd
omassh command
Command omassh is a keyboard-driven SSH client for the terminal.
Command omassh is a keyboard-driven SSH client for the terminal.
hack
demoserver command
Command demoserver is a throwaway SSH server for recording the demo, so the embedded panes in demo.gif show real shells rather than connection errors.
Command demoserver is a throwaway SSH server for recording the demo, so the embedded panes in demo.gif show real shells rather than connection errors.
seed command
Command seed fills a database with plausible infrastructure for the demo recording.
Command seed fills a database with plausible infrastructure for the demo recording.
internal
config
Package config loads Omassh's YAML configuration.
Package config loads Omassh's YAML configuration.
keymap
Package keymap resolves key presses to actions, so bindings can be changed from the config file without the interface knowing which key did what.
Package keymap resolves key presses to actions, so bindings can be changed from the config file without the interface knowing which key did what.
portable
Package portable moves a host list between machines as text.
Package portable moves a host list between machines as text.
probe
Package probe checks whether hosts are reachable.
Package probe checks whether hosts are reachable.
safefile
Package safefile replaces a file without ever leaving it half written.
Package safefile replaces a file without ever leaving it half written.
secret
Package secret keeps the one thing omassh stores that its own files must not hold.
Package secret keeps the one thing omassh stores that its own files must not hold.
sftpx
Package sftpx browses and transfers files over OpenSSH's sftp subsystem.
Package sftpx browses and transfers files over OpenSSH's sftp subsystem.
smoke
Package smoke drives the real omassh binary against a real SSH server.
Package smoke drives the real omassh binary against a real SSH server.
sshx
Package sshx builds and runs OpenSSH invocations on omassh's behalf.
Package sshx builds and runs OpenSSH invocations on omassh's behalf.
store
Package store holds Omassh's data model and its bbolt persistence.
Package store holds Omassh's data model and its bbolt persistence.
term
Package term runs an SSH session inside an embedded terminal pane.
Package term runs an SSH session inside an embedded terminal pane.
ui
Package ui implements Omassh's terminal interface.
Package ui implements Omassh's terminal interface.
ui/theme
Package theme holds Omassh's colour palette and the styles derived from it.
Package theme holds Omassh's colour palette and the styles derived from it.
yamlerr
Package yamlerr says what yaml complained about, in omassh's words.
Package yamlerr says what yaml complained about, in omassh's words.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL