The .calnote format
What is inside a notebook, and how to read one without DunneNote.
A notebook is a directory, and its contents are documented so that a third party
can read one using nothing but standard tools — sqlite3, jq and
sha256sum. This is deliberate. A note-taking application that can hold your
work hostage is not much better than no note-taking application.
Layout
MyResearch.calnote/
├── format.json self-describing manifest
├── notebook.db SQLite database — pages, canvases, content
├── blobs/
│ └── sha256/
│ └── aa/bb/aabbcc….bin content-addressed binary store
├── attachments/
│ └── pdf/report.pdf user-facing linked files
└── .state/ transient UI state — safe to delete
The manifest
format.json names the format version the notebook is written in and carries
the notebook’s stable identity. That identity is what lets a link from another
notebook keep resolving after this one is moved or renamed.
The database
notebook.db is an ordinary SQLite database. Pages, canvases, grid rows and
columns, calendar events, links and the search index are all tables in it. You
can open it with the sqlite3 command-line tool and look around.
Writes happen inside transactions, so an interrupted session leaves a consistent
database rather than a partial one. SQLite’s own write-ahead log may leave
notebook.db-wal and notebook.db-shm files behind after an unclean shutdown;
they are normal, and SQLite’s recovery handles them.
Blobs and attachments
Images and other embedded binary content live in blobs/, addressed by the
SHA-256 hash of their contents and sharded into subdirectories by the first four
hex characters of that hash. Storing the same image on ten pages stores it once.
attachments/ holds files you have linked rather than embedded, under their own
sanitised names. Unlike the transient files, attachments/ is durable
content: any backup or sync tool must copy it alongside notebook.db and
blobs/, or the notebook arrives incomplete.
Versioning and forward compatibility
The format carries a version, and the rules for what an older reader must do when it meets something new are written down rather than left to chance. An older version encountering a container kind it does not recognise must show an inert placeholder and — critically — must not delete it. Opening a newer notebook in an older build is allowed to lose display fidelity. It is not allowed to lose data.
Locking
A .calnote.lock file marks a notebook as open. It is advisory: it stops two
DunneNote instances from writing to the same notebook at once, which is a
different problem from stopping you copying the folder.