跳到正文
DEV Community· Rahul Anand·· 3 小時前AI 評分50

Papyra 將筆記存為 Markdown,移除容器後仍可讀取

A Note App You Can Delete

AI 導讀

Rahul Anand 介紹自託管筆記應用 Papyra,其筆記以 Markdown 檔案保存,停止容器並移除映像後仍可由 Obsidian、VS Code 或 grep 開啟。

正文

Every notes app has an export button. Few have a good answer to the real question: what's left when I stop using you? Usually it's a zip of HTML with the styling stripped. That's not "you can leave". That's a weekend.

So before any feature, Papyra got one rule: uninstalling it must leave you with what you'd have had anyway. That rule decides the whole architecture.

Diagram: the usual note app keeps the only copy in a database with an export button bolted on; Papyra keeps the only copy in .md files and treats SQLite and Lucene as rebuildable caches

The files are the truth

Every note is a Markdown file with a small YAML header:

cat of a note file: YAML frontmatter with id, title, tags, colour, then the Markdown body with a table

There is a SQLite database and a search index. Both are caches. The test of "cache" is whether you can throw it away, so there's an endpoint that does exactly that:

Terminal: the Lucene index is small; POST /api/system/rebuild-index returns the number rebuilt; searching for sourdough still finds the right notes
Throw the index away, ask for it back, lose nothing.

Before adding any column I ask: if I delete this table, can I rebuild it from the files? If not, it belongs in the file, or it's UI state that's allowed to get lost.

What "delete it" looks like

Stop the container. Remove the image. You have this:

ls and grep on the notes folder: plain .md files with readable names, found by grep

Obsidian opens it. VS Code opens it. grep reads it. Pictures sit next to it in media/, linked the way Obsidian links them.

What you lose is presentation and plumbing, never a sentence you wrote:

Diagram: what survives deletion (notes, tags, colours, pins, attachments) versus what stays behind with the app (history, card order, tag colours, share links, comments, sign-ins)

One honest exception: locked notes are encrypted on disk. That's the point of locking them.

head of a locked note: the YAML header is readable, the body is one long papyra-locked:v1 ciphertext line

So for locked notes the exit has one extra step: unlock your vault and use Export, which writes them out as plain Markdown in their own folder.

What it buys

Other tools can write to your notes, and Papyra keeps up:

GIF: a shell appends a line to groceries.md; the open To Do list gains the new item a moment later

What it costs

Diagram: five costs of files-as-truth: no multi-note transactions, crash-safe writes by hand, catching up on every boot, file names are user input, and sync tools' conflict copies

One deserves a closer look: catching up after downtime. The server was off; Syncthing wasn't. Notes arrived, changed, vanished. So at startup Papyra compares every file to its cache before it opens the port. Here's that, done for real:

Terminal: with Papyra stopped, notes are deleted, added and edited on disk; on restart the cold-boot log line reports what it reindexed and pruned, before

When it runs is the interesting part:

Diagram: with BackgroundService the port opens and the first search hits a half-updated index; with IHostedService.StartAsync the walk finishes before the port opens

ASP.NET Core's BackgroundService would run it while the port opens, so the first search could see stale results. Doing the work in IHostedService.StartAsync makes the host wait for it. Startup is slower; the first request is right.

And the log line says what it did. After an ordinary upgrade it reads 0 (re)indexed, 0 pruned, and you know nothing moved underneath you.

Was it worth it?

For notes, yes. Notes are small and independent, there's no cross-note rule worth a transaction, and what people fear is the ten-year risk. Plain files have the best ten-year record of any format.

If your data has foreign keys that must hold, or many writers that must merge, use a database. Files won't thank you.

The app should have to earn the next day. Not trap you into it.


I'm Rahul. I vibe-coded Papyra: what would have taken me a year or more to build by hand took a few months. It's GPLv3 and self-hosted: one Docker container, your notes as plain Markdown files.

GitHub · Live demo, no signup · Docs

來源:DEV Community · dev.to