$git clone https://github.com/ridafkih/keeper.shKeeper.sh is a simple & open-source calendar syncing tool. It allows you to pull events from your Google Calendar, Outlook, iCloud, Fastmail, CalDAV server, or a remotely hosted iCal or ICS links, and push them to one or many calendars so the time slots can align across them all.
| 1 |  |
| 2 | |
| 3 | # About |
| 4 | |
| 5 | Keeper.sh is a simple & open-source calendar syncing tool. It allows you to pull events from your Google Calendar, Outlook, iCloud, Fastmail, CalDAV server, or a remotely hosted iCal or ICS links, and push them to one or many calendars so the time slots can align across them all. It also serves as a global MCP server and API for you or your agents to manage all your calendars from one convenient interface. You can use the hosted version for convenience and zero-setup, or self-host to get Pro features free. |
| 6 | |
| 7 | # Features |
| 8 | |
| 9 | - Aggregating calendar events from remote sources |
| 10 | - Event content agnostic syncing engine |
| 11 | - Push aggregate events to one or more calendars |
| 12 | - MCP (Model Context Protocol) server for AI agent calendar access |
| 13 | - Open source under AGPL-3.0 |
| 14 | - Easy to self-host |
| 15 | - Easy-to-purge remote events |
| 16 | |
| 17 | # Bug Reports & Feature Requests |
| 18 | |
| 19 | If you encounter a bug or have an idea for a feature, you may [open an issue on GitHub](https://github.com/ridafkih/keeper.sh/issues) and it will be triaged and addressed as soon as possible. |
| 20 | |
| 21 | # Contributing |
| 22 | |
| 23 | High-value and high-quality contributions are appreciated. Before working on large features you intend to see merged, please open an issue first to discuss beforehand. |
| 24 | |
| 25 | ## Local Development |
| 26 | |
| 27 | The dev environment runs behind HTTPS at `https://keeper.localhost` using a [Caddy](https://caddyserver.com/) reverse proxy with automatic TLS. The `.localhost` TLD resolves to `127.0.0.1` automatically per [RFC 6761](https://datatracker.ietf.org/doc/html/rfc6761) — no `/etc/hosts` entry is needed. |
| 28 | |
| 29 | ### Prerequisites |
| 30 | |
| 31 | - [Bun](https://bun.sh/) (v1.3.11+) |
| 32 | - [Docker](https://docs.docker.com/get-started/) & Docker Compose |
| 33 | |
| 34 | ### Getting Started |
| 35 | |
| 36 | ```bash |
| 37 | bun install |
| 38 | ``` |
| 39 | |
| 40 | #### Generate and Trust a Root CA |
| 41 | |
| 42 | The dev environment runs behind HTTPS via Caddy. You need to generate a local root certificate authority and trust it so your browser accepts the certificate. |
| 43 | |
| 44 | ```bash |
| 45 | mkdir -p .pki |
| 46 | openssl req -x509 -new -nodes \ |
| 47 | -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ |
| 48 | -keyout .pki/root.key -out .pki/root.crt \ |
| 49 | -days 3650 -subj "/CN=Keeper.sh CA" |
| 50 | ``` |
| 51 | |
| 52 | Then trust it on your platform: |
| 53 | |
| 54 | **macOS** |
| 55 | |
| 56 | ```bash |
| 57 | sudo security add-trusted-cert -d -r trustRoot \ |
| 58 | -k /Library/Keychains/System.keychain .pki/root.crt |
| 59 | ``` |
| 60 | |
| 61 | **Linux** |
| 62 | |
| 63 | ```bash |
| 64 | sudo cp .pki/root.crt /usr/local/share/ca-certificates/keeper-dev-root.crt |
| 65 | sudo update-ca-certificates |
| 66 | ``` |
| 67 | |
| 68 | #### Start the Dev Environment |
| 69 | |
| 70 | ```bash |
| 71 | bun dev |
| 72 | ``` |
| 73 | |
| 74 | This starts PostgreSQL, Redis, and a Caddy reverse proxy via Docker Compose, along with the API, web, MCP, and cron services locally. Once running, open `https://keeper.localhost`. |
| 75 | |
| 76 | ### Architecture |
| 77 | |
| 78 | | Service | Local Port | Accessed Via | |
| 79 | | -------- | ---------- | ------------------------------------ | |
| 80 | | Caddy | 443 | `https://keeper.localhost` | |
| 81 | | Web | 5173 | Proxied by Caddy | |
| 82 | | API | 3000 | Proxied by Web at `/api` | |
| 83 | | MCP | 3001 | Proxied by Web at `/mcp` | |
| 84 | | Postgres | 5432 | `postgresql://postgres:postgres@localhost:5432/postgres` | |
| 85 | | Redis | 6379 | `redis://localhost:6379` | |
| 86 | |
| 87 | # Qs |
| 88 | |
| 89 | ## Why does this exist? |
| 90 | |
| 91 | Because I needed it. Ever since starting [Sedna](https://sedna.sh/)—the AI governance platform—I've had to work across three calendars. One for my business, one for work, and one for personal. |
| 92 | |
| 93 | Meetings have landed on top of one-another a frustratingly high number of times. |
| 94 | |
| 95 | ## Why not use _this other service_? |
| 96 | |
| 97 | I've probably tried it. It was probably too finicky, ended up making me waste hours of my time having to delete stale events it didn't seem to want to track anymore, or just didn't sync reliably. |
| 98 | |
| 99 | ## How does the syncing engine work? |
| 100 | |
| 101 | - If we have a local event but no corresponding "source → destination" mapping for an event, we push the event to the destination calendar. |
| 102 | - If we have a mapping for an event, b |