Ledge on Your Phone
Ledge runs on an iPhone, an iPad or an Android phone as a window onto a server. The phone holds no notes: it reaches a server over ssh, the way the desktop app does in Keep Notes on a Remote Server, and shows you what is there.
Get Ledge for iPhone from the App Store. It runs on iOS and iPadOS 17 or newer. Ledge for Android is in an open beta on Google Play and runs on Android 10 or newer: ledge.sh/android has the three steps to join. A phone with no Google account installs the APK instead ("Install the Android APK" below). Either one needs a server to connect to before it shows anything.
A server that already serves your desktop needs nothing more. A machine without one needs the server installed first, as "Install the server" on that page describes, and the phone shows the same commands ("Set up a server" below).
Install the Android APK
Every Ledge release on GitHub carries the Android app as android-Ledge.apk. It installs without Google Play and without a Google account. Download it on the phone and open it, and Android asks once to let your browser or file manager install apps.
The APK does not update itself. To update, download the newer APK and install it over the old one. The pairing with your server stays.
An APK install and a Google Play install cannot update each other, because the two are signed with different keys. Switching from one to the other means uninstalling Ledge first and pairing the phone with your server again.
The first screen
The first launch opens on "Connect to your Ledge server", which offers three ways to add a server:
| Control | Use it when |
|---|---|
| Scan a pairing code | The server can show a code with ledge pair, or a desktop that already has the server can show one ("Pair with a code") |
| I don't have a server yet | You have a Mac or Linux machine with ssh, and Ledge is not installed on it ("Set up a server") |
| Add an existing server | Ledge is already installed on the server, and you would rather type its account and address ("Pair by address") |
Pair with a code
A pairing code names a server and its host keys, so the phone can add it without anyone typing an address or comparing a fingerprint. On the server, run:
ledge pairOn a terminal, it first lists every address the machine has, with a note on which devices reach each one: its tailnet name and address, the address your ssh session reached, its public address when it runs in a cloud, its other network addresses, and its name. Type a number to pick one, or an address of your own as host or host:port, or press Return for the first. It then prints the code as a QR code, then the account, host, port, and host keys it holds, then the same code as a link. Without a terminal, it takes the first address and lists the rest under the code, and --host names one on the next run. ledge pair --help lists the other flags.
A desktop that already has the server in its list can show the same code without a terminal: the QR code icon on the server's row in Notes On… ("Show a pairing code for a server" on Keep Notes on a Remote Server). The same link pastes into the desktop app's Add Server form ("Add a server from a pairing code" on that page).
The code names one address, and the reader connects to exactly that, so pick the one your other devices reach from where they will be. A tailnet name works from anywhere a device is on the tailnet. A home network address works from a device on that network. A cloud machine's public address works from anywhere, when its sshd is reachable from outside. A machine behind a router's port forward has an outside address no source knows: type it at the menu with its port, or give them with --host and --port. A desktop's code names the address that desktop dials, with the same reach.
On the phone, tap Scan a pairing code on the first screen, or in Add Server… inside the app ("More than one server" below), and point the camera at the QR code. Scan it from Ledge rather than the phone's camera app, which opens the code in the browser. Ledge shows what the code names and connects only when you tap Connect. Choose how to sign in first, the same way as in "Pair by address": with a key, whose line still has to be in the server's authorized_keys, or with a password. Ledge signs in only if the server offers one of the host keys in the code, so there is no fingerprint to check by eye.
The code holds no password and no key. Someone who photographs it learns where the server is and which account to try, and nothing that signs them in.
Opening a ledge://pair link on the phone shows the same screen, with a warning. A link can come from anyone, including someone running a server that only looks like yours, so connect only if the link came from your own server.
A code never replaces a host key the phone already has. When Ledge has a different key pinned for the same address, it refuses the code and keeps that key. If the server's key really changed, connect to it from the Servers list, where Ledge shows you the new key to check.
Set up a server
I don't have a server yet opens "Set up a server", which shows the two commands that make a machine a Ledge server, on Linux or a Mac:
curl -fsSL https://ledge.sh/server.sh | sh
~/.ledge/.server/bin/ledge pairRun them in a terminal on that machine, signed in as the account the phone should use rather than root. The first installs the server in that account's home, where a command run over ssh finds it, and needs no sudo. The second prints a pairing code for that account, and Scan the pairing code on the same screen reads it. It names ledge by its full path because the PATH line the installer adds reaches only new terminals.
Copy commands puts them on the phone's clipboard. Share commands opens the phone's share sheet, for AirDrop, Quick Share, a message, or any app that can carry them to a computer with a terminal open on that machine.
On a Mac, turn on Remote Login first, in System Settings under General, then Sharing; on a Linux desktop, install the openssh-server package. A computer that runs the Ledge app needs only "Install Shell Command (ledge)" from the app's command palette in place of the first command: it puts ledge where the phone's ssh looks, pointing at the app's own copy, so the phone sees the same notes the app shows. The second command then prints the code.
The machine needs sshd running and an address the phone can reach. Keep Notes on a Remote Server has the details of the install, including installing with Bun instead, and Tutorial: Set Up a Ledge Server walks through a fresh VPS.
If you already have a server, Add an existing server at the bottom of the screen opens the form from "Pair by address", and Back from there returns to the first screen.
Pair by address
Add an existing server opens "Pair with a server", a form with three parts. The same form opens from Add a server in the Servers list, with Scan a pairing code at its top.
The first part is the machine: user@host, and a port when sshd is not on 22. A phone reads no ~/.ssh/config, so write the address out.
The second is how to sign in. With A key, the default, the form shows a command to run on the server. On its first launch the phone makes a key of its own, in the Secure Enclave on an iPhone or iPad and in the Android Keystore on Android, and that key never leaves the phone: there is no file to copy in or out. What leaves is the public half, as one line for the server's ~/.ssh/authorized_keys:
restrict,command="PATH=$HOME/.ledge/.server/bin:$PATH ledge serve" ecdsa-sha2-nistp256 AAAA... ledge-iphone-3f2a91c0The command adds that line to the file, creating ~/.ssh first if the account has none:
mkdir -p ~/.ssh && chmod 700 ~/.ssh && printf '\n%s\n' 'restrict,command="PATH=$HOME/.ledge/.server/bin:$PATH ledge serve" ecdsa-sha2-nistp256 AAAA... ledge-iphone-3f2a91c0' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keysRun it on the server, signed in as the account Ledge uses: over ssh from your computer, or in the provider's web console for a new VPS. Copy command puts it on the phone's clipboard. Share command opens the share sheet, for AirDrop, Quick Share, a message, or any app that can carry it to that terminal, which is where a phone's clipboard falls short. The comment at the end of the line names the phone, ledge-iphone-… or ledge-android-…, so the line is easy to find again when you want to revoke it.
The line arrives already restricted, in the way "Restrict the key to Ledge" on Keep Notes on a Remote Server describes: the phone's key can speak Ledge's protocol and nothing else. It looks for ledge in ~/.ledge/.server/bin first and then on the PATH an incoming ssh gets, so a server installed in either place starts ("Check that ssh can find the server" on the same page).
The third is Connect. The phone dials the server, shows its host key fingerprint, and asks "Is this the server?" alongside the command that prints the same fingerprint on the server. Trust pins the key, and a server that later presents a different one is refused, the same as on the desktop.
Ledge adds the server only once ledge serve answers there. On a machine where ssh cannot find it, Connect says "Ledge's server is not installed" and adds nothing. Install it, then tap Connect again.
Sign in with a password instead
Choose "A password" under Sign in with and type the password for that account. The eye button at the end of the field shows what you typed, to check it before connecting. The phone keeps the password to itself, in the keychain on an iPhone or iPad and sealed with an Android Keystore key on Android, and no key line has to be installed.
The trade-off is the one described on Keep Notes on a Remote Server: a password reaches a fresh machine today, and a key is the better long-term answer. A server with PasswordAuthentication no refuses it.
When the phone cannot reach its server
A server slow to answer shows "Connecting to user@host…" and, after a few seconds, a Choose a Different Server button.
A server that cannot be reached shows "Ledge could not reach a server." with the reason, then Try again and Choose a server. Try again comes first because the usual cause is the phone having moved networks, not the server having moved. Choose a server opens the Servers list, where you pick another one or add one.
A host key that has changed, or a key or password the server no longer accepts, lands you back on the pairing form with the address filled in. Retrying cannot fix either, so the pin is dropped and you compare the fingerprint again.
Removing the last server returns the phone to the first screen. Deleting the app deletes its key with it, so a reinstalled phone is a new device to every server and needs its line installed again. A new phone set up from a backup of an old one starts the same way: the key and the server list stay behind.
More than one server
Inside the app the connection bar works as on the desktop: tap it to add, edit, remove, or switch servers, with the same fingerprint step (Keep Notes on a Remote Server). The form shows the command that installs the phone's key where the desktop's shows a key path, with Share Command beside Copy Command.
Add Server… starts with Scan a pairing code, where the desktop's form has a field for the pasted link. It opens the camera, then the same "Pair with a server" screen as the first launch, and the app reopens on the new server once you tap Connect there. Cancel returns you to the form, where you can type the address instead. Editing a server has no scan: a code never replaces a host key the phone already has.
A phone and a desktop can be on one server at once. Each keeps its own tabs, and a note's terminal has one owner between them.
What a phone does
| On a phone | Not on a phone |
|---|---|
| Reading, editing, and creating notes, with live preview | The terminal drawer |
| Quick open, full-text search, tags, backlinks, the outline | |
| Daily notes, templates, wikilinks | |
| Attaching a folder on the server as a workspace, by typing its path | |
| Images, added from the photo library, the camera, or Files | |
| Running a block inline, with the host picker and the confirmation | |
| Editing a note's profile | |
| Unlocking locked notes | |
| The trash | |
| Switching workspaces and servers |
The pages for those features say how each works on a touch screen: Run on every block, the Code Block button, and the control keys above the keyboard in Running Code, adding a picture in Images, the mode chips under the search field in Finding Things, and splits in Panes and Tabs.
Tapping through the tree reuses one tab rather than filling the strip, since a note you tap opens as an italic preview (Panes and Tabs). A long press on the tab holds Keep Tab Open, which is what makes it stay, and so does typing in the note. It matters more here than on a desktop: there is no ⌘W, so a strip that filled up would take a long press and a menu item per tab to empty.
A block keeps running on the server while the app is in the background, and what it printed is waiting when you come back. Android drops the connection a few seconds after the app leaves the screen, and Ledge reconnects when you return. A program that needs a whole terminal belongs in a desktop's drawer on the same server.
On Android, Back closes whatever is open on top, a menu, a dialog or the sidebar, and with nothing open it puts Ledge in the background without closing it. Back and the navigation bar also put the keyboard away.
Unlocking a locked note asks for the passphrase every time. The phone stores none of it, and Face ID or a fingerprint does not stand in for it. The relock timer is the server's, so a phone put away for an hour finds its locked notes closed again (Note Locking).
The manual a phone shows is the connected server's copy, so it describes the version of Ledge that server runs.