Markdown notes that run shell commands, code, SQL, and AI prompts

A Markdown notebook for developers and DevOps who keep commands in their notes. Write a note, press ⌘↩ on a code block, and its output streams in beneath it. In your shell, in your project, on your machine, or on a server.

Install Ledge Servercurl -fsSL https://ledge.sh/server.sh | sh
Free and open source · Apache-2.0 · macOS on Apple Silicon · Linux · Windows 11 with WSL · iPhone and iPad · Android (beta)

Each note has its own shell

Shell blocks run in a persistent shell that belongs to the note. A cd, an exported variable, or an activated virtualenv carries into the next run.

  • ⌘↩ runs the block under the caret. Output streams in beneath it.
  • ⇧⌘↩ sends the block to the note's terminal drawer, a second shell you can keep typing in.
  • A running block takes input. Answer a sudo password or a [y/N] in the output panel.
  • cwd: and env: in the frontmatter set where the note's shells start and what they carry.
  • norun on a fence takes the Run button away. Use it for a command you are quoting, not running.
Docs: Running Code

Keep your notes on a server

Point Ledge at a machine over ssh and the notes live there. The server holds the files and runs the shells. The app on your computer is the window onto it.

  • No account, no service, and no port to open. The only thing between you and the server is ssh.
  • Shells and running blocks belong to the server. Close the laptop and they keep going. The output is waiting when you open it again.
  • Your computer, your phone, and a second window can all be on one server at once, each with its own tabs.
  • Sign in with an ssh key, or with a password kept in the system keychain. Ledge pins the server's host key and refuses any other.
  • Install with the command below. ledge backup puts an encrypted copy of the notes in an S3-compatible bucket every hour.
Install Ledge Servercurl -fsSL https://ledge.sh/server.sh | sh
Docs: Keep Notes on a Remote Server

The same notes on your phone

Ledge runs on iPhone and iPad as a window onto the same server. The phone holds no notes. It reads, edits, and runs what is on the server.

  • Read, edit, and create notes with live preview. Search, tags, backlinks, daily notes, and wikilinks all work.
  • Tap Run on a block and it runs on the server. Switch apps and it keeps running. The output is waiting when you come back.
  • ^C, ^D, Escape, and the arrow keys sit above the keyboard, so you can interrupt a command, quit a pager, or move around in htop.
  • Scan the pairing code that ledge pair prints on the server. The phone signs in with a key made in the Secure Enclave that never leaves it, and that key can speak to Ledge and nothing else.
  • Locked notes unlock with the passphrase. The phone stores none of it.
Docs: Ledge on Your Phone

Run blocks on other machines

Add a host: line to the frontmatter and every run in the note happens over ssh on that host. The note itself stays where it is.

  • Declare several hosts and Ledge asks which one on every run. Enter repeats your last pick.
  • Mark a block confirm and Ledge shows the code, names the machine, and asks before running it.
  • Authentication is your own ssh. Keys, agents, ~/.ssh/config, and 2FA work as they do in a terminal.
  • The note's cwd and env travel with the run. Profiles and secrets never do.
  • python, node, and other interpreted blocks run on the host too, with a per-machine interpreter override in Settings.
Docs: Run Code on Remote Hosts

Put notes side by side

⌘D splits the view left and right, ⇧⌘D top and bottom, and either side can split again. Each pane has its own tabs.

  • Every note keeps its own shell, so two panes are two working environments. A run keeps streaming in the pane you left.
  • Drag a tab to another pane. It keeps its caret, scroll position, undo history, and any output still streaming.
  • ⌃Tab cycles tabs. ⌃1 to ⌃9 jump to one by position.
  • Each workspace saves its layout and restores it at the next launch. ⌘1 to ⌘9 switch workspaces, layout and all.
  • One terminal drawer spans the window and follows the focused note.
Docs: Panes and Tabs

Secrets stay out of your notes

A profile is a named file of environment variables kept outside your notes folder. A note that names it gets the variables in its shells. The note carries only the name.

  • profile: deploy in the frontmatter reads ~/.config/ledge/profiles/deploy.env, a plain dotenv file readable only by you.
  • Notes get synced, backed up, and read by agents. A profile value stays on the machine.
  • Any number of notes can share one profile. An env: line overrides a value for one note.
  • A built-in editor shows the values masked, with one toggle to reveal them.
  • envFile: .env loads the project's own dotenv for everything that is not a secret.
Docs: Profiles and Secrets

Built to be worked by agents

Ledge ships an MCP server. claude mcp add ledge -- ledge mcp connects Claude Code, and any MCP agent can read, search, create, and edit your notes through it.

  • Notes are addressed by title, which survives renames, so an agent's references do not go stale.
  • A terminal opened inside a note knows which note it is in. "Add a TODO section here" needs no explanation.
  • A prompt code block is a runnable block. ⌘↩ pipes it to the agent and the reply streams in beneath it.
  • Agents can read your settings and the manual, so "check the Ledge docs" is a fair instruction.
  • There is no delete tool, and locked notes refuse their bodies to every agent.
Docs: Agents and Ledge

Lock the notes that matter

