SSH
Reaching a guest over SSH from the Mac — which address actually works, the one line added to your ssh config, keys, accounts and host-key trust.
SSH is off on every machine until you turn it on. Enabling it opens a login path into the guest, and that is not something anyone should acquire by accident.
1vm ssh enable dev
1vm ssh config --install # add the Include line to ~/.ssh/config
ssh dev # plain ssh now resolves the machine
There is also a Configure → SSH pane in the app with the same controls; neither front end wraps the other.
Which address actually works
This is the part that trips people up, and it depends on the engine. The guest
address you see in 1vm list is not always something you can connect to.
| Engine | Network | Mac → guest |
|---|---|---|
| Virtualization.framework | NAT or host-only | Direct, to the guest's 192.168.64.x lease |
| Virtualization.framework | Bridged | Direct, to its LAN address |
| 1VM engine | NAT or host-only | Through a loopback port forward only |
| 1VM engine | Bridged | Direct, to its LAN address |
On the 1VM engine the guest sits behind a user-mode network stack at a constant
10.0.2.15, and your Mac has no interface on that subnet. ssh 10.0.2.15 can
never work there. What works is the forward:
ssh -p 2222 onevm@127.0.0.1
Turning SSH on creates that forward for you, and it applies live — no
reboot. Forwards bind 127.0.0.1, so enabling SSH never exposes a guest to
your network.
The one line added to your ssh config
1vm ssh config --install adds exactly one sentinel-delimited block, at the
top of ~/.ssh/config:
# >>> 1VM managed >>> do not edit this block
Include ~/Library/Application Support/OneVM/VMs/.ssh/config
# <<< 1VM managed <<<
Everything else is generated into the library's own .ssh/ directory, so your
file keeps one line and 1VM owns the rest. ONEVM_HOME moves the whole thing,
which means a project-local library carries its own SSH config.
The rules this follows, because it is editing a file it does not own:
- Top of the file, because
ssh_configis first-obtained-wins — anIncludeplaced after an existingHost *block would silently lose. - Idempotent. Running it ten times leaves one block.
- Atomic, with a backup. The original is copied to a timestamped
config.1vm-backup-…before anything is written. - Reversible.
--uninstallremoves exactly the sentinel block and nothing else.--printshows it without writing. - It refuses rather than reorders. If your config already sets options for all hosts above where the block would go, 1VM tells you and declines instead of rearranging your file.
- It will not shadow a real host. If the machine name already resolves to
something in your ssh config, only the
<name>.1vmalias is emitted.
A generated block looks like this — the port and address come from the reachability rules above, never from the raw guest IP:
Host dev dev.1vm
HostName 127.0.0.1
Port 22417
User dev
IdentityFile ".../dev.vm/agent-key"
IdentitiesOnly yes
AddressFamily inet
StrictHostKeyChecking yes
ConnectTimeout 5
Keys
Each machine has its own Ed25519 key inside its bundle at agent-key, mode
0600. Because it lives in the folder, 1vm clone carries it along and the
clone is reachable with the same identity.
You can use your own key instead. 1VM stores the path to it and copies only
the matching .pub into the guest — it never reads, copies or stores your
private half.
1vm ssh keys dev # show the machine key
1vm ssh keys dev --print-public # the public line
1vm ssh keys dev --regenerate # new keypair
1vm set dev ssh.identityFile ~/.ssh/id_ed25519
Accounts
1vm set dev ssh.users … manages which accounts exist in the guest. Names are
validated per guest family — Linux and macOS take [a-z_][a-z0-9_-]{0,31},
Windows is capped at 20 characters and rejects the reserved punctuation — and
admin membership maps to wheel/sudo, Administrators, or admin
respectively.
onevm is reserved. It is the account 1vm exec, 1vm task run and the
MCP server log in as, so renaming or deleting it would break them. The pane
shows it as a locked row labelled as the agent account rather than hiding it —
a login that exists but is invisible in the list of logins would be a lie.
root and Administrator are reserved too, and root login stays off.
Host-key trust
Most tools trust whatever answers on first connection. 1VM does better, because it has an out-of-band channel: it reads the guest's public host key over the guest-agent channel, which does not traverse the network being authenticated, and pins it.
| State | What you see | What ssh does |
|---|---|---|
| Not learned yet | "the first connection will trust whatever answers" | StrictHostKeyChecking accept-new |
| Learned | the SHA256:… fingerprint and the date | StrictHostKeyChecking yes |
| Mismatch | a warning naming the date the old key was pinned, and a Relearn button | yes — so the connection correctly fails |
1vm ssh trust dev # pin the current host key
1vm ssh trust dev --relearn # re-pin after a legitimate change
A mismatch is normal after 1vm reset, after restoring a snapshot taken before
sshd existed, or after a reinstall. It is also exactly what an attack looks
like — so it is always a button you press, never a silent overwrite.
Cloning. The identity key is copied to a clone deliberately; the host-key
pin is not, because two machines presenting one host key is a real problem. Use
1vm clone dev test --rekey-ssh to regenerate the guest's host keys on first
boot.
Security defaults
- Key-only authentication. Password and keyboard-interactive auth are off, root login is off, and there is no agent or X11 forwarding.
- Forwards bind loopback, so a guest is never exposed to your LAN by enabling SSH. Binding a public address is possible but warns you in plain words about what it means.
- Private key material and account-creation arguments travel the private channel only and never reach a log.
Rollback mode wipes SSH accounts
Rollback mode discards everything written since boot at power-off — which includes accounts and keys provisioned into the guest. This is not a bug, and 1VM handles it by re-running provisioning on every boot rather than trusting a marker file. Expect the guest's host key to change each time, too.
Guest to Mac
The reverse direction — SSH from the guest to your Mac — reaches the Mac at
10.0.2.2 on the 1VM engine, and only for ports with a registered service.
1VM will not enable macOS Remote Login for you; that stays your decision in
System Settings.
Current state
SSH is implemented across the app, the CLI and both engines, and covered by the unit test suite. Two honest caveats:
- Live-guest soak testing is still the outstanding acceptance gate.
- The shipped Omarchy factory image predates
opensshbeing added to its package list, so a factory Omarchy guest has nosshduntil that image is rebuilt. Other Linux guests are unaffected.