come over.
bring everything.
outl ships native importers for the three places your notes probably live. The migration is one command and reversible — your original files are never touched.
strip the
id::
poison.
outl import logseq ~/path/to/logseq-graph ~/notes what the importer does
- → strips every
id::line from your .md. Your file becomes the file you actually wrote. - → resolves
((uid))block references to[[page-name]]or((readable-id))where the destination is unambiguous. - → slugifies filenames —
My Page Title.mdbecomesmy-page-title.mdwithtitle:: My Page Titlepreserved at the top. - → seeds the
.foo.outlJSON sidecars so the CRDT op log has stable IDs from day one. Your original Logseq graph is read-only — nothing modified there.
before / after
json out,
your notes back.
Roam stores everything in a server-side JSON tree. Export from Roam (Settings → Export All → JSON), then point outl at it:
outl import roam ~/Downloads/roam-export.json ~/notes what gets translated
| roam concept | outl equivalent |
|---|---|
| Page | pages/<slug>.md with title:: property |
| Block | Markdown bullet, optionally with stable ID in sidecar |
| Daily Note | journal/<YYYY-MM-DD>.md |
[[Page]] ref | [[page]] ref (resolved to slug) |
((uid)) block ref | ((stable-id)) in markdown · ID in sidecar |
#tag | #tag (unchanged) |
Block property key:: | Block property key:: (unchanged) |
{{[[TODO]]}} | TODO with property status:: todo |
your vault is
already there.
Obsidian's vault is plain markdown. outl can read it directly — no
conversion needed. Just point
outl init at the vault and the
first run seeds sidecars for blocks that already exist.
outl init ~/ObsidianVault what carries over
- ✓
[[wiki-links]]— unchanged. - ✓
#tags— unchanged. - ✓ YAML frontmatter — preserved (outl reads it, just doesn't write delimiters).
- ✓ Daily notes plugin pattern (
YYYY-MM-DD.md) — auto-detected as journal. - ~
^block-idreferences — converted to outl block IDs on firstoutl doctorrun. - ~ Canvas (
.canvas) files — preserved as-is. Not yet rendered (roadmap phase 5). - · Plugin-specific syntax (Dataview queries, Templater) — left as-is. They won't break, but outl doesn't interpret them yet.
what to check.
Any block reference the importer couldn't pin down stays as
((unresolved:UID)). Run outl reconcile for an
interactive triage UI, or grep them out manually.
outl doctor walks the op
log + sidecars + .md files, reports any orphan blocks, cycle
attempts (impossible, but theoretically), or sidecar/markdown
drift. Run it once after import to baseline.
outl never touches your source. Your Logseq graph / Roam JSON /
Obsidian vault are read-only inputs. The destination directory
is a fresh outl workspace. Worst case: rm -rf ~/notes and try again with different flags.
The destination is a flat folder of .md + .outl sidecars + .outl/log.db. Drop a git init on it and commit.
Now your notes have history.
ready when
you are.
5 minutes, one command, your originals untouched.