Files
verdande/docs/notes.md
T
Kristian WindandClaude Opus 5 c8d67dd07d
CI / OpenAPI is valid (push) Successful in 38s
CI / Go (push) Failing after 18m36s
CI / End-to-end (push) Failing after 18m54s
CI / Docker image builds (push) Successful in 7m27s
Documentation / build (push) Failing after 7m7s
Documentation / deploy (push) Skipped
Release / Build and publish image (push) Failing after 9m59s
feat(notes): notekroppe forsegles, og søgningen åbner dem selv
Grunden er den samme som for postkassers kodeord og OAuth-tokens: en
sikkerhedskopi kan hentes gennem fladen, og indtil nu bar hver eneste af dem hver
eneste note i klartekst. Noter er dér, folk skriver kodeord og API-nøgler ned — det
ved vi, fordi der ligger sådan nogle i den installation, det her blev bygget til.

**Både titel og krop.** Titlen *er* første linje, så en forseglet krop ved siden af
en læselig titel ville efterlade begyndelsen af hver eneste note i klartekst.

**Og de to kopier gik med.** Det var derfor det ikke var gjort for længe siden:
`notes_fts` indekserede kroppen, og den genererede `fold`-søjle bar en
translittereret kopi af den. At forsegle kroppen og lade dem stå ville have været
teater — en angriber med filen kunne læse hver note ud af indekset. Begge er væk,
og tabellen er bygget om, fordi SQLite ikke kan fjerne en genereret søjle på plads.

**Søgningen åbner teksten i Go.** Målt frem for gættet: tolv hundrede noter à 3,3 kB
koster 33–43 ms pr. søgning, og fladen venter 250 ms efter sidste tastetryk, før den
spørger — så det ligger inde i pausen. Tiden går ikke med at åbne konvolutterne;
fire megabyte AES-GCM er få millisekunder. Den går med at folde og småskrive hver
krop, og det er lineært: regn med omtrent 350 ms ved ti tusind noter. Dér er det den
forkerte form, og svaret er da et nøglet indeks — hvis pris er, at præfiks- og
delordssøgningen falder bort.

bm25 forsvandt med indekset. Det, der er beholdt, er den halvdel, der betød mest:
titlen vejer ti gange kroppen. Med et loft pr. ord, så en note, der nævner ordet
fyrre gange, ikke slår en, der nævner alle ordene én gang — det er den fejl, en ren
optælling laver, og den er let at lave og svær at få øje på.

**En bagudfyldning ved opstart, ikke efterhånden.** Instansens indstillinger
forsegles, når de skrives, og det er rigtigt for dem. For et arkiv på tolv hundrede
importerede noter, som ingen redigerer igen, betyder "når de skrives" aldrig — og
det er præcis dét arkiv, der er værd at forsegle. Idempotent, så den koster et
gennemløb af id'er hver anden gang, og `updated_at` røres ikke: at lukke en
konvolut om en note er ikke at skrive den, og et stempel med i aften ville kaste
den rækkefølge væk, importen brugte en dag på at hente hjem.

En fejl under bagudfyldningen stopper opstarten. Halvdelen af noterne lukket er
ikke i stykker — `v1:`-præfikset gør, at begge slags kan læses — men en instans,
der starter og lader som om den beskytter noget, den ikke gør, er værre end en, der
siger fra.

Hvad der stadig ligger åbent, står i SECURITY.md og på dokumentationssiden:
opgavetitler, beskrivelser, kommentarer, og `note_links`, som røber hvilke
projekter og opgaver en note peger på, men ikke hvad der står.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:40:55 +02:00

6.3 KiB

Notes

Markdown in, Markdown out. Nothing in between.

A note is written as rich text — headings, lists, quotations, code — and stored as the Markdown it looks like. That is not a detail about the database: it is what makes a note yours. Export gives you the same text you were looking at, and on the day this program is gone, every note opens in any editor.

Writing

Start typing. The first line is the title — always, and re-derived every time you save, so a note you rewrite the top of is called by what it now says rather than by what it once did.

Style Written as Shortcut
Title # at the start of the line pick it in Aa
Heading ##
Subheading ###
Quote >
Code block three backticks, with an optional language
Bullet and numbered lists - and 1. , nested with Tab
Bold two asterisks around the words ⌘B
Italic one asterisk around the words ⌘I
Underline a u tag around the words ⌘U
Strikethrough two tildes around the words
Code one backtick around the words

