Developer guide¶
chatwire is an open-source project written in Python (bridge + web server) and TypeScript (React frontend + mobile app). This guide covers the architecture, how to contribute, and how to build plugins and themes.
Tech stack¶
| Layer | Technology |
|---|---|
| Bridge | Python 3.10+, asyncio, sqlite3 |
| Web server | FastAPI + uvicorn |
| Frontend | React 18, TypeScript, Vite, TanStack Query, Zustand |
| Mobile | React Native + Expo (iOS + Android) |
| Plugin SDK | Python, chatwire-sdk package |
| Process management | macOS launchd |
| Tests | pytest (Python), vitest (frontend), Jest (mobile) |
Quick start for contributors¶
git clone https://github.com/allenbina/chatwire.git
cd chatwire
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/chatwire install-agents
.venv/bin/chatwire doctor
See Contributing for the full setup.
Guide pages¶
| Page | What you'll find |
|---|---|
| Architecture | Process model, data flow, plugin sandbox, config format |
| Contributing | Clone, dev install, tests, PR process |
| Plugin SDK | BaseIntegration, hooks, slot API, settings schema |
| Theme format | CSS variable reference, packaging, custom CSS |
| API reference | REST endpoints, SSE, MCP tools |
| Testing | pytest + vitest + Jest, CI, coverage |
| Release process | Versioning, PyPI, Homebrew tap, changelogs |
Repo layout¶
bridge.py Message relay loop + integration dispatcher
chat_db.py Reads chat.db, HEIC → JPEG via sips
chat_send.py osascript wrappers + anti-spam guard
config.py config.json loader and migrator
chatwire_cli.py CLI: setup / install-agents / doctor / logs / status / migrate / mcp
contacts.py Contacts.app → handle/name lookup
echo_log.py Cross-process echo dedup
whitelist.py Runtime-mutable contact allowlist
rules.py Automation rules engine
_version.py Semver source of truth
verify.py Plugin signature verification
integrations/ Built-in plugins (web, stats, mcp, favorites)
chatwire-plugins/ Standalone plugin packages (ntfy, telegram, webhook, mqtt, ha, xmpp)
packages/sdk/ chatwire-sdk Python package (BaseIntegration, plugin CLI)
packages/shared/ @chatwire/shared TypeScript types + ChaiwireClient
packages/mobile/ React Native + Expo mobile app
web/ FastAPI web server
main.py App entry point, middleware, auth, health endpoints
api_v1.py REST API router
api_ui.py UI-specific API router (stats, themes, etc.)
auth.py Cookie session + Bearer token auth
api_keys.py API key store and scope enforcement
setup_wizard.py First-run wizard routes
themes.py Theme discovery and picker
log_stream.py Structured JSONL logger
registry.py Plugin marketplace registry
version_check.py Update notification
web/frontend/ React SPA
src/components/ UI components (MessageList, ComposeBox, Layout, …)
src/pages/ Route-level pages (ChatPage, SettingsPage, PopoutPage)
src/hooks/ Custom React hooks (useTheme, useSounds, useServerEvents)
src/plugins/ Plugin slot system (registry, SlotRenderer, StatsWidget)
e2e/ Playwright E2E + axe accessibility tests
migrations/ Config schema migration runner
templates/launchd/ Plist templates rendered by install-agents
scripts/ install.sh, chatwire-loop.sh
docs/ This documentation
docs/wiki/ (legacy — superseded by docs/ tree)
Key design decisions¶
- Two separate processes — the bridge and web server are independent launchd agents. They communicate via files on disk (no IPC socket). This means the web UI keeps working if the bridge crashes, and vice versa.
- Flat Python layout — top-level modules instead of a
chatwire/package. Avoids a sweeping import rewrite;py-modulesinpyproject.tomlhandles packaging. - Plugin sandbox — plugins run through a
SandboxedContextthat restricts data access by tier. Raw phone numbers and emails never reach plugin code directly. - No hot-reload — the bridge reads
config.jsonon restart, not dynamically. This keeps the process model simple. - Zero dependencies for the bridge core — the bridge itself only uses the Python stdlib (sqlite3, asyncio, subprocess). FastAPI and its dependencies are only needed by the web process.