The silent
guardian
of your
calendar.
Alfred is a self-hosted, single-user AI executive assistant. Natural-language tasks in; scheduled calendar blocks out. Two-way Google Calendar sync, Telegram chat, and a pluggable LLM backend. Open source.
The list is the lie.
Every productivity tool I've used asks me to do the hard part myself: decide what gets scheduled when. The list grows. The calendar stays empty. The list grows again.
A task without a calendar block isn't a task. It's a wish.
Alfred is what I wanted: type a task in natural language, get it scheduled into the next available focus block on my real calendar, with a Telegram nudge before it starts. No SaaS, no team features, no kanban view. One user, one calendar, one assistant.
Five verbs.
The whole product is five verbs, looped. Everything else is configuration.
Capture.
Tasks in natural language. Type them, speak them, forward a Telegram message.
Organize.
Schedule onto the calendar based on priority and deadline.
Remind.
Telegram pings before a block starts.
Plan.
Daily schedule generated for review each morning.
Sync.
Two-way with Google Calendar. The calendar is the source of truth.
The runtime.
One container. Two surfaces (web + Telegram). One agent loop in the middle. Google Calendar is the source of truth; everything else syncs.
One container, one user.
No multi-tenancy. No teams. Single SQLite file. docker-compose up on a $5 VPS and you own it.
Bring your own brain.
Claude tested across the full agent loop. OpenAI + Ollama on basic chat. Choose by latency, cost, or privacy.
GCal wins.
Google Calendar is the source of truth. Alfred is a thin layer that decides what to put there.
Three hard parts.
Agent loop, calendar sync, and surface parity. Everything else is plumbing.
Natural language → calendar block.
"Move the Tuesday gym slot to 7am and add a 2-hour deep work block before lunch" hits the agent loop, which has four
tools: find_free_slot, create_event, reschedule_event, and set_reminder.
The LLM picks tools, executes them, observes results, and keeps looping until either the task is fully scheduled or it escalates back to me with a clarifying question. No hard-coded intents — the model picks the shape of each request.
Two-way sync without conflicts.
Google Calendar push notifications via webhook for fast incoming changes; a 15-minute poll as belt-and-suspenders for missed webhooks. Outgoing changes write to Alfred's SQLite first, queue a GCal API call, and reconcile on response.
Conflict rule is simple: most-recent-update wins,
with the GCal etag as the version cursor. Edge cases (offline
edits, dropped webhooks) get caught by the poll.
Two surfaces, one agent.
The web UI and the Telegram bot are both thin shells over the same agent loop. Same tool registry, same context window, same SQLite session table. You can start a conversation on web, walk away, finish it on Telegram, and Alfred doesn't notice.
Built as a session-key abstraction — both the
browser and the Telegram chat resolve to the same user_id,
and conversation state is loaded by user, not by surface.
# web request
POST /chat
session: browser_xyz
user_id: u_42
message: "what's left for today?"
# minutes later · telegram
POST /chat
session: telegram_abc
user_id: u_42
message: "actually push the call to 4pm"
# context loaded by user_id, not session
context = db.sessions.find({
user_id: "u_42",
$orderby: { updated: -1 }
})
# → agent sees the morning thread + the new message
→ reschedules the call · replies via Telegram Open source, all the way down.
The repo's on GitHub. Anyone can read the architecture; some folks have already forked. Stack chosen so you can self-host on a laptop or a $5 VPS.
alfred/
├── backend/ # Python 3.11 · FastAPI · async
│ ├── api/ # REST endpoints
│ ├── agents/ # pluggable LLM tools
│ ├── integrations/ # gcal + telegram adapters
│ └── models/ # SQLAlchemy · SQLite
├── frontend/ # React 19 · Vite · Tailwind 4
│ ├── src/components/
│ ├── src/hooks/
│ └── vite.config.ts
├── docker/ # self-hosting
├── README.md
└── LICENSE "Build for yourself first. The internet of one is a real market."
Alfred is what happens when you stop trying to build a startup and start trying to fix your own week. Single user, no roadmap, no growth metric — just the smallest possible thing that makes the next Monday feel less heavy.
I'd recommend every founder ship something like this every year. It calibrates your taste in a way SaaS revenue doesn't.
Open-sourcing it was the right call. Other people self-hosting Alfred has surfaced bugs I'd never have caught alone, and the contributions have made it better than the version I would have shipped solo.
The pluggable LLM backend — Claude, OpenAI, or Ollama — was driven by contributors who wanted to run everything offline. Good problem to have.
Clone the repo. Run docker-compose. You're done.