Every line becomes exactly one block. Markdown would normally join consecutive lines into a paragraph; here it does not, because you typed a line and expect to get it back.

Press the source button in the toolbar to see the note as the file it is. Edits there are edits to the note.

Pictures

Paste one, or drag it onto the note. It is stored with the note and travels with it in an export. Every raster format works; SVG is stored but not shown inline — it is a document that can run script, and a note is not a place to run somebody else's.

Code blocks

A fenced block is coloured for the language it names, and guesses when it does not. Each one carries a Copy button in its corner.

Linking

Three kinds of reference, parsed out of the text as you write it:

You type It means
#Firma The project — the same #Firma quick add understands, not a second kind of tag
[[Møde med Anders]] Another note, by its title
A pasted task link That task

Links are an index over the text, rebuilt every time the note is saved. The text stays the truth.

The interesting direction is backwards. A task shows what has been written about it, and a project shows the notes filed in it — without anybody having to remember to attach anything. Write #Firma in a note and it appears on that project's page.

A link to something that has been deleted reads as a dead link. It is a fact about the note, and it should not vanish because something it mentioned did.

Filing and sharing

A note belongs to a project, or to nobody.

  • In a project: everybody who can read the project can read the note, and an editor can change it. Choose it under Share in at the foot of the note.
  • In no project: yours, and nobody else's.

There is no folder tree. Projects, groups and labels already exist, are already shared and already have roles — a second hierarchy to file things in is one too many, and a note in a project inherits every one of those answers.

Finding

The list groups itself: favourites first, then Today, Yesterday, This week, and then by month. Sort by created, last touched or name, and the grouping follows — sorted by name it groups by first letter instead.

Search finds a word wherever it is:

  • gron finds grøn. Unicode does not consider ø an accented o, so stripping diacritics is not enough for Danish. A folded copy of every note is indexed beside the original.
  • inventory finds keepInventory. An index is built of whole words, and a decade of notes is full of commands and identifiers that are several words in one. When the index has nothing, the text itself is read.
  • Results are ranked by how well they match, with the title weighted ten times the body. A word in the title is what the note is about; the same word once in a long body is a mention.

Putting notes away

Two different things, and the difference matters:

  • Archive — this is finished and I do not want to read past it. It stays findable and comes back whenever you ask.
  • Trash — this was a mistake. It is gone in thirty days.

Select several with ⌘-click and shift-click to archive or delete them together.

In and out

Export gives you a zip of Markdown files, one per note, with the dates in a YAML front matter block that Obsidian and Bear also read:

---
created: 2019-04-02T10:15:00Z
modified: 2024-11-30T21:04:00Z
---

# Møde med Anders

Han vil gerne have kaffe hver uge.

Import takes the same shape back. Pictures travel in the zip beside the notes, and the links in the text are relative — so a note and its pictures must be in the same archive, which matters when a large export is split into parts.

Two things to know before importing:

  • Nothing is merged. Every run creates new notes. Importing the same archive twice gives you two of everything.
  • The dates are read from the front matter. Without them, a move of a thousand notes is a pile that was all written the same evening — and the order they were written in is half of what an archive is.

From Apple Notes

tools/apple-notes-til-markdown.py in the repository exports Apple Notes to exactly this shape, front matter and pictures included. It is macOS-only, because Apple Notes is.

For a large library, tools/pak-noter-i-portioner.py splits the export into uploadable parts, keeping each note with its own pictures.

What is encrypted, and what is not

A note's title and body are sealed in the database, under a key kept in a file beside it rather than inside it. A backup you download does not carry the key, so it does not carry your notes.

Both halves, not only the body: a note's title is its first line, so a readable title beside a sealed body would leave the beginning of every note in the open.

What is still in the clear: task titles, descriptions and comments — and the list of what a note links to, so a stolen file shows which projects and tasks a note points at, though not what it says.

Sealing the notes cost the search its index — a full-text index over a note's body is another copy of that body — so search reads the notes themselves now. At twelve hundred notes that is about forty milliseconds, and the box waits a quarter of a second after you stop typing, so you will not see it.