slip captures a thought into a zettel with as little ceremony as possible, then writes it as markdown a machine can read.
A zettelkasten is a slip box, so the tool is a slip.
Capture is the whole of v1.
Reading, linking, and searching are handled by zk, which slip passes through to.
nix run github:unmango/slipThe packaged binary carries zk on its PATH.
For a build with no zk in its closure:
nix build .#slip-standaloneslip # opens an editor
slip capture a thought worth keeping # captures the arguments
pbpaste | slip # captures stdin
slip < notes.md # sameslip prints only the path it wrote, so it composes:
$EDITOR "$(slip)"In the editor:
| Key | Action |
|---|---|
ctrl+s, ctrl+d |
save and exit |
ctrl+c, esc |
discard and exit |
enter |
newline |
Enter inserts a newline. Saving is a deliberate chord, because a capture tool that saves halfway through a thought is worse than no capture tool.
The first of these that is set wins:
--dir$ZK_DIR$ZK_NOTEBOOK_DIR, the same override zk itself honours- the zk notebook found by walking up from the working directory
$XDG_DATA_HOME/zettelkasten, else~/.local/share/zettelkasten
Point ZK_DIR at a git repository or a synced folder.
The XDG path is a defensible default, not where a corpus you care about should live.
---
zettel_id: "202609081412"
title: The thing I was thinking about
note_type: NOTE_TYPE_FLEETING
format: CONTENT_FORMAT_MARKDOWN
create_time: 2026-09-08T14:12:33-05:00
---
The thing I was thinking about
and the rest of what I typed, verbatim.The body is stored exactly as typed. The title is derived from the first line rather than cut out of it.
zettel_id is a local-time timestamp to the minute, and it is also the filename.
Two thoughts captured in the same minute get a, b, and so on appended, in both places at once.
Frontmatter keys are the field names from unmango/apis, under proto/unmango/zettelkasten/, which is the schema of record.
Enum values are the full proto names, so the file is close to protojson and the conversion is mechanical.
Output-only fields are deliberately absent: uid is server-assigned, and the link and word counts are computed from the corpus.
slip runs any command it does not implement as zk, so one binary covers both:
slip list # zk list
slip edit # zk edit
slip lsp # zk lspNotes slip writes are already valid zk notes.
zk keeps frontmatter keys it does not recognise verbatim, so zettel_id, note_type, and format survive in its metadata map, and it reindexes on every invocation, so a note appears without running zk index.
One setting makes zk read our creation timestamp rather than the file's mtime:
slip initThat appends the following to .zk/config.toml, and reports what to add by hand if the table already exists:
[format.markdown.frontmatter]
creation-date-key = "create_time"With that, zk list --format jsonl gives back titles, tags parsed from the body, links, word counts, and our own metadata intact.
slip is MIT.
zk is GPL-3.0, and is only ever invoked as a separate process, never linked or copied.