First connect — what’s actually happening
The shadow vault model
obsidian-remote-ssh does not edit your remote files directly through Obsidian’s vault layer. Instead it:
- Creates a local shadow vault under
<plugin-dir>/shadow-vaults/<profile-id>/. This is a real Obsidian vault on your local disk. - Mirrors the remote vault’s files into that shadow vault (lazily — large files are fetched on first open).
- Watches both sides for changes and syncs them through the SSH-tunneled daemon RPC.
Result: Obsidian thinks it’s editing a local vault. All Obsidian features (Dataview, Templater, Excalidraw, …) work because they ARE working against a local vault. The “remote-ness” lives in the sync layer, invisible to most plugins.
See Shadow vault architecture for the full design.
What gets installed where
On your local machine
<vault>/.obsidian/plugins/remote-ssh/
main.js # plugin bundle
manifest.json
styles.css
server-bin/obsidian-remote-server-<os>-<arch> # bundled daemon binary, uploaded on connect
data.json # plugin settings (profiles, host keys, etc.)
console.log # operational log (5 MB × 4 files: console.log + .1 + .2 + .3)
telemetry.jsonl # local-only counters (opt-in)
~/.obsidian-remote/vaults/<profile-id>/ # shadow vault for each profile (a real Obsidian vault on disk)
On the remote host (under your remote user’s $HOME)
~/.obsidian-remote/
server # daemon binary (uploaded from plugin)
server.sock # Unix socket the daemon listens on
token # 32-byte auth token (mode 0600, daemon-generated)
server.log # daemon stdout/stderr
The plugin never writes outside ~/.obsidian-remote/ and the configured remote vault path.
What flows over the wire
- SSH session — established with your normal keys/agent.
~/.ssh/configis honored (jump hosts, Host aliases, IdentityFile). - SFTP subsystem — used once per connect to upload the daemon binary and read the auth token file.
- TCP port-forward — Local TCP socket → SSH → Unix socket on the remote (
~/.obsidian-remote/server.sock). All RPC traffic flows over this tunnel. - JSON-RPC 2.0 — see API & protocol for the wire format.
Nothing leaves the SSH connection. No third-party servers, no STUN, no relay.
Connect lifecycle
sequenceDiagram participant U as You (Obsidian) participant P as Plugin participant S as SSH (your config) participant H as Remote host participant D as Daemon U->>P: Connect "My Pi" P->>S: open SSH (keys / agent) S->>H: authenticate P->>H: SFTP upload server binary (skip if hash matches) P->>H: SHA256 verify binary P->>H: SSH exec: nohup ./server --vault-root=… & H-->>D: daemon starts, writes ~/.obsidian-remote/token P->>H: SFTP read token (poll up to 5s) P->>S: open TCP forward → ~/.obsidian-remote/server.sock P->>D: JSON-RPC: auth(token) D-->>P: {ok: true} P->>D: JSON-RPC: server.info D-->>P: {version, protocolVersion, vaultRoot} P->>U: open shadow vault window
After connect
- The daemon stays up across plugin reloads. If you close Obsidian and reopen within ~5 minutes, the next connect skips the binary upload entirely.
- If the daemon crashes, the plugin auto-restarts it on the next operation. Daemon panel surfaces the previous log.
- See Reconnect behavior for what happens when the SSH connection drops.
Next: SSH config import or jump straight to Configuration reference.