Self-hosted, single-user Google Calendar sync — a Reclaim.ai alternative you can run on a homelab box.
skulid is one Go binary plus a Postgres database, packaged as a Docker Compose stack. You bring your own Google OAuth credentials and your own public HTTPS endpoint; skulid mirrors events between your calendars on rules you define and maintains automatic focus/availability blocks based on busy time elsewhere. An optional Claude-powered chat lets you talk to your calendars.
- Sync rules — mirror events between calendars one-way or bidirectionally, with Reclaim-style 4-level visibility presets, all-day handling modes, and a working-hours-only toggle.
- Smart blocks — auto-maintain focus/busy blocks on a target calendar based on busy time elsewhere.
- Tasks — one-shot work the scheduler auto-places in your next available Working-hours slot.
- Habits — recurring soft blocks (Lunch, Decompress) that drift near an ideal time within ±flex.
- Categories drive event color-coding and weekly hour totals.
- Planner — week timeline of every connected calendar.
- Priorities — Kanban of active tasks by priority bucket.
- Buffers — padding the scheduler keeps around busy time.
- AI assistant (optional) — chat with Claude; 17 tools across events, tasks, and habits; every write requires confirmation.
- Webhook + polling — Google push channels with 5-minute fallback.
- Token sealing — refresh tokens AES-256-GCM encrypted at rest.
- Single-user TOFU — first Google login claims the instance.
git clone https://github.com/ryakel/skulid.git
cd skulid
cp .env.example .env
# fill in EXTERNAL_URL, Google OAuth, SESSION_SECRET, ENCRYPTION_KEY
docker compose up -ddocker compose up -d pulls the multi-arch image from
ghcr.io/ryakel/skulid:latest.
Pin a release with SKULID_TAG=v1.2.3 in .env. Deploy from your own
internal registry by setting SKULID_IMAGE=registry.home.lan/ryakel/skulid.
For local development, build the image directly and point the tag at it:
docker build -t ghcr.io/ryakel/skulid:dev .
SKULID_TAG=dev docker compose up -dTags latest and vX.Y.Z are published on every push to main by
.github/workflows/build-and-publish.yml.
Open EXTERNAL_URL in a browser, sign in with Google, you own the
instance. Full walkthrough in
Getting Started.
The detailed docs live in the GitHub Wiki,
and are version-controlled in wiki/ for review alongside
code changes (synced to the Wiki by .github/workflows/wiki-sync.yml
on push to main).
| Page | What's in it |
|---|---|
| Home | Index and orientation |
| Getting Started | Zero-to-running walkthrough |
| Architecture | Stack, data model, change flow |
| Planner | Week timeline view |
| Tasks | Auto-scheduled one-shot blocks |
| Habits | Recurring soft blocks |
| Priorities | Kanban view of active tasks |
| Sync Rules | Visibility modes, all-day, working-hours-only, filters |
| Smart Blocks | Working hours, DST, recompute, examples |
| Categories | Built-in palette + auto-categorization heuristics |
| Hours | Working/Personal/Meeting windows per account |
| Buffers | Padding around scheduled blocks |
| AI Assistant | Tools, confirmation flow, persistence |
| Configuration | Every supported environment variable |
| Operations | Backups, watch renewal, audit log, troubleshooting |
| Security Model | Threat model and what we do/don't protect against |
| Development | Local setup, conventions, adding features |
Beta. Breaking schema changes get a numbered migration under
migrations/; an instance reports its own version via goose_db_version
rather than a number pinned in prose here. The token sealing key is not
auto-rotated — back up your ENCRYPTION_KEY somewhere offline.
Go 1.26 · chi · pgx + Postgres 16 · goose · Google Calendar API v3 · HTMX + Alpine.js · distroless. See Architecture for the full map.
Open an issue first for anything beyond a small fix. The codebase prefers explicit code over abstraction — see Development for conventions.
MIT.