Skip to content
Getting started with Zettel

Getting started with Zettel

Initialize

Run this command from the project root:

zettel init

The command makes the config file zettel.yml and the note directory .zettel/.

Create a note

zettel note create "Connection pooling causes stale reads" \
  -t debugging,postgres -p agent:summary

Zettel prints the note ID, for example a3f2-connection-pooling-causes-stale-reads. Most commands accept the 4-character prefix (a3f2) instead of the full ID.

The -p flag sets the note’s default provenance: who produced the text. An agent passes agent:summary, agent:index, or agent:inference. A person passes human or human:<name>. Without the flag the provenance is unknown, which is the weakest thing a later reader can be told about a span.

Add a body on the command line:

zettel note create "Why we chose YAML over TOML" \
  -t architecture \
  -b "YAML supports anchors and aliases for config reuse. TOML can't."

View and search

# List all notes
zettel note list

# Filter by tag
zettel note list --tag postgres

# Filter by provenance: human, agent, agent:inference, citation,
# reviewed, unknown. A comma list matches any.
zettel note list --provenance human
zettel read --provenance human,citation,reviewed

# List the notes with unreviewed agent content
zettel note list --unreviewed

# Show one note
zettel note show a3f2-connection-pooling-causes-stale-reads

# Search the full text. The pattern is a regex and matches case;
# (?i) at the front turns that off.
zettel search "Connection pool"
zettel search "(?i)stale reads"

# Show all note content; pipe it to other tools
zettel read
zettel read --tag debugging

Link notes

Notes connect to each other through the links frontmatter field. Add a link when you create the note:

zettel note create "Workaround: force new connection" \
  -t postgres \
  -l a3f2-connection-pooling-causes-stale-reads

Add a link to a note that already exists:

zettel note edit b7c1-workaround-force-new-connection --add-link a3f2-connection-pooling-causes-stale-reads

Explore the graph

# Show the notes that link to this note
zettel backlinks a3f2-connection-pooling-causes-stale-reads

# Show a note and the linked notes within 2 hops
zettel context a3f2-connection-pooling-causes-stale-reads

# Traverse more hops
zettel context a3f2-connection-pooling-causes-stale-reads -d 4

# Show the notes with no links
zettel orphans

# Show the knowledge base statistics
zettel stats

Edit and maintain

# Change the title
zettel note edit a3f2-connection-pooling-causes-stale-reads --title "Connection pooling and stale reads"

# Add a tag
zettel note edit a3f2-connection-pooling-causes-stale-reads --add-tag production-incident

# Append to the body
zettel note edit a3f2-connection-pooling-causes-stale-reads --append "Confirmed: setting max_age=300 resolves this."

# Delete a note
zettel note delete a3f2-connection-pooling-causes-stale-reads

Mix provenance in one note

A <!-- prov ... --> marker overrides the note default for one section:

zettel note edit a3f2 --append '<!-- prov citation:b7c1 p=12 -->
> The pool serves a stale connection for up to max_age seconds.
<!-- /prov -->

<!-- prov agent:inference -->
The retry storm on 2026-08-02 probably started here.
<!-- /prov -->

<!-- prov human:cody -->
Confirmed with the infra team.
<!-- /prov -->'

A citation names its source: another note’s ID, or a src=<url> attribute for an external source. Text outside the markers keeps the note default.

The review workflow

  1. Agents create notes and mark spans with their provenance.
  2. A human lists the pending work: zettel note list --unreviewed.
  3. zettel note review <id> shows the numbered spans.
  4. The human approves what they stand behind: zettel note review <id> --approve all --reviewer <name>, or --approve 1,3 for single spans, by the numbers the listing shows. The approval writes a reviewed= stamp.
  5. A reader pulls trusted content with zettel read --provenance human,citation,reviewed.

Only a human runs --approve.

Migrate from the status model

Notes from before the provenance model carry a status: key. Convert them once:

zettel migrate

A permanent note becomes provenance: human. A draft note stays unknown. The status key is removed either way.