Introduces dodder, a distributed zettelkasten and content-addressable blob store. Covers core concepts (zettels, object IDs, tags, types, blobs, the store), installation via Nix, a first-steps walkthrough including repository initialization and zettel creation, and the mental model for working with dodder. Activated when a user is new to dodder, asks what dodder is, wants to get started, install, set up, or learn how dodder works.
Dodder is a distributed zettelkasten written in Go. It functions as a content-addressable blob store with automatic two-part IDs, flat organization, and full version tracking. Think of it as Git for knowledge: every piece of content is stored by its hash, every change is recorded in an append-only inventory list, and the working copy on disk is just a checkout of what lives in the store.
Unlike traditional note-taking tools that impose folder hierarchies, dodder
keeps everything flat. Organization comes from tags and types rather than
directory structure. Each note (called a "zettel") receives an automatically
assigned human-friendly identifier composed of two parts drawn from
user-supplied word lists, producing IDs like one/uno or red/apple.
Two user-facing binaries exist: dodder and der (same tool, shorter name). A
third binary, madder, is low-level and intended for internal or advanced
repository operations. All three share the same Go module and source tree.
The .dodder/ directory is the store, analogous to .git/ in a Git
repository. It holds all blob content (by SHA hash), inventory lists (the
authoritative version history), configuration, and derived indexes.
| Concept | Description |
|---|---|
| Zettel | A note or blob with an automatically assigned ID. The primary content type. |
| Object ID | Two-part human-friendly identifier (e.g., one/uno) drawn from user-supplied yin and yang word lists. |
| Tag | Organizational label attached to zettels. Plain (project), dependent (-dependency), or virtual (%computed). |
| Type | Content format definition prefixed with ! (e.g., !md, !txt, !toml-config-v2). Types are versioned. |
| Blob | Raw content stored by its SHA hash (blake2b-256). Same content always produces the same hash. |
| Store | The .dodder/ directory. Holds blobs, inventory lists, configuration, and indexes. Like .git/. |
Dodder is built and distributed through Nix.
Clone the repository and build with Nix:
nix build
This produces debug and release binaries. Alternatively, enter the development
shell to get dodder (and the der alias) on the PATH:
nix develop
From within the development shell or a configured environment:
just build
This compiles debug binaries to go/build/debug/ and release binaries to
go/build/release/.
dodder --help
Or equivalently:
der --help
Both names invoke the same binary.
Every dodder repository requires two word lists -- called "yin" and "yang" --
that dodder combines to generate unique zettel IDs. Supply them during init:
dodder init -yin <(echo -e "one\ntwo\nthree") \
-yang <(echo -e "uno\ndos\ntres") my-repo
This creates a my-repo/ directory containing a .dodder/ store. The yin list
(one, two, three) and the yang list (uno, dos, tres) define the ID
space. Dodder assigns IDs by combining entries from these lists: the first
zettel gets one/uno, the second gets one/dos, and so on. The cross-product
of the two lists determines how many unique IDs are available before the lists
need to be extended.
Change into the new repository before continuing:
cd my-repo
Create a new empty zettel without opening an editor:
dodder new -edit=false
Dodder assigns the next available ID from the yin/yang cross-product, stores the empty blob, and records the new object in its inventory list. Output shows the assigned ID and metadata.
To preview which IDs dodder will assign next, use:
dodder peek-zettel-ids
This displays the upcoming IDs from the yin/yang combination without creating anything.
Display all zettels in the repository:
dodder show :z
The :z query selects the zettel genre. Output shows each zettel's ID, type,
tags, and blob hash in a log-style format. For a single zettel, pass its ID