This document records the invariants Tickwise enforces and the trust boundaries it crosses, so anyone reviewing the codebase can verify that new code doesn't break them.
| Boundary | Direction | Authentication |
|---|---|---|
Loopback HTTP API (127.0.0.1:19532) |
Inbound from local processes only | None — bound to loopback |
| Cloudflare Tunnel ingress | Inbound from internet | Bearer token (/api/mobile/*) or unguessable URL token (/api/calendar/feed/*.ics) |
| LLM API (Anthropic / OpenAI) | Outbound | API key from OS keyring |
| Calendar provider (CalDAV / Google) | Outbound | Per-provider credentials, OS keyring |
These properties must hold for every release. The matching tests live
under tests/integration/test_security.py.
- API binding — uvicorn binds
127.0.0.1only. Verified by inspectingtickwise.config.API_HOST. - Cloudflare ingress allowlist — when active, the tunnel exposes
only
api/calendar/feed/.*andapi/mobile/.*. Verified by inspecting the ingress config emitted bytickwise.cloudflare.api_client. - No plaintext API keys in DB —
llm_config.api_key_refstores a keyring alias, not the key itself. Verified by ensuring no row inllm_configever contains a value matching the heuristic for a real API key. - Mobile bearer token is unguessable — 64 hex chars (256 bits) and only the SHA-256 hash is persisted.
- ICS feed token is unguessable — 32 hex chars (128 bits).
- No raw OCR text on disk — only the first 200 chars of redacted
text land in
activities.redacted_text. Verified by reading the capture-loop persistence code. - No screenshots ever written to disk — verified by inspecting the
Screenshotdataclass usage; bytes never leave RAM. - Backup excludes secrets — the
/api/backup/exportallowlist does not includemobile_auth_tokens,classification_cache,llm_usage_log, orredaction_log.
The browser extension and the OCR pipeline both consume untrusted content. Tickwise must:
- Treat OCR text as data, never as instructions.
- Apply redaction before sending to any LLM or external service.
- Apply the user's domain blocklist on the extension side, not just server-side, so excluded data never leaves the browser.
Security issues should be filed privately. There's no published email
yet — file a private GitHub security advisory on
code-lodge/tickwise.