Clawbolt is open source and contributions are welcome.
- Fork the repository on GitHub
- Clone your fork and set up local development
- Create a branch for your changes
- Make your changes and ensure all checks pass
- Open a pull request
For development, you can run Clawbolt directly with Python and uv (no Docker required).
- Python 3.11+
- uv (Python package manager)
- PostgreSQL (for data storage)
pip install uv
uv synccp .env.example .envEdit .env with your credentials. At minimum:
- An LLM API key
- At least one messaging channel:
- iMessage: pick one backend. Linq (
LINQ_API_TOKEN+LINQ_FROM_NUMBER) for hosted iMessage/RCS/SMS, or BlueBubbles (BLUEBUBBLES_SERVER_URL+BLUEBUBBLES_PASSWORD) for a self-hosted bridge. The app surfaces either as a single "iMessage" channel to users. Configuring both at once is not supported. - Telegram:
TELEGRAM_BOT_TOKENandTELEGRAM_ALLOWED_CHAT_ID
- iMessage: pick one backend. Linq (
Create the development database:
createdb -U clawbolt clawboltRun migrations:
uv run alembic upgrade headuv run uvicorn backend.app.main:app --reloadThe server starts on http://localhost:8000.
File storage is exposed through the Google Drive integration. To exercise the file tools (upload_to_storage, find_saved_files, etc.), set GOOGLE_DRIVE_CLIENT_ID and GOOGLE_DRIVE_CLIENT_SECRET in .env, restart the server, and ask the assistant in chat to "connect google drive" (it calls manage_integration and replies with an OAuth link). Without those env vars, the file tools stay hidden, which is fine for working on anything other than file flows. See Google Drive Setup for the OAuth client walkthrough.
Without Docker, you need a tunnel to give messaging providers a public URL:
# Install cloudflared, then:
cloudflared tunnel --url http://localhost:8000If using the iMessage channel backed by Linq, the webhook registers automatically when the server detects the tunnel URL. If using BlueBubbles as the iMessage backend, configure the webhook on your BlueBubbles server per its setup guide.
If using Telegram, copy the tunnel URL and register the webhook manually:
curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
-H "Content-Type: application/json" \
-d '{"url": "https://<tunnel-url>/api/webhooks/telegram"}'See Linq Setup, BlueBubbles Setup, or Telegram Setup for details.
Clawbolt uses pytest with FastAPI's TestClient. Tests require a running PostgreSQL instance with a clawbolt_test database.
Tests connect to postgresql://clawbolt:clawbolt@localhost:5432/clawbolt_test. The conftest handles table creation and per-test transaction rollback automatically.
# Create the test database (one-time setup)
createdb -U clawbolt clawbolt_testuv sync --all-extras
uv run pytest -vStore isolation: The _isolate_file_stores autouse fixture patches settings.data_dir and calls reset_stores() to clear cached store singletons between tests. Each test runs in a database transaction that is rolled back after the test completes.
Mock factories: All external services are mocked in tests. Mock factories live in tests/mocks/:
| Mock | What it replaces |
|---|---|
| Telegram | Telegram Bot API calls |
| LLM | any-llm acompletion calls |
| Storage | MockStorageBackend for Google Drive operations |
Auth override: The get_current_user dependency is overridden in tests to return a fixed test user, bypassing authentication.
- Type annotations required on all functions
- Ruff for linting and formatting (rules:
E, F, I, UP, B, SIM, ANN, RUF) - Line length: 100 characters
- Pydantic v2 for all data classes and request/response schemas
- Async routes: all route handlers use
async def - LLM calls: all LLM calls via any-llm
acompletion(async)
Use conventional commit prefixes:
| Prefix | Use for |
|---|---|
feat: |
New features |
fix: |
Bug fixes |
docs: |
Documentation changes |
refactor: |
Code refactoring |
test: |
Adding or updating tests |
ci: |
CI/CD changes |
chore: |
Maintenance tasks |
Every change should pass all checks:
uv run pytest -v # tests pass
uv run ruff check backend/ tests/ # lint passes
uv run ruff format --check backend/ tests/ # format passes
uv run ty check --python .venv backend/ tests/ # type checking passes- Bug fixes include regression tests
- New features include appropriate tests
- Documentation is updated if needed
- Every data class and endpoint uses
user_idscoping - External services are abstracted behind service classes in
backend/app/services/ - Config uses Pydantic
BaseSettingswithextra="ignore"