Lean, self-hostable Calendly & Cal.com alternative — a single Go binary + SQLite, API-first and MCP-native. Apache-2.0.
See the codeA lean, self-hostable scheduling engine that lives in your AI stack.
Calnode is a Calendly-style booking app — with first-party video meetings, recording, and AI notetaking built in — shipped as a single Go binary with an embedded SQLite database: no Redis, no Postgres, no separate API server, no multi-gigabyte image. It's API-first, webhook-native, and built for a world where agents do the booking. Self-host the whole thing on a $5 box; nothing is paywalled.
Calnode runs for pennies on a small VPS — it's a single static binary serving a SQLite file, so there's almost nothing to pay for.
Apache-2.0 · Go 1.26 · single static binary · SQLite + Litestream
Kick the tires — one command, no config:
docker run -p 3000:3000 -v ./data:/data ghcr.io/calnode/calnode:latest
# → open http://localhost:3000
The whole app — booking pages, admin UI, SQLite — in one container, with data in
./data. With no encryption key set it runs on an ephemeral one (fine for a look;
stored credentials won't survive a restart). Deploying for real — HTTPS, a
persistent encryption key, backups — see Deploy for real below,
or the full DEPLOY.md.
docker run it, or drop it on a VPS. No external services to orchestrate
(built-in video, if you turn it on, is the one add-on — it needs a LiveKit server)..ics feeds).sqlc-generated queries — not a
100-package monorepo. Add an endpoint without spelunking.The default open-source scheduler is a SaaS monolith. Calnode is the opposite.
| cal.com | Calnode | |
|---|---|---|
| Codebase | 500k+ LOC TS across ~100 packages | Lean Go + one SvelteKit app |
| Runtime deps | 4 GB+ image; needs Redis + Postgres + API server | One static binary + a SQLite file |
| Database | Postgres (+ Redis) | SQLite (WAL) + Litestream point-in-time backup |
| Webhooks | UI-only | API-first, HMAC-signed, per-webhook payloads |
| AI / agents | None | REST API + webhooks + a native MCP server (stdio + HTTP) |
| Video & recording | Third-party links (Zoom / Meet) | Built-in in-browser rooms + recording + AI notes (self-hosted LiveKit) |
| Deploy | Orchestrate several services | docker run one container |
| Isolation | Shared multi-tenant DB (org_id everywhere) | Instance-per-tenant — isolation is the default |
| Licence | AGPL-3.0 | Apache-2.0, nothing paywalled for self-host |
(cal.com figures reflect its public footprint; see the the design docs for the full rationale.)
If you're weighing self-hosted schedulers and you want small, fast, scriptable, and AI-ready over feature-maximal, Calnode is built for you.
Everything a human can do, an agent can do — over the API today:
# Find slots and book, with an API key
curl -s "$BASE/v1/event-types/intro-call/slots?from=2026-06-16&to=2026-06-20&tz=Pacific/Auckland" \
-H "Authorization: Bearer $API_KEY"
curl -s -X POST "$BASE/v1/bookings" -H "Authorization: Bearer $API_KEY" \
-H 'Idempotency-Key: 9f3c…' -H 'Content-Type: application/json' \
-d '{"event_type_slug":"intro-call","start_at":"2026-06-17T21:00:00Z","name":"Alex","email":"alex@example.com","timezone":"Pacific/Auckland"}'
Wire booking lifecycle events (booking.created / .rescheduled / .cancelled)
to n8n / Make / your own service with HMAC-signed webhooks — all configured through
the API, not buried in a UI.
Native MCP server. A Model Context Protocol server is compiled into the binary
(official Go SDK), exposing eight first-class tools — list_event_types,
get_event_type, get_available_slots, create_booking, get_booking,
reschedule_booking, cancel_booking, list_bookings. The MCP tools call the same internal services as
the REST API (no parallel code path), so booking side effects — calendar events,
confirmation emails, webhooks, reminders — fire identically.
Two transports:
calnode mcp (logs to stderr, JSON-RPC on stdout).POST /mcp for remote agents. Calnode is its own OAuth 2.1
authorization server (dynamic client registration + PKCE), so an agent adds the
server by URL and clicks Connect → signs in with the workspace's Google/Microsoft
login → approves a consent screen — no pre-shared key. A cno_ API key also works
(Authorization: Bearer <key>) for scripts. (The Connect UX needs HTTPS — it shines
on a deployed instance.)User: "Book a 30-min call with Wynne next week — I'm in Auckland."
Agent: get_available_slots("intro-call", "2026-06-16", "2026-06-20", "Pacific/Auckland")
→ presents options → create_booking(…) → returns confirmation + meeting link
Conversational booking — in the booking page itself. Beyond agents, the booking page (and the embed widget) ships an optional "Book by chat" assistant: a visitor types "free Tuesday afternoon or next week" and it resolves real availability and books — the deterministic engine still computes the slots (the model never invents times), and the assistant only ever sees free/busy windows, never your calendar contents. Bring your own model — any OpenAI-compatible endpoint (a hosted model or one you run yourself); off by default, with the standard calendar always there as the fallback.
Connecting Claude (remote / HTTP). In Claude (claude.ai or Desktop) →
Settings → Connectors → Add custom connector → enter https://<your-instance>/mcp
→ Connect → sign in → Allow. Custom connectors need a paid Claude plan; the
server must be on HTTPS. Local stdio alternative (any plan): point an MCP client at
the calnode mcp subcommand via its config file — no OAuth, runs against the local DB.
Permissions. MCP tools are role-scoped, mirroring the rest of the app: an owner/admin acts across the whole workspace, a member sees and manages only bookings they host. (The stdio subcommand is the local operator → full access.) Booking creation and availability are the public booking surface, open to all. Roles are fixed (owner / admin / member); configurable RBAC is intentionally out of the lean core.
docker run -d -p 3000:3000 \
-e BASE_URL=https://booking.example.com \
-e CALNODE_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
-e CALNODE_RECOVERY_SECRET="$(openssl rand -hex 32)" \
-e DATABASE_URL=sqlite:///data/calnode.db \
-v calnode-data:/data \
ghcr.io/calnode/calnode:latest
Pinning a version. :latest follows the newest tagged release; :edge tracks
main. For reproducible deploys, pin a release — :0.1 (tracks patches within the
minor) or an exact :0.1.0. See releases.
Open / → it redirects to /admin/ and walks you through first-run setup (create
the owner account, connect a calendar, add an event type). Put a TLS-terminating
proxy in front that forwards the original Host header.
Full guide → DEPLOY.md (env vars, Railway step-by-step, custom domains, Resend email, Google & Microsoft OAuth, Litestream backups, troubleshooting).
Shipped
calnode mcp + Streamable HTTP at /mcp)get_meeting_notes/get_transcript + recording.completed/transcript.ready/notes.ready webhooks. BYO LiveKit endpoint (Cloud or self-hosted); configured in Settings → Video — see docs/VIDEO.mdAccept-Language with a footer switcher and an operator-set fallback language; the
booker's choice is stored on the booking, so reminders arrive in the language they
booked in. Adding a language is adding one JSON file - no code change. (The admin UI and the built-in video room are
English-only. Non-English translations are machine drafts without native review -
corrections by PR are very welcome.)Idempotency-Key on booking creation; transactional double-booking guard<head> code injection + window.dataLayer events (GTM/GA4)On the roadmap
/metrics · multi-domain (one instance, many hostnames)modernc.org/sqlite, CGO-free → static binary) · goose migrationsgo:embed)html/template + vanilla JS (no framework runtime)A few load-bearing decisions (full detail in the the design docs):
Calnode's backend is small enough to fit entirely in one LLM's context window — most scheduling software (cal.com included) can't say that. AUDIT.md turns that into a self-serve check: a copy-paste scanner block (govulncheck, gosec, gitleaks across full history, SBOM, semgrep — all neutral, standard tooling you run yourself), an adversarial LLM prompt-pack for your own coding agent, and a claims → verification manifest mapping every security claim we make to exactly how to check it in the source. Not a certification — a due-diligence accelerator.
Self-hosting is free forever — docker run it and you own the whole thing. If you'd
rather not run it yourself, Orchestratr — the team
behind Calnode — will deploy and operate it for you:
Built for regulated and mid-to-large orgs that need Calnode on their own infrastructure, fully integrated. → calnode.com/services
Apache-2.0. The full scheduler is self-hostable, and nothing previously free is ever paywalled.
The code is Apache-2.0; the "Calnode" name and logo are not — see TRADEMARK.md (use the code freely; name your fork something else). Contributions are accepted under a CLA so the project can stand behind every line and keep its future licensing options open — see CONTRIBUTING.md.
Go
74.3%
Svelte
14.5%
HTML
4.0%
JavaScript
3.6%
Vue
1.4%
Lean, self-hostable Calendly & Cal.com alternative — a single Go binary + SQLite, API-first and MCP-native. Apache-2.0.
See the codeA lean, self-hostable scheduling engine that lives in your AI stack.
Calnode is a Calendly-style booking app — with first-party video meetings, recording, and AI notetaking built in — shipped as a single Go binary with an embedded SQLite database: no Redis, no Postgres, no separate API server, no multi-gigabyte image. It's API-first, webhook-native, and built for a world where agents do the booking. Self-host the whole thing on a $5 box; nothing is paywalled.
Calnode runs for pennies on a small VPS — it's a single static binary serving a SQLite file, so there's almost nothing to pay for.
Apache-2.0 · Go 1.26 · single static binary · SQLite + Litestream
Kick the tires — one command, no config:
docker run -p 3000:3000 -v ./data:/data ghcr.io/calnode/calnode:latest
# → open http://localhost:3000
The whole app — booking pages, admin UI, SQLite — in one container, with data in
./data. With no encryption key set it runs on an ephemeral one (fine for a look;
stored credentials won't survive a restart). Deploying for real — HTTPS, a
persistent encryption key, backups — see Deploy for real below,
or the full DEPLOY.md.
docker run it, or drop it on a VPS. No external services to orchestrate
(built-in video, if you turn it on, is the one add-on — it needs a LiveKit server)..ics feeds).sqlc-generated queries — not a
100-package monorepo. Add an endpoint without spelunking.The default open-source scheduler is a SaaS monolith. Calnode is the opposite.
| cal.com | Calnode | |
|---|---|---|
| Codebase | 500k+ LOC TS across ~100 packages | Lean Go + one SvelteKit app |
| Runtime deps | 4 GB+ image; needs Redis + Postgres + API server | One static binary + a SQLite file |
| Database | Postgres (+ Redis) | SQLite (WAL) + Litestream point-in-time backup |
| Webhooks | UI-only | API-first, HMAC-signed, per-webhook payloads |
| AI / agents | None | REST API + webhooks + a native MCP server (stdio + HTTP) |
| Video & recording | Third-party links (Zoom / Meet) | Built-in in-browser rooms + recording + AI notes (self-hosted LiveKit) |
| Deploy | Orchestrate several services | docker run one container |
| Isolation | Shared multi-tenant DB (org_id everywhere) | Instance-per-tenant — isolation is the default |
| Licence | AGPL-3.0 | Apache-2.0, nothing paywalled for self-host |
(cal.com figures reflect its public footprint; see the the design docs for the full rationale.)
If you're weighing self-hosted schedulers and you want small, fast, scriptable, and AI-ready over feature-maximal, Calnode is built for you.
Everything a human can do, an agent can do — over the API today:
# Find slots and book, with an API key
curl -s "$BASE/v1/event-types/intro-call/slots?from=2026-06-16&to=2026-06-20&tz=Pacific/Auckland" \
-H "Authorization: Bearer $API_KEY"
curl -s -X POST "$BASE/v1/bookings" -H "Authorization: Bearer $API_KEY" \
-H 'Idempotency-Key: 9f3c…' -H 'Content-Type: application/json' \
-d '{"event_type_slug":"intro-call","start_at":"2026-06-17T21:00:00Z","name":"Alex","email":"alex@example.com","timezone":"Pacific/Auckland"}'
Wire booking lifecycle events (booking.created / .rescheduled / .cancelled)
to n8n / Make / your own service with HMAC-signed webhooks — all configured through
the API, not buried in a UI.
Native MCP server. A Model Context Protocol server is compiled into the binary
(official Go SDK), exposing eight first-class tools — list_event_types,
get_event_type, get_available_slots, create_booking, get_booking,
reschedule_booking, cancel_booking, list_bookings. The MCP tools call the same internal services as
the REST API (no parallel code path), so booking side effects — calendar events,
confirmation emails, webhooks, reminders — fire identically.
Two transports:
calnode mcp (logs to stderr, JSON-RPC on stdout).POST /mcp for remote agents. Calnode is its own OAuth 2.1
authorization server (dynamic client registration + PKCE), so an agent adds the
server by URL and clicks Connect → signs in with the workspace's Google/Microsoft
login → approves a consent screen — no pre-shared key. A cno_ API key also works
(Authorization: Bearer <key>) for scripts. (The Connect UX needs HTTPS — it shines
on a deployed instance.)User: "Book a 30-min call with Wynne next week — I'm in Auckland."
Agent: get_available_slots("intro-call", "2026-06-16", "2026-06-20", "Pacific/Auckland")
→ presents options → create_booking(…) → returns confirmation + meeting link
Conversational booking — in the booking page itself. Beyond agents, the booking page (and the embed widget) ships an optional "Book by chat" assistant: a visitor types "free Tuesday afternoon or next week" and it resolves real availability and books — the deterministic engine still computes the slots (the model never invents times), and the assistant only ever sees free/busy windows, never your calendar contents. Bring your own model — any OpenAI-compatible endpoint (a hosted model or one you run yourself); off by default, with the standard calendar always there as the fallback.
Connecting Claude (remote / HTTP). In Claude (claude.ai or Desktop) →
Settings → Connectors → Add custom connector → enter https://<your-instance>/mcp
→ Connect → sign in → Allow. Custom connectors need a paid Claude plan; the
server must be on HTTPS. Local stdio alternative (any plan): point an MCP client at
the calnode mcp subcommand via its config file — no OAuth, runs against the local DB.
Permissions. MCP tools are role-scoped, mirroring the rest of the app: an owner/admin acts across the whole workspace, a member sees and manages only bookings they host. (The stdio subcommand is the local operator → full access.) Booking creation and availability are the public booking surface, open to all. Roles are fixed (owner / admin / member); configurable RBAC is intentionally out of the lean core.
docker run -d -p 3000:3000 \
-e BASE_URL=https://booking.example.com \
-e CALNODE_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
-e CALNODE_RECOVERY_SECRET="$(openssl rand -hex 32)" \
-e DATABASE_URL=sqlite:///data/calnode.db \
-v calnode-data:/data \
ghcr.io/calnode/calnode:latest
Pinning a version. :latest follows the newest tagged release; :edge tracks
main. For reproducible deploys, pin a release — :0.1 (tracks patches within the
minor) or an exact :0.1.0. See releases.
Open / → it redirects to /admin/ and walks you through first-run setup (create
the owner account, connect a calendar, add an event type). Put a TLS-terminating
proxy in front that forwards the original Host header.
Full guide → DEPLOY.md (env vars, Railway step-by-step, custom domains, Resend email, Google & Microsoft OAuth, Litestream backups, troubleshooting).
Shipped
calnode mcp + Streamable HTTP at /mcp)get_meeting_notes/get_transcript + recording.completed/transcript.ready/notes.ready webhooks. BYO LiveKit endpoint (Cloud or self-hosted); configured in Settings → Video — see docs/VIDEO.mdAccept-Language with a footer switcher and an operator-set fallback language; the
booker's choice is stored on the booking, so reminders arrive in the language they
booked in. Adding a language is adding one JSON file - no code change. (The admin UI and the built-in video room are
English-only. Non-English translations are machine drafts without native review -
corrections by PR are very welcome.)Idempotency-Key on booking creation; transactional double-booking guard<head> code injection + window.dataLayer events (GTM/GA4)On the roadmap
/metrics · multi-domain (one instance, many hostnames)modernc.org/sqlite, CGO-free → static binary) · goose migrationsgo:embed)html/template + vanilla JS (no framework runtime)A few load-bearing decisions (full detail in the the design docs):
Calnode's backend is small enough to fit entirely in one LLM's context window — most scheduling software (cal.com included) can't say that. AUDIT.md turns that into a self-serve check: a copy-paste scanner block (govulncheck, gosec, gitleaks across full history, SBOM, semgrep — all neutral, standard tooling you run yourself), an adversarial LLM prompt-pack for your own coding agent, and a claims → verification manifest mapping every security claim we make to exactly how to check it in the source. Not a certification — a due-diligence accelerator.
Self-hosting is free forever — docker run it and you own the whole thing. If you'd
rather not run it yourself, Orchestratr — the team
behind Calnode — will deploy and operate it for you:
Built for regulated and mid-to-large orgs that need Calnode on their own infrastructure, fully integrated. → calnode.com/services
Apache-2.0. The full scheduler is self-hostable, and nothing previously free is ever paywalled.
The code is Apache-2.0; the "Calnode" name and logo are not — see TRADEMARK.md (use the code freely; name your fork something else). Contributions are accepted under a CLA so the project can stand behind every line and keep its future licensing options open — see CONTRIBUTING.md.
Go
74.3%
Svelte
14.5%
HTML
4.0%
JavaScript
3.6%
Vue
1.4%