cd /news/artificial-intelligence/horolog-a-self-hosted-open-source-al… · home topics artificial-intelligence article
[ARTICLE · art-89820] src=github.com ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

Horolog – a self-hosted, open-source alternative to Reclaim.ai

Horolog, a self-hosted, open-source AI calendar alternative to Reclaim.ai, was released on GitHub by developer Ujjwal Reddy, offering automatic scheduling of focus time, habits, and tasks with re-plan latency of 0.6–97 ms compared to Reclaim's ~15 seconds. The tool runs entirely on the user's machine, supports multiple language models including Ollama and OpenAI, and guarantees minimal disruption by moving only affected blocks when meetings change, as verified by tests.

read8 min views1 publishedAug 10, 2026
Horolog – a self-hosted, open-source alternative to Reclaim.ai
Image: source

An open-source, self-hosted AI calendar that defends your time.

Horolog places focus time, habits and tasks around the meetings you actually have - then keeps them there. It runs entirely on your own machine, against your own calendar, with whichever language model you already trust.

It is a working alternative to Reclaim.ai (acquired by Dropbox in 2024). At the time of writing no open-source equivalent existed: Cal.com covers booking links, but the auto-scheduling half - focus time, habits, priority preemption, automatic rescheduling - had not been built in the open.

git clone https://github.com/ujjwalredd/horolog.git && cd horolog
cp .env.example .env
docker compose -f infra/docker-compose.yml up

No account. No telemetry. Nothing leaves the machine.

Most "AI calendar" tools re-plan your entire week whenever anything changes, so you stop trusting what you see. Horolog optimises for the opposite:

Re-plan latency | 0.6–97 ms (Reclaim documents ~15 s) | Blocks moved when a meeting lands | only the ones it hit - verified by test, not asserted | Re-plan with nothing changed | a provable no-op, to the slot | Where your calendar lives | your machine | Model | Ollama, vLLM, SGLang, llama.cpp, Anthropic, OpenAI - your choice |

The stability guarantee is the point. Measured on 20 blocks with an eight-hour meeting dropped into the middle of the week: 4 blocks collided, 4 blocks moved, 16 untouched. An earlier single-pass placer moved 16 of 20 - the regression test that pins this is test_a_busy_week_does_not_cascade.

Each is the same scheduling primitive wearing a different hat - one engine, not five subsystems. See ARCHITECTURE.md for why that matters.

Agent What it does
Smart Task Scheduler
Deadline-aware tasks, split across sittings and placed around real meetings. Priority P1–P4 decides who wins a contested slot.
Habit & Routine Manager
"Gym three times a week between 10 and 4." Recurrence, time-of-day windows, per-day caps, automatic relocation.
Dynamic Calendar Sync
ICS feeds and CalDAV servers in, an ICS feed of your plan back out. Recurring events expanded; free/transparent events ignored.
Smart Meetings
Multi-attendee scheduling that intersects everyone's availability - without letting a colleague's calendar block your own solo work.
Productivity Analytics
Deep-work hours, meeting load, fragmentation, longest free run per day, after-hours load, unmet demand.

Three more, built on the same engine:

Feature What it does
Decompression buffers
Off by default (HOROLOG_AUTO_BUFFER_ENABLED ). Holds recovery time after every meeting between 30 minutes and 4 hours, from any source — ICS, CalDAV or typed in. A run of back-to-back meetings gets one buffer, at the end. It spends real capacity, which is why it is opt-in.
Booking links
/book/<name> offers true free time from GET /api/availability : hours holding flexible focus work are still bookable, because accepting one moves that work rather than colliding with it. Only real commitments close a slot. POST /api/book writes the accepted meeting to the calendar mirror and re-solves around it.
Multi-Provider Integrations
Native sync modules for Linear (POST /api/sync/linear ), Todoist (todoist.py ), and GitHub Issues (github.py ). Issues automatically schedule as fluid tasks around your real-life calendar.

Booking links have no authentication./book/<name>

is a display label, not an identity — this is a single-user, self-hosted app. Put the route behind your reverse proxy before exposing it to the internet.

Real captures, from npm run seed:demo

's sample week - not mockups.

Planner — month, week, day or list. Priority by accent weight, kind by glyph, movability by rule style. Live over SSE.

Task inbox — every intent and where it actually landed. Anything that did not fit is called out, not hidden.

Habits & Focus Time — builds routines and weekly focus goals in the units people speak: "3× a week, an hour each, between 10 and 4" or "10 hours a week, ≥90-minute sittings".

Meetings — a Smart Meeting only lands where every attendee's busy time allows, without ever blocking your own solo work.

Analytics — stat tiles plus a per-day load chart. Palette validated for colour-vision deficiency, not eyeballed.

Calendars — connect Google or Outlook, an ICS feed, or a CalDAV server; export your plan as a subscribable feed.

Time — a live "today" view: what's happening right now, what's next, and a moving now-line on the day's timeline.

⌘K

anywhere opens natural-language capture: "write the design doc, about three hours, by Friday".

