Skip to content

Export and restore walkthrough

This walkthrough is a founder-produced proof asset for beachhead users who want to verify Marrow’s portability claim before trusting it with real data. It walks export → bundle inspection → restore, and links to the authoritative restore guarantee scope table.

In the bundleNot in v4 (see scope table)
Node tree, revisions, attachmentsComments
node_properties in manifest.jsonShare links
links.json backlink indexFolder view definitions
Trash state (with --include-trash)Stars, Inbox, watches (user-scoped)

Full scope: What round-trips today · Export bundle format.

  • Marrow running locally (Quickstart)
  • A workspace with at least one page and an attachment (optional but recommended)
  • jq installed (brew install jq on macOS)

From the api/ directory with venv active and DATABASE_URL pointing at your dev database:

Terminal window
cd api
source .venv/bin/activate
marrow export --workspace <workspace-slug> --output /tmp/marrow-demo.zip

Replace <workspace-slug> with the slug shown in the UI or returned by GET /api/workspaces/. The CLI writes a timestamped zip; --output sets the path.

To include soft-deleted nodes (trash):

Terminal window
marrow export --workspace <workspace-slug> --include-trash --output /tmp/marrow-demo-with-trash.zip

List top-level entries:

Terminal window
unzip -l /tmp/marrow-demo.zip

You should see:

manifest.json
nodes/
revisions/ # omitted in --slim exports
assets/
links.json

Read the manifest (schema version, node tree, properties):

Terminal window
unzip -p /tmp/marrow-demo.zip manifest.json | jq '.schema_version, .workspace, (.nodes | length), (.node_properties | length)'

Peek at a page’s canonical JSON (BlockNote) and human Markdown:

Terminal window
# Pick a page node id from manifest.json → .nodes[]
unzip -p /tmp/marrow-demo.zip "nodes/<page-node-id>.json" | jq '.[0].type'
unzip -p /tmp/marrow-demo.zip "nodes/<page-node-id>.md" | head

Inspect internal links:

Terminal window
unzip -p /tmp/marrow-demo.zip links.json | jq '.internal_links | length'

Attachments land under assets/ named by attachment ID. Folder nodes appear only in manifest.json — they have no files under nodes/.

See Export bundle format for the full layout reference.

Restore proves the bundle is self-contained. Use a disposable database so you do not overwrite live data.

Terminal window
# Stop API first if it holds connections.
# `-v` drops the Postgres volume so the database is genuinely empty —
# without it the old rows survive and restore collides on existing slugs.
docker compose down -v
docker compose up -d
cd api && alembic upgrade head
marrow restore /tmp/marrow-demo.zip

The CLI prints the restored workspace slug. Open the web app and confirm pages, folders, attachments, and properties match.

Terminal window
createdb marrow_restore_test # requires local Postgres client
DATABASE_URL=postgresql://marrow:marrow@localhost:5433/marrow_restore_test \
alembic upgrade head
DATABASE_URL=postgresql://marrow:marrow@localhost:5433/marrow_restore_test \
marrow restore /tmp/marrow-demo.zip

After restore, spot-check:

  1. Tree shape — folders and pages in the same hierarchy; positions preserved.
  2. Page content — open a JSON-format page; BlockNote content should match pre-export.
  3. Properties — folder schema and page values on inherited properties.
  4. Backlinks — pages that linked to each other still show backlinks in the UI.
  5. Attachments — download an attachment from a restored page.

If anything differs, that is a critical bug against the restore guarantee — please file an issue.

Do not expect these to survive export→restore in v4:

  • Comments on pages — planned for bundle v5
  • Share links — planned for bundle v5
  • Folder view definitions (table/board/list configs) — planned for bundle v5
  • Stars, Inbox notifications, watches — user-scoped; never exported by design

Using those features in the UI is fine; they simply are not in the portable bundle yet. See the scope table.