A calendar TUI in Zig — reads the local Mac calendar (everything Calendar.app sees: iCloud, Google, Exchange), navigable month/day/event views, create/edit/RSVP from the keyboard, background polling, and meeting notifications. Catppuccin, keyboard-driven, memory-frugal.
macOS only (Apple Silicon). Both this app and its write engine (the
icalCLI) are built on Apple's EventKit — there is nothing to port until the data layer is rethought. macOS 14+ required for the native calendar source.
| What | Why | Install |
|---|---|---|
| macOS 14+ (Apple Silicon) | EventKit full-access API | — |
ical CLI |
all calendar writes (create/edit/RSVP) + fallback read source | scripts/install-daemon.sh installs it automatically, or: brew tap BRO3886/tap && brew install ical |
| Homebrew | installs the above | — |
Without ical the TUI still works read-only via EventKit; creating, editing,
and RSVPing will tell you it's missing.
| Doc | What |
|---|---|
ARCHITECTURE.md |
Design: threading, data sources, memory rules, notifications, TCC. Start here before changing code. |
CODING_STANDARDS.md |
Binding Zig standards: memory/allocator rules, errors, interop, testing. |
CONTRIBUTING.md |
Setup, commands, workflow, pre-commit checklist. |
Grab the -macos-arm64.tar.gz from the
latest release
(Apple Silicon), or with gh:
gh release download --repo matthewmyrick/docket --pattern '*.tar.gz'
tar -xzf docket-*-macos-arm64.tar.gz
./docket/docket # or move it onto your PATHOr build from source (toolchain below): zig build -Doptimize=ReleaseSafe.
First run prompts for calendar access (System Settings → Privacy & Security →
Calendars). A downloaded binary carries the quarantine flag; if Gatekeeper
objects: xattr -d com.apple.quarantine docket.
This app shows whatever the Mac's calendar store knows about, so you connect accounts once at the system level — no per-app setup:
- System Settings → Internet Accounts → Add Account
- Pick Google (or Microsoft Exchange, iCloud, or any CalDAV server), sign in, and make sure the Calendars toggle is on
- That's it — macOS syncs the account into the same store Calendar.app uses, and this app picks the events up on its next poll (within a minute), each calendar in its own color
Hide calendars you don't want (Birthdays, holidays, a noisy shared one) with
calendars_exclude in the config below. Events you create from the TUI go
to your default calendar unless you fill the form's calendar field.
docket # the TUI: arrows move days, Enter drills in
docket --daemon # headless poll + notify (launchd)
docket --agenda # print today's events and exit
docket --version # print version and exitInstall the notification daemon (runs at login, survives the TUI closing):
scripts/install-daemon.sh # builds ReleaseSafe, installs launchd agentLogs land in ~/Library/Logs/docket.log; set DOCKET_DEBUG=1
for per-cycle lines.
~/.config/docket/config.zon (all keys optional; unknown keys
are an error):
.{
.poll_interval_seconds = 60, // clamped to [15, 3600]
.source = .auto, // .auto | .eventkit | .ical_cli
.lead_times_minutes = .{ 10, 1 },
.all_day_notify_at = null, // e.g. "09:00"
.notify_sink = .auto, // .auto | .herdr | .terminal_notifier | .osascript | .none
.week_start = .monday, // .monday | .sunday
.calendars_exclude = .{}, // e.g. .{ "Birthdays", "Siri Suggestions" }
.show_declined = false,
}
| Key | Where | Action |
|---|---|---|
← → / h l |
month, day | previous / next day |
↑ ↓ / k j |
month | same weekday ± a week |
↑ ↓ / k j |
day | select event · detail: scroll |
[ ] / PgUp PgDn |
month | previous / next month |
Enter |
month → day → detail | drill down |
q / Esc |
anywhere | back / close |
/ |
anywhere | fuzzy-search events (type, ↑↓, Enter) |
a |
anywhere | new event — form with date + time fields (blank time = all-day; blank = skip) |
y n m |
day, detail | RSVP: accept / decline / tentative |
e |
day, detail | edit selected event (same form, prefilled) |
t |
anywhere | jump to today |
r |
anywhere | refresh now |
o / c |
detail | open / copy video link or url |
? |
anywhere | help overlay |
Q / Ctrl-C |
anywhere | quit |
Pinned at milestone 0 (see ARCHITECTURE.md §3):
| Tool | Version | Source |
|---|---|---|
| Zig | 0.16.0 (.zigversion) |
brew install zig |
| libvaxis | 0.6.0 @ ca781b3c (pinned by hash in build.zig.zon) |
zig fetch |
ical |
0.12.1 | brew tap BRO3886/tap && brew install ical |
Native EventKit shim landed at milestone 4 (source: eventkit, with
ical_cli fallback).
Measured idle RSS, ReleaseSafe, 45-day window (recorded per release —
ARCHITECTURE.md §12): ~21 MB with the native EventKit source (≈10 MB of that is
EventKit/AppKit framework-resident pages), ~11 MB with the ical_cli
source. App-controlled memory is arena-bounded and flat over uptime.
macOS permission note: TCC ties the calendar grant to the binary's identity;
after rebuilding you may need to re-grant. Reset for testing with
tccutil reset Calendar dev.matthewmyrick.docket.
PRs welcome — start with CONTRIBUTING.md (workflow,
gates) and ARCHITECTURE.md (the invariants your change
must keep). For anything sizeable, open an issue first. Maintained by
@matthewmyrick.