ryakel/skulid

Self-hosted, single-user Google Calendar sync — an alternative you can run on a homelab box.

★ 0Forks 0GoGitHub ↗Compare

README

skulid

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.

Features

  • 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.

Quick start

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 -d

docker 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 -d

Tags 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.

Documentation

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

Status

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.

Stack

Go 1.26 · chi · pgx + Postgres 16 · goose · Google Calendar API v3 · HTMX + Alpine.js · distroless. See Architecture for the full map.

Contributing

Open an issue first for anything beyond a small fix. The codebase prefers explicit code over abstraction — see Development for conventions.

License

MIT.

Contributors

clauderyakeldependabot[bot]

Issues