Ronny: a self-hosted email → Telegram assistant in Zig 0.16, which now reads its own logs and fixes itself

Ronny: a self-hosted email → Telegram assistant in Zig 0.16, which now reads its own logs and fixes itself


Hi all. I’ve been building Ronny, a personal assistant that watches my mailbox and talks to me on Telegram. It’s written in Zig 0.16 and runs on my home server: about 15k lines in 28 files, with 133 tests.

Repo: GitHub - eddygarcas/ronny-zig: Email assistant in Zig. Watches a mailbox over IMAP and answers on Telegram, by text or voice. Search, summaries and spam checks run on a local model, so mail content never leaves your machine — and nothing is sent without your explicit yes. · GitHub

What it does

  • Watches the inbox with IMAP IDLE and notifies me on Telegram about mail from an allowlist of senders. A local spam check runs first, so even an allowlisted sender’s marketing or phishing gets dropped.
  • Takes text or voice commands to search, read, summarise and draft replies, and to add calendar entries. Voice notes are transcribed with whisper.cpp on the GPU, and summaries can come back as voice notes made with piper and ffmpeg.
  • New: meeting reminders. It checks Google Calendar and messages me N minutes before any entry with a link to join. That link can be a Meet link, a conference add-on, or a known meeting host found in the location or description. If the entry has an agenda, it’s summarised by the local model and sent as text or a voice note, depending on my settings. The lead time and the on/off switch are chat settings (“remind me 15 minutes before meetings”).

Supported Zig versions

zig-0-16

AI / LLM usage disclosure

Tagged llm.

Writing the code. Most of the code was written with Claude Code; 56 of the 65 commits carry a Co-Authored-By: Claude trailer. I decided what to build and set the hard rules: no send without a typed yes, no address taken from model output, mail content stays on the machine. I also drove the architecture: three processes, deterministic parsers for closed grammars, the send gate. I reviewed, tested and ran each change against my real mailbox and calendar before committing. The non-obvious decisions, and the bugs that shaped them, are written down in AGENTS.md and docs/.

Maintaining it. The daily log review is itself an AI agent, Claude Code running headless. It commits and deploys fixes without me reviewing them first, inside the limits described above. Those commits are marked the same way.

At runtime. Ronny calls LLMs:

  • A local Ollama model does the spam checks, summaries, drafts and agenda summaries.
  • A hosted model picks which action a chat command maps to, from a closed set.
  • Model output never decides who gets mail, or whether it gets sent.