What It Does
The daemon can listen for SSH next to its HTTP API. Any SSH client then reaches a workspace the way it reaches a machine:ssh opens a shell or runs a command, and sftp and scp move files. Nothing is installed anywhere and there is no real host behind the door; every command runs in mirage’s shell and every file operation goes through the workspace’s mounts.
The SSH username names the workspace:
Turn It On
-
Pick a port and add it to
~/.mirage/config.toml: -
Authorize your public key. The file uses OpenSSH’s
authorized_keysformat: -
Restart the daemon (
mirage daemon restart) and create a workspace with an id you can type:
~/.mirage/ssh/host_ed25519_key and keeps it, so your known_hosts entry stays valid across restarts. Both daemons read and write the same OpenSSH key format, so either can serve the other’s key.
An ~/.ssh/config entry saves the flags:
Settings
Per key the environment variable wins over the[daemon] table, which wins over the default. mirage config list --resolved shows each one’s effective value and where it came from, and mirage config set ssh_port 2222 writes it.
The Python daemon needs the
ssh extra (pip install 'mirage-ai[ssh]'); the TypeScript daemon needs ssh2 installed beside it (npm install ssh2). A configured door that cannot open (the port is taken, the library is missing) fails the daemon’s start instead of leaving it up without SSH.
What a Login Gets
- One session per channel. Every
sshcommand and every shell runs as a fresh mirage session under the workspace’s default profile, just as each channel is a fresh process under sshd. Acdorexportnever leaks from one channel to another, and two channels never wait on each other’s lines. The session is closed when the channel ends. - Profiles and policies apply. SSH lines go through
Workspace.shell, and SFTP goes through the same mount core the FUSE mount uses, so mount modes, hides, asks and policies answer exactly as they do for a shell in that session. A read-only mount refuses ansftp putwith “Permission denied”. - A login environment.
HOMEis/, where every session starts,USERandLOGNAMEare the workspace id,SSH_CLIENTandSSH_CONNECTIONdescribe the connection, andTERMcomes from the client’s terminal. A variable the profile already set keeps its value, and each one clears the session’spre_sessiongate like anyexport. - History. Typed lines land in
/.bash_historylike any other top-level line. The login environment line does not. - An interactive shell. With a terminal (
sshwith no command), the door printsmirage:<cwd>$, echoes and edits the line, and treats Ctrl-C as an interrupt: the running line is cancelled and$?reads 130, as in bash. Ctrl-D on an empty line orexit [N]ends the shell. Without a terminal (ssh -T host < script), lines are read from stdin with no prompt, the waybash -sreads them. - Bounded input. A non-terminal shell line may contain up to 1 MiB before its newline. Longer lines close the channel with status 1 and
mirage: shell input line too long; no prefix is executed. Streaming stdin to a command is not subject to the line limit. Terminal editors ring the bell and ignore additional input at their line limit; Backspace and Ctrl-U still work. - The client’s exit status.
ssh host cmdexits with the line’s status, and a dropped connection cancels the line it was running.