Locking encrypts a note's body on disk behind a passphrase. Agents never see it, sync services carry ciphertext, and reading it requires an unlock.

  • The title, frontmatter, and tags stay visible, so links and navigation keep working.
  • Search and backlinks skip locked bodies and say how many notes they skipped.
  • One passphrase covers every locked note. ⌘L relocks, and the vault relocks itself after 15 minutes idle.
  • Images pasted into a locked note are sealed with it.
  • A locked note is self-contained. On another machine it unlocks with the passphrase alone. There is no recovery.
Docs: Note Locking

Sync with anything, share with git

Notes are plain files in ordinary folders, so syncing them is syncing a folder: iCloud Drive, Dropbox, git, Syncthing, or whatever you already use. There is no database on the side.

  • Ledge watches the folder and follows outside changes live, even in an open note.
  • Push a workspace to a git remote and the people you work with clone it. Each clone attaches as an ordinary workspace, and a pull shows up in the sidebar with nothing to refresh.
  • One note changed in two clones is a git conflict like any other. If a pull rewrites a note you have open, your version keeps the file and the other goes to the workspace trash.
  • A Sync note in the workspace pulls, commits, and pushes with one ⌘↩.
  • Profiles never enter the folder, locked notes travel as ciphertext, and the trash stays out of git.
Docs: Keep Notes Synced

Ways to use Ledge

Six kinds of notes, each with the manual page that covers it.

Data exploration
sql
select plan, count(*)
from signups group by plan;
Done41 ms
 plan | count
------+-------
 free |  1204
 pro  |   187
python
import pandas as pd
df = pd.read_csv("signups.csv")
print(df["source"].mode()[0])
Done610 ms
newsletter
Docs: Running Code
Project runbook
cwd: ~/Projects/api
envFile: .env
sh
docker compose up -d db
make migrate
Done3.8 s
 ✔ Container api-db-1  Started
migrated to 0043_signup_source
Docs: Run a Project from a Note
Incident response
profile: prod-readonly
redis
INFO keyspace
Done12 ms
# Keyspace
db0:keys=4812339,expires=0,avg_ttl=0
Docs: Profiles and Secrets
Learning to code
ruby
%w[a b c].each_with_index do |s, i|
  puts "#{i}: #{s}"
end
Done88 ms
0: a
1: b
2: c
Docs: Running Code

The notes app underneath

Everything else you expect from a notes app is here.

Your languages

Python, Node, Ruby, and PHP run out of the box. A redis block feeds redis-cli, pointed at staging or prod by the note's REDIS_URL. TypeScript uses the Bun runtime bundled with the app, so ts blocks run with nothing installed. Adding an interpreter is one line in Settings.

"sql": "psql \"$DATABASE_URL\" -f"

Daily notes and templates

⌘J opens today's note, stamped from your daily template. Templates are ordinary notes with {{date}} and {{yesterday}} tokens, and one carrying a prompt block gives every morning a one-keystroke briefing.

promptSummarize [[{{yesterday}}]]

A workspace is a folder

Attach a project folder and its Markdown files become notes where they are, running their blocks in the project. ⌘1 to ⌘9 switch workspaces, and each keeps its own pane layout. Removing an attached folder from Ledge touches no files.

Attach Folder as Workspace…

Live preview

Syntax hides away from the caret: **bold** shows bold, links show their label, tables and images render in place, and a checkbox is clickable. Move the caret in and the exact characters come back. One setting shows every character all the time.

"editor.livePreview": false

Paste anything

Copy part of a web page, a Slack thread, or a Google Doc, and ⌘V pastes it as Markdown: headings, lists, checkboxes, links, and tables included. ⇧⌘V pastes the plain text. An image on the pasteboard lands in the workspace as a file, rendering in place; pasted into a locked note, it is encrypted from the first byte.

![](.ledge-assets/pasted-2026-07-31.png)

Find your way back

⌘P opens a note by title; type # for full-text search. Wikilinks address titles, so a retitle never leaves a stale link. Backlinks and outline each get a panel.

[[Oncall handbook#Paging]]

Tags and favorites

Tag a note with inline #hashtags or a frontmatter tags: line, and ⌥⌘T lists every tag with its count. Press f on a note's row, or click its star, and it sits in a Favorites section at the top of the sidebar, however deep in a folder it lives. Both are frontmatter, so they travel with the file.

favorite: true

Deleting is undoable

⌫ moves a note to the workspace trash and shows an Undo strip. The Trash section in the sidebar holds it for 30 days, where r restores it and permanent deletion asks first.

"trash.ttlDays": 30

The ledge CLI

Lists, reads, searches, creates, and appends from any terminal, and the app follows along live. ledge run runs a note's code blocks there too, so a runbook works from a script or CI. Results go to stdout, everything conversational to stderr, so pipes stay clean.

ledge run Deploy --all

Runnable Markdown notes_

Free and open source under Apache-2.0. No account required, and no database on the side. Your notes are Markdown files on your computer or on a server you run, and they stay yours.

Install Ledge Servercurl -fsSL https://ledge.sh/server.sh | sh
FreeApache-2.0No accountPlain filesMac, Linux, Windows, iPhone