Thanks for your interest. zero is a focused tool with a clear scope: keep your inbox at "only what still needs you" and never lose anything. The best contributions stay true to that one job.
See also: Architecture · API reference · Maintenance checklist · Security
zero is published under the PolyForm Noncommercial 1.0.0 license. By contributing, you agree that your changes will be distributed under the same terms. This means the project -- and your contribution -- may not be used commercially or resold. If that is a concern, please raise it before putting in the work.
- macOS 26+ (Apple Silicon)
- Xcode command-line tools:
xcode-select --install - Python 3
gwsCLI authenticated for at least one Gmail accountclaudeCLI configured
cd macapp
./build.sh # compiles zero.app into macapp/build/To produce a distributable disk image:
./make-dmg.sh # produces zero.dmg in macapp/You do not need the macOS app running to work on the Python layer. Start the server directly:
python3 lib/keeper_server.pyThe server binds to 127.0.0.1 and serves the JSON API the native app uses.
Use ./bin/zero run to trigger a sweep from the command line, or POST /api/run to the server.
For Gmail operations you need accounts.json configured (see the
accounts.json.example template) and gws auth login run for each account.
review_open_loops.py runs dry by default (no changes applied):
python3 lib/review_open_loops.py <gws_config_dir> <account_label>Pass --execute to apply label changes. Use this to iterate on the judgment
logic and the keep-policy.md prompt without touching production mail.
lib/ Python server and scripts (the core)
keeper_server.py Local HTTP server (stdlib only, no deps)
review_open_loops.py Per-thread keep/archive judgment (calls claude CLI)
dashboard_state.py Builds app/state.json from Gmail + learning store
inbox_zero.py gws wrapper and thread operations
learning.py Keeps-rule learning and inference
...
macapp/ SwiftUI menu-bar app (thin shell)
Sources/ Swift source files
build.sh Build script (produces .app)
make-dmg.sh DMG packaging
app/
state.json Cached state (gitignored, written at runtime by dashboard_state.py)
keep-policy.md The plain-English keep policy (the only user config)
accounts.json Per-account registry (gitignored, from accounts.json.example)
knowledge/ Optional voice-grounding files (gitignored)
docs/ Architecture and pipeline docs
The judgment pipeline is: Swift app starts keeper_server.py, which spawns
review_open_loops.py per account, which calls the claude CLI (Haiku) with the
thread contents and keep-policy.md, then applies reversible label changes via the
gws CLI.
Match the style of the file you are editing. The codebase has a consistent voice; do not introduce a different style in the same file.
Python (lib/):
- Stdlib only for
keeper_server.pyand the core server path. No third-party dependencies in the hot path. - Use subprocess calls to
gwsandclaudeCLIs the same way existing code does. Do not add SDK imports that are not already there. - Keep functions short and named after what they do, not how they do it.
Swift (macapp/):
- The Swift shell is deliberately thin. If something can be done in Python (where the logic already lives), do it there. Only put things in Swift that genuinely need native macOS integration.
- Match the SwiftUI patterns already in
Sources/.
General:
- Make surgical changes. One logical change per PR; do not refactor adjacent code in the same diff unless you have a specific reason.
- Dry-run first. For any Gmail operation, verify behavior with
--executeoff before turning it on. - The keep policy is the product. Changes that blur the reversibility guarantee or that add irreversible side effects need a very strong justification.
- Open an issue first for anything beyond a clear bug fix or small improvement. Describe the problem you are solving, not the solution. This avoids duplicate work and lets us align on scope before you write code.
- Keep PRs focused. One fix or feature per PR.
- Include a brief test plan in the PR description: how you verified the change works and does not break anything. A dry-run log, a before/after inbox count, or a screenshot of the panel is usually enough.
- The core invariant is that nothing is ever deleted and every archive is reversible. Any PR that risks that property will not be merged.
The docs/api/openapi.json spec is hand-written (no codegen). When you change an
endpoint — path, parameters, request body, response shape — update it manually. Validate:
python3 -m json.tool docs/api/openapi.json >/dev/null && echo OKPreview the Scalar render locally:
python3 -m http.server 8080 --directory docs/api
open http://localhost:8080See docs/MAINTENANCE.md for the full list of what to keep in sync on each change.
This section is for maintainers.
Before running bin/release:
- Bump the version in
macapp/Sources/zero-Info.plist(CFBundleShortVersionString). - Add a
## [X.Y.Z] - YYYY-MM-DDsection toCHANGELOG.md(Keep a Changelog 1.1.0). Move items out of## [Unreleased]into the new section. - Commit and push
master(the script only pushes the tag, not the branch):git push origin master
- Run the release script:
The script verifies the working tree is clean on
bin/release X.Y.Z
master, confirms the changelog section exists, builds the.appand.dmg, creates and pushes thevX.Y.Ztag, publishes a GitHub release with the changelog notes and.dmgattached, then installs the new app to/Applications/zero.app.
Known caveat — unsigned/un-notarized binary. The .dmg is not signed or notarized.
Users must right-click → Open on first launch to bypass Gatekeeper.
Open an issue with:
- macOS version
- What you did
- What you expected
- What happened (include any log output from
logs/if relevant)
For security issues, see SECURITY.md.