# kastrup **The fast unified messaging hub. Written in Rust.** ![Rust](https://img.shields.io/badge/language-Rust-f74c00) ![License](https://img.shields.io/badge/license-Unlicense-green) ![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS-blue) ![Stay Amazing](https://img.shields.io/badge/Stay-Amazing-important) Unified terminal messaging client. All your email, chat, and feeds in one TUI. Built on [Crust](https://github.com/isene/crust). Feature clone of [Heathrow](https://github.com/isene/Heathrow) rewritten in Rust for speed and single-binary distribution. ## Screenshot ![Screenshot](img/screenshot.png) *Unified messaging: threaded RSS feeds (shown), mail, chat, and Workspace in one inbox.* ## Features - **Multi-source messaging**: Maildir email, RSS/Atom feeds, WeeChat/IRC, Messenger, Instagram - **4-pane TUI**: source/message list, message content, info bar, and status bar - **Threading**: flat, threaded, and folder-grouped message views - **Background sync**: automatic polling with configurable intervals per source - **Compose/Reply/Forward**: full email composition with editor integration - **Inline images**: Kitty protocol image display (V key) - **Folder browser**: hierarchical Maildir folder navigation (B key) - **Search**: substring and notmuch full-text search - **AI assistant**: draft, summarize, translate, ask (I key) - **AI triage** (z key): Claude reads the current message, optionally takes a free-text hint, and emits a JSON action plan (calendar events to [Tock](https://github.com/isene/tock) / todos to a [hyperlist](https://github.com/isene/hyperlist) at `~/.tasks/todo.hl`). Multi-pick preview before commit; rolling history via `:triage` - **Address book**: contact storage and lookup (@ key) - **Labels and tagging**: multi-select tagging, label management - **Customizable themes**: full 256-color theme editor with presets - **Per-view settings**: independent sort, thread mode, and section order per view - **SQLite database**: messages and metadata at `~/.kastrup/kastrup.db` ## Install Download the prebuilt binary from [Releases](https://github.com/isene/kastrup/releases), or build from source: ```bash cargo build --release cp target/release/kastrup ~/.local/bin/ ``` ## Key Bindings ### Navigation | Key | Action | |-----|--------| | j/k, Down/Up | Move cursor | | h, Left | Collapse current thread (threaded view) | | Space | Toggle thread collapse | | Home / End | First / last message | | PgDn / PgUp | Page down / up | | Enter | Open message (or expand section in threaded view) | | n / p | Next / previous unread | | J | Jump to date | | G | Cycle view mode (flat / threaded / folder) | ### Views | Key | Action | |-----|--------| | A | All messages | | N | New (unread) | | Ctrl-S | Sources management | | 0-9 | Custom views | | F1-F12 | Extended custom views | | F | Favorites browser | | L | Load more messages | | Ctrl-R | Refresh current view | | Ctrl-F | Edit filter | | K | Kill (close) view | ### Message Operations | Key | Action | |-----|--------| | R | Toggle read / unread | | M | Mark all as read | | `*` / `-` | Toggle star | | t / T | Tag message / tag all | | Ctrl-T | Tag by regex | | d | Mark for deletion | | `<` | Purge deleted | | u / U | Mark unseen | | Shift-Space | Mark browsed as read | ### Compose & Reply | Key | Action | |-----|--------| | r | Reply | | e | Reply in editor | | g | Reply-all | | f | Forward (then `i` inline / `a` attach) | | m | Compose new | | E | Edit draft | ### Attachments & External | Key | Action | |-----|--------| | v | View / save attachments | | V | Toggle inline image | | D | Download images to disk | | x | Open in external app | | X | Open HTML in browser | ### Search & AI | Key | Action | |-----|--------| | / | Search messages (notmuch + DB substring fallback) | | **S** | **`:search`** — natural-language query → claude translates to a `Filters` JSON spec → applied to the message list | | @ | Address book | | l | Label message | | s | File / save message | | `+` | Add to favorites | | I | AI assistant menu (draft / summarize / translate / ask) | | **c** | **`:claude PROMPT`** — pipe message + custom prompt to `claude -p`, response shown in the right pane | | **C** | **`:chat`** — suspend kastrup, open interactive `claude` with the current message snapshot as context | | **`:`** | **Colon prompt** — type any verb explicitly: `:claude PROMPT`, `:search QUERY`, `:chat`, `:triage`, `:q`/`:quit` | | Esc | Clear sticky search, return to current view | | **z** | **AI triage** — Claude reads the current message (+ optional hint) and proposes calendar events / hyperlist todos; multi-pick preview before committing to `~/.tock/incoming/` and `~/.tasks/todo.hl` | | Z | Open in Tock (regex date capture → calendar event) | | **`:triage`** | Show the most recent 20 triage decisions from `~/.kastrup/triage.log` | ### UI | Key | Action | |-----|--------| | o | Cycle sort order | | i | Invert sort | | w / W | Cycle pane width forward / back | | H | Set top-bar (view) colour | | B | Folder browser | | Ctrl-B | Cycle border style | | P | Preferences | | y / Y | Copy message ID / copy right pane content | | Ctrl-L | Force redraw | | ? | Help (press again for extended help) | | q | Quit | ## Configuration Config file: `~/.kastrup/kastruprc` On first run, Kastrup creates the database and guides you through initial setup (default email, editor). ### External fetch scripts (messenger / instagram) Both `messenger` and `instagram` source types shell out to a Python script that drives a Marionette-controlled Firefox. The default location is `~/.kastrup/plugins/.py`. Override per-source with: ```yaml fetch_script: ~/path/to/messenger_fetch.py ``` `~/` and `$HOME` are expanded. ### Views Views are user-defined filter recipes stored in the `views` table. Each row's `filters` is a JSON blob with a `rules` array of `{field, op, value}` triples: ```json {"rules": [{"field":"folder","op":"like","value":"python.slack.|irc.libera."}]} ``` To express OR across heterogeneous criteria — e.g. one view for "everything related to project Foo, across email AND Slack AND IRC" — use `branches` instead of (or together with) `rules`. Each branch is an independent rule set; results are unioned: ```json { "name": "Foo", "branches": [ {"rules": [{"field":"folder","op":"=","value":"Customers.Foo"}]}, {"rules": [{"field":"folder","op":"like","value":"python.slack.workspace.#foo"}]}, {"rules": [{"field":"sender","op":"like","value":"foo"}]} ], "top_bg": "24" } ``` There's nothing source-specific about which view-key gets a "chat" badge or layout — any view (numbered or F-key) can mix mail folders, chat channels, and sender / content patterns however the user wants. Supported rule fields: `read`, `starred`, `folder`, `source_id`, `source_type`, `sender`. Supported `op` values: `=` (exact) and `like` (SQL `LIKE %value%`, pipe-separated value gives OR-of-LIKE within the field). ## AI triage (`z` key) Mail and chat make passable todo lists, but the bookkeeping is manual: read the message, decide if it's an event or a task, mentally route it. The `z` key delegates that triage to Claude. Press `z` on any message: 1. **Optional hint** — kastrup prompts for free-text. Skip with Enter, or steer the result, e.g. `add as a reminder a week before the deadline mentioned in the linked PDF`. 2. **Triage call** — shells out to `~/.kastrup/triage.sh` (a small bash wrapper around `claude --print`). The system prompt at `~/.kastrup/triage-prompt.txt` constrains Claude to return a strict JSON array of action objects, with the current message subject / sender / folder / body and your tock calendar names + existing hyperlist categories as context. 3. **Multi-pick preview** — actions render in the right pane with `[x]` / `[ ]` markers. Space to toggle, j/k to move, Enter to commit selected, Esc to cancel. 4. **Commits** — calendar actions drop an ICS into `~/.tock/incoming/` (picked up by [Tock](https://github.com/isene/tock)); todo actions append to `~/.tasks/todo.hl` under the right category (creates the section if missing), atomically so an open scribe buffer reloads cleanly. 5. **History** — every triage decision logs to `~/.kastrup/triage.log` (rolling 20 entries). `:triage` displays the log in the right pane. Both the prompt and the wrapper live as user-editable files in `~/.kastrup/` — tune the prompt without rebuilding kastrup. The shipped defaults install on first `z` use if missing. Defaults the wrapper expects: | File | Purpose | |------|---------| | `~/.kastrup/triage-prompt.txt` | System prompt sent to Claude | | `~/.kastrup/triage.sh` | Shell wrapper around `claude --print` | | `~/.tasks/todo.hl` | Destination [hyperlist](https://github.com/isene/hyperlist); edit in [scribe](https://github.com/isene/scribe) (which reloads on external append from v0.1.43) | | `~/.tock/incoming/` | Drop folder for ICS events (existing Tock convention) | | `~/.kastrup/triage.log` | Rolling history of triage decisions | Action shapes Claude can return: ```json [ { "kind": "calendar", "title": "Project kickoff", "when": "2026-06-03T14:00:00+02:00", "duration_min": 60, "calendar": "Work" }, { "kind": "todo", "category": "Personal", "text": "Renew passport before travel" }, { "kind": "clarify", "question": "Body mentions both 'next week' and a fixed date — one event or two items?" } ] ``` A pure FYI message (newsletter, ack, etc.) returns `[]` — no actions, nothing to commit. ## Architecture A detailed walk-through of the receive flows, send flows, auth matrix, threads and shared state, and the dedup scheme lives in [`docs/architecture.html`](docs/architecture.html). Open it locally for the rendered diagrams. ## Dependencies Runtime: SQLite (bundled). Optional: `notmuch` (search), `montage` (multi-image compositing), `curl` (RSS feeds). ## Part of the Rust Terminal Suite See the [Fe₂O₃ suite overview](https://github.com/isene/fe2o3) and the [landing page](https://isene.org/fe2o3/) for the full list of projects. | Tool | Clones | Description | |------|--------|-------------| | [rush](https://github.com/isene/rush) | [rsh](https://github.com/isene/rsh) | Shell | | [crust](https://github.com/isene/crust) | [rcurses](https://github.com/isene/rcurses) | TUI library | | [kastrup](https://github.com/isene/kastrup) | [Heathrow](https://github.com/isene/heathrow) | Messaging hub | | [pointer](https://github.com/isene/pointer) | [RTFM](https://github.com/isene/RTFM) | File manager | | [scroll](https://github.com/isene/scroll) | [brrowser](https://github.com/isene/brrowser) | Web browser | | [crush](https://github.com/isene/crush) | - | Rush config helper | ## License [Unlicense](https://unlicense.org/) - public domain. ## Credits Created by Geir Isene (https://isene.org) with extensive pair-programming with Claude Code.