https://github.com/avelino/outl
Local-first outliner. Markdown is the source of truth. Sync that doesn't corrupt your tree when two devices edit offline
https://github.com/avelino/outl
markdown notes-app outliner
Last synced: about 1 month ago
JSON representation
Local-first outliner. Markdown is the source of truth. Sync that doesn't corrupt your tree when two devices edit offline
- Host: GitHub
- URL: https://github.com/avelino/outl
- Owner: avelino
- License: mit
- Created: 2026-05-24T14:04:46.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-07-04T21:44:18.000Z (about 1 month ago)
- Last Synced: 2026-07-04T23:04:21.223Z (about 1 month ago)
- Topics: markdown, notes-app, outliner
- Language: Rust
- Homepage: https://outl.app
- Size: 19.1 MB
- Stars: 66
- Watchers: 2
- Forks: 5
- Open Issues: 32
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
outl
Local-first outliner. Markdown is the source of truth. Sync that
doesn't corrupt your tree when two devices edit offline.
Inspired by [Roam Research](https://roamresearch.com) and [Logseq](https://logseq.com).
Tree CRDT sync ([Kleppmann et al. 2022][paper]), per-device append-only op log, IDs in a sidecar so the `.md` you see is the `.md` you wrote.
- **Why outl?** → [outl.app/docs/why-outl.html](https://outl.app/docs/why-outl.html)
- **Sync, done right:** → [outl.app/docs/sync.html](https://outl.app/docs/sync.html)
- **CRDT walkthrough:** → [outl.app/docs/crdt.html](https://outl.app/docs/crdt.html)
[paper]: https://martin.kleppmann.com/papers/move-op.pdf
## Install
```bash
# macOS / Linux via Homebrew (beta channel — every push to main)
brew tap avelino/outl https://github.com/avelino/outl
brew trust avelino/outl # one-time, third-party tap
brew install outl-beta # TUI/CLI/MCP
brew install --cask outl-desktop-beta # GUI
```
iOS beta on TestFlight: [join here](https://testflight.apple.com/join/P2GdWAMd). Point the TUI at the same iCloud Drive container _(`/Documents/`)_ and both clients share a workspace.
- **From source / channels:** → [getting started](https://outl.app/docs/getting-started.html), [homebrew](https://outl.app/docs/homebrew.html)
## Quick start
```bash
outl init ~/notes # scaffold a workspace
outl --workspace ~/notes # opens the TUI on today's journal
```
Press `?` for keymap, `:` for the command palette, `Ctrl+P` to fuzzy-jump.
- [Tutorial (15 min)](https://outl.app/docs/tutorial.html)
- [TUI manual](https://outl.app/docs/tui.html)
- [CLI reference](https://outl.app/docs/cli.html)
- [Markdown dialect](https://outl.app/docs/markdown-format.html)
- [Shortcuts](https://outl.app/docs/shortcuts.html)
## Coming from Logseq or Roam?
```bash
outl import logseq ~/path/to/logseq-graph ~/notes
outl import roam ~/Downloads/backup.json ~/notes
```
The importer strips `id::` lines, resolves `((uid))` block refs to page links, slugifies filenames, seeds the sidecars. Anything it can't resolve stays as `((unresolved:UID))` for manual triage.
## Contributing
- [Developer setup](https://outl.app/docs/development.html)
- [Contributing guide](https://outl.app/docs/contributing.html)
- [Architecture](https://outl.app/docs/architecture.html)
- [Roadmap](https://github.com/users/avelino/projects/2/views/1) — where the project is going
## Background reading
The engineering decisions behind outl on [avelino.run](https://avelino.run):
- **[File sync isn't trivial](https://avelino.run/file-sync-isnt-trivial/)** — why concurrent file moves are a distributed-systems problem that Dropbox and Google Drive still get wrong.
- **[From paper to outliner](https://avelino.run/from-paper-to-outliner/)** — the gap between "the CRDT converges" and "the app ships": projections, content-addressable reconciliation, surviving iCloud's lazy materialisation.
## License
[MIT](LICENSE).