Log the things you care about without leaving the terminal.
A config-driven capture tool that writes structured Markdown into your Obsidian vault, or any folder you point it at.
We don't write enough about the things that matter to us. Not because we don't want to - because the friction kills the impulse before we act on it.
Pour exists to close that gap. One command, a few keystrokes, back to what you were doing. If we can capture the thought, the meaning isn't lost to time. A moment, a cup, a song, a passing thought - permanent in your hands.
Write more... pour.
pour coffee # log a brew
pour me # capture a thought into your daily note
pour todo # add a task
pour note # create a fleeting note
pour # open the dashboard
You describe a module once in ~/.pour/config.toml. After that, pour coffee opens a form, you fill it in, and a note lands in your vault:
---
date: 2026-10-02
bean: "[[Ethiopia Guji]]"
method: V60
ratio: "1:16"
---
Blueberry up front, tea-like finish. Grind one step finer next time.That's Coffee/2026-10-02-Ethiopia Guji.md, written by pour from a short block of config. Fields become YAML frontmatter, so Dataview, Bases, or a ten-line script can query a year of brews later. The tasting notes go to the body. The bean list in the form came from the files in Coffee/Beans/, and the bean is written as a wikilink back to its note.
Pour knows nothing about coffee. Everything it knows lives in your config, so the same engine logs a set list, a book, a workout, or the thing you thought of in the shower.
Some captures don't need a form at all:
$ pour habit water 16
water: 64/96 oz · ✓ 20260805.mdPrebuilt binary (recommended)
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/mads-jm/pour/main/install.sh | sh# Windows (PowerShell)
irm https://raw.githubusercontent.com/mads-jm/pour/main/install.ps1 | iexThe installer puts pour at ~/.local/bin/pour (Unix) or %LOCALAPPDATA%\Programs\pour\pour.exe (Windows), adds it to your PATH, and drops the resources/ folder next to it (sample configs, presets, an AI-agent reference). To pin a version, run curl ... | sh -s -- 0.2.2 on Unix, or set $env:POUR_VERSION = '0.2.2' before the irm line on Windows.
With cargo
cargo install --git https://github.com/mads-jm/pour
# or, from a clone: cargo build --release → target/release/pourNeeds a Rust toolchain with 2024 edition support.
Platform notes
- Linux needs ALSA. Pour links
libasound.so.2for the completion sound, so the binary won't start without it, even with sound off. Desktop distros ship it. On a minimal server or container image, installlibasound2(libasound2t64on Ubuntu 24.04+) oralsa-libon Fedora and Arch. Building from source also needspkg-configand the headers:libasound2-devon Debian/Ubuntu,alsa-lib-develon Fedora. - macOS binaries are Apple Silicon only and need macOS 14.2 or later, because
cpal(the audio library) does. An older macOS refuses to launch the binary even with sound off. CI compiles every change for macOS, but nobody has run pour on a Mac yet. If you do, please open an issue and say how it went, good or bad. - Windows needs nothing extra.
pour initpour init walks you through creating ~/.pour/ with a config.toml, a secrets.toml, and a few example modules. Everything pour keeps lives in that folder. Set POUR_HOME to move it.
Or write the config by hand. Point it at your vault:
# ~/.pour/config.toml
config_version = "0.4.0"
[vault]
base_path = "/path/to/your/vault"Add a module:
[modules.todo]
mode = "append"
path = "Daily/%Y%m%d.md"
icon = "✅"
append_under_header = "### Tasks"
append_template = "- [ ] {{body}}"
append_shallow = true
[[modules.todo.fields]]
name = "body"
field_type = "text"
prompt = "Task"
required = true
target = "body"And run it:
pour todo| Mode | What it does |
|---|---|
create |
A new note per entry, with YAML frontmatter. A brew, a show, a book. |
append |
Adds a line under a header in a note that already exists, like your daily note. |
update |
Rewrites a few frontmatter properties on an existing note and leaves everything else alone. Pour never creates that note. Your template owns it. |
Which mode a thing belongs in is a design question with a real answer. Pour Types explains how to tell.
update mode is what makes habit tracking a one-liner. Give a module toggle and counter fields, then call it with a field name:
[modules.habit]
mode = "update"
path = "Daily/%Y%m%d.md"
icon = "🌱"
[[modules.habit.fields]]
name = "water"
field_type = "counter"
prompt = "Water"
unit = "oz" # display only, never written to YAML
goal = 96 # renders as 64/96 oz$ pour habit water 16 # a counter adds; =16 would set it
water: 64/96 oz · ✓ 20260805.md
$ pour habit # no field → the usual formIf the daily note is missing a key, pour adds it and tells you your template is stale, so the capture still lands. If the note itself is missing, pour fails loudly instead of making one up.
Each of these is optional and documented in the field and config reference.
- Eight field types.
text,number,textarea,static_select,dynamic_select(options read from a vault folder),composite_array(repeatable rows, like the stages of a pour-over recipe),toggle, andcounter. - Conditional fields.
show_whenhides a field until another field has a given value. Hidden fields skip validation and stay out of the note. - New notes from inside the form. Type a bean that doesn't exist yet and
allow_createopens a sub-form, writesCoffee/Beans/<name>.mdfrom a template, and carries on with your brew. - Presets.
Ctrl+Ssaves the current form,←/→cycles through saved ones. Setpreset_axesandpopens a picker instead, drilling down by method, then bean. Composite fields keep their own presets, so a favorite recipe can be replayed on its own. - Paths that fill themselves in. strftime tokens,
{{field_name}}, and{{slug}}in the filename:inbox/%Y%m%d-%H%M%S{{slug}}.md. - Fixed frontmatter per module.
tags,cssclasses, a customdateformat. - Captures outside the vault. A module can set its own
base_pathand write to a notes repo or anywhere else on disk. - Your past captures beside the form. The TUI shows the earlier captures from the same module that look most like the one you're filling in, three by default, one column each, with every row beside its form field. A
·means the capture had what the form holds now, so the differences stand out.[modules.<name>.priors]decides what counts as similar. Without the block, pour scores on every select andwikilinkfield.Ctrl+Rcollapses the panel. - Post-write hooks.
post_write_shellruns a command after a save, for example to commit and push a note. - A completion sound.
[sound] on_save = trueplays one short tone when a capture saves in the TUI. It's off by default, and when it's off pour never opens an audio device.
Warning
post_write_shell runs arbitrary commands from your config. Only pour-generated tokens ({{base_path}}, {{rel_path}}, {{abs_path}}, {{slug}}, {{slug_or_time}}) interpolate. A {{field_name}} in a hook is rejected at load, so nothing you type into a form can reach a shell. Hooks don't run for phone captures unless you set post_write_shell_on_serve = true. And a hook that pushes to a public repo makes a bad capture public history.
Every key the TUI listens to is in keyboard shortcuts. For a walkthrough that maps a real vault onto a config, see Adapting Pour to Your Vault.
The terminal is the right tool at your desk and the wrong one at the kitchen counter. pour serve runs the same engine behind a small web app on your LAN:
pour serve # port 8421
pour serve --port 9000It prints a QR code. Scan it, add the page to your home screen, and every module is a tile you can tap. The phone writes exactly the same Markdown the terminal does. It also queues captures while you're offline, shows a 90-day history heatmap, and lets you manage presets. From the dashboard, press s to start the server in place, and Ctrl+C to drop back into the TUI.
It's LAN-only, guarded by a token pour generates and keeps in secrets.toml. For access away from home, Tailscale or ZeroTier work well. Add mobile_visible = false to keep a module off the phone entirely. Token rotation, logging, and home-screen icons are covered in Capturing From Your Phone.
No. Pour writes plain Markdown with YAML frontmatter to a folder. Obsidian reads that well, and so do Logseq, Hugo, Zola, grep, and anything else that reads text files.
Without Obsidian, set base_path to any folder and skip the API settings. Pour writes straight to disk.
With Obsidian and the Local REST API plugin, pour writes through the API instead. That gets you faster writes, richer folder listings for dropdowns, and plugin commands like Templater fired after a note is created. Put the key in ~/.pour/secrets.toml or POUR_API_KEY:
# ~/.pour/secrets.toml (keep this out of version control)
api_key = "your-key-here"If the API isn't reachable, pour falls back to writing files directly, and it reports which one it used on every save. Dropdowns work the same way. Pour lists the source folder through whichever path is live and caches the result, so if the vault can't be read, the last known list still shows up. With no list at all, the field takes free text. More in Pour Without Obsidian.
Rust, ratatui and crossterm for the TUI, axum for pour serve, reqwest for the Obsidian API.
Full dependency list
| Area | Crate |
|---|---|
| TUI | ratatui + crossterm |
| HTTP server | axum + tower + tower-http + tokio |
| HTTP client | reqwest |
| Static assets | rust-embed (PWA shell embedded in the binary at compile time) |
| Serialization | serde + toml + toml_edit + serde_json |
| Time | chrono |
| URL encoding | percent-encoding (vault paths with spaces in REST API URLs) |
| QR codes | qrcode (terminal QR code for pour serve) |
| LAN address | local-ip-address (the LAN-routable address printed at startup) |
| Identifiers | uuid (idempotency keys, capture IDs) |
| Constant-time compare | subtle (bearer-token comparison) |
| Logging | tracing + tracing-subscriber (structured server logs, POUR_LOG) |
| Unicode width | unicode-width (terminal cursor and column accounting) |
| Filesystem paths | dirs (finds the home directory for ~/.pour/) |
| Errors | anyhow |
| Shell open | open (the o key on the summary screen opens the note in Obsidian) |
| Audio | cpal plays the completion sound and links ALSA on Linux. alsa (Linux only) keeps alsa-lib's diagnostics off the TUI's terminal |
cargo build
cargo test
cargo clippy
cargo fmt -- --checkTests live in tests/, mirroring src/. CONTRIBUTING.md covers branching, commit style, and the just recipes for working against a live config. The design spec, architecture notes, and decision records live in pour - docs/, which is itself an Obsidian vault. Open it in Obsidian and the graph view shows how it fits together.
MIT. See LICENSE.
▽
