Self-hosting · automation
Self-hosted assistant on a Raspberry Pi 5
A personal assistant that syncs my university calendar, watches deadlines and will grow into a PWA and an MCP server.
Problem
The university campus only exposes deadlines as an ICS export, and I don’t want to check it by hand.
Design
One Python module holds the logic. It’s exposed three ways: a systemd timer for the sync, a FastAPI backend for a phone PWA, and an MCP server so an LLM can query it on demand.
Decisions
- Upsert keyed on the UNIR UID, stored in the event’s private extended properties: idempotent, safe to re-run.
- Delete only after two missing runs: protection against partial feeds.
- Cloudflare Tunnel instead of port forwarding: works behind CGNAT and keeps the router closed.
- API LLMs first, local models later: a Pi 5 only runs 1–3B models well.
Status
Sync in production on the Pi since October 2026: a systemd timer runs it every 30 minutes, and re-runs are idempotent.
Roadmap
Done
- Calendar sync with guarded deletions, as a hardened systemd service and timer.
- Self-hosted ntfy for failure alerts, published through an outbound Cloudflare Tunnel with WAF rules and rate limiting.
- External heartbeat: if the Pi goes silent, an outside monitor raises the alarm.
- Remote SSH through Cloudflare Access with two-factor login, no open ports.
- This website, served by a hardened Caddy on the same Pi behind the tunnel.
Next
- Backups and a move from the SD card to NVMe storage.
- Notes synced across devices with Syncthing, with an explicit list of what the assistant may read.
- Publishing from Obsidian: mark a note as public and it appears on this site, no manual deploy.
Later
- A FastAPI backend and a PWA with Web Push notifications.
- An MCP server so an LLM can query the same functions on demand.
- An LLM router across providers, and study blocks planned from free time and deadlines.
Being designed
- Power-cut handling (UPS or HAT, clean shutdown, alerts).
- Voice input and output.
- Home automation.