migration

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.

01
from logseq

strip the
id:: poison.

command
$ 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.md becomes my-page-title.md with title:: My Page Title preserved at the top.
  • seeds the .foo.outl JSON sidecars so the CRDT op log has stable IDs from day one. Your original Logseq graph is read-only — nothing modified there.

before / after

logseq · pages/launch.md
- ## ship 0.1.0 id:: 6624a82c-3b11-4d44-9d3f-d9c7e8f0a1b3 collapsed:: false - post #launch id:: 6624a82c-9a22-4e55-be40-eaf8d9015c2a - binaries id:: 6624a82c-7c33-4f66-cf51-f0a9eb126d3b
outl · launch.md + .launch.outl sidecar
title:: launch - ship 0.1.0 - post #launch - binaries
02
from roam research

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:

command
$ 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
03
from obsidian

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.

command
$ 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-id references — converted to outl block IDs on first outl doctor run.
  • ~ 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.
after the import

what to check.

unresolved refs

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.

integrity

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.

reversibility

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.

version control

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.