The model only ever fills in a form. It reads your sentence and produces a schema-checked request; it never picks a time and it cannot write to your calendar. The scheduler does that, from your real availability. Output is constrained at decode time, so malformed answers are not caught - they are impossible.

HOROLOG_LLM_PROVIDER=openai
HOROLOG_LLM_BASE_URL=http://localhost:11434/v1
HOROLOG_LLM_MODEL=qwen3:8b

HOROLOG_LLM_PROVIDER=anthropic
HOROLOG_LLM_MODEL=claude-opus-5
HOROLOG_LLM_API_KEY=sk-ant-...

HOROLOG_LLM_PROVIDER=openai
HOROLOG_LLM_BASE_URL=https://api.openai.com/v1
HOROLOG_LLM_MODEL=gpt-4.1
HOROLOG_LLM_API_KEY=sk-...

Anthropic needs the optional extra: pip install 'horolog[anthropic]'

(already included in the Docker image).

Requirements: Python 3.12+, Node 20+. No database server needed - SQLite by default.

cd services/api && uv venv --python 3.12 && uv pip install -e ".[dev]" && cd -
cd apps/web && npm install && cd -

npm run dev
api: 8000 is in use, using 8001
  Horolog
  api  http://localhost:8001
  web  http://localhost:3000

npm run dev

picks free ports rather than assuming 8000/3000, wires the web app's proxy to whichever port the API got, clears any .next

left behind by a production build, and shuts both halves down together. Running the two by hand works too (npm run dev:api

, npm run dev:web

) - just don't start two next dev

processes against the same checkout, because they share .next

and overwrite each other's chunks.

The API loads the repository-root .env

created by the quickstart. The launcher also keeps OAuth callback URLs on the actual ports it selected, so Google and Microsoft callbacks continue to work when 8000 or 3000 was already occupied.

The web app proxies /api/*

to the API, so the browser sees one origin and there is no CORS to configure. Override with HOROLOG_API_URL

.

npm test       # 45 tests, ~1s
npm run bench  # solve-time + quality benchmark
npm run check  # ruff + ruff format + mypy strict + pytest, then tsc + next build

The suite is fast on purpose - the scheduling engine has no I/O, so its properties are checked directly rather than through a server.

$ python -m horolog.bench
horizon 2016 slots (21 days), 9:00-18:00 workday
demand held at 85% of open capacity | 7 runs each

 intents   reqs   load  blocks  cold p50  cold p95  warm p50  warm p95  unmet
      30     70   84%     107      3.8ms     4.6ms     3.0ms     5.3ms      5
     100    232   86%     224     13.6ms    14.8ms     8.9ms    16.9ms      8
     300    700  185%     335     59.6ms    92.2ms    54.2ms    64.2ms    365

The 300-intent row is deliberately over-subscribed (185% of capacity) - the unmet

column is correct behaviour there, not a failure.

Every setting is read from the environment with a HOROLOG_

prefix and validated at boot, so a bad value fails the process rather than the first request that needs it. Full list in .env.example.

The one worth knowing: HOROLOG_TIMEZONE

defaults to the host's own zone. Left at UTC on a machine that isn't, a 9-to-5 goal silently lands at 5am.

docker compose -f infra/docker-compose.yml up -d

Brings up the API, the web app, Postgres, and Ollama with a small model pulled on first boot. For SQLite instead of Postgres, drop the db

service and set HOROLOG_DATABASE_URL=sqlite+aiosqlite:///./data/horolog.db

.

Authentication: this build is single-user and unauthenticated by design - it runs on your machine against your calendar. For a team instance, put it behind an SSO proxy (oauth2-proxy, Authelia, Tailscale, Cloudflare Access) rather than exposing it directly. Horolog trusts the identity your proxy asserts instead of rolling its own credential store.

Backups: see docs/BACKUP.md - one command either way, SQLite or Postgres. There's no schema-migration tooling, so back up before pulling a new version.

services/api/horolog/
  domain/       Pydantic contracts - the single source of truth
  solver/       expand → greedy placement → score
  providers.py  ICS + CalDAV
  llm.py        multi-provider structured extraction
  capture.py    natural language → validated intent
  analytics.py  derived metrics
  api.py        HTTP surface + SSE
apps/web/app/   Next.js 15, App Router, Tailwind v4
docs/           ARCHITECTURE.md
infra/          docker-compose + Dockerfiles

We welcome contributions! Please read our Contributing Guide and Code of Conduct before submitting pull requests.

The engine is the product; everything else is I/O around it. If you change anything under solver/

, the property tests in services/api/tests/test_solver.py are the contract - they encode behaviour (no overlap, bounded churn, honest shortfall) rather than implementation, so a better algorithm should keep them all passing.

Run python -m horolog.bench

before and after any placement change and put the numbers in the PR.

Please see our Security Policy for vulnerability reporting guidelines.

AGPL-3.0-or-later. If you run a modified version as a network service, publish your changes.

Not affiliated with Reclaim.ai or Dropbox. "Reclaim.ai" is referenced only to describe the category this project reimplements.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @horolog 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/horolog-a-self-hoste…] indexed:0 read:8min 2026-08-10 ·