# CI Agent — Competitive Intelligence Monitoring CI Agent monitors companies you choose, collects publicly available information from multiple sources on a schedule, analyzes it with an LLM into an evidence-linked report, detects meaningful changes between runs, and alerts you by email (and optionally SMS) with a severity and confidence score. See [`PLAN.md`](PLAN.md) for the build strategy, [`ARCHITECTURE.md`](ARCHITECTURE.md) for system design, [`SECURITY.md`](SECURITY.md) for the threat model, [`TASKS.md`](TASKS.md) for the live implementation checklist, [`KNOWN_LIMITATIONS.md`](KNOWN_LIMITATIONS.md) for what's stubbed vs. fully live, [`DEPLOYMENT.md`](DEPLOYMENT.md) for running this in production behind Cloudflare/Nginx, and [`docs/FIREBASE_MIGRATION.md`](docs/FIREBASE_MIGRATION.md) for a (skeptical) look at what moving to Firebase would take. A live reference deployment runs at [ciagent.org](https://ciagent.org), with its own code hosted on a self-hosted Gitea instance at [git.ciagent.org](https://git.ciagent.org) (public read, admin-only write) — click "Git Repository" on the landing page. ## Quick start (Docker) ```bash git clone cd ci-agent cp .env.example .env docker compose up --build ``` Then visit: | Service | URL | |---|---| | Frontend | http://localhost:3000 | | Backend API | http://localhost:8000 | | API docs (Swagger) | http://localhost:8000/docs | The default `.env.example` runs in `AUTH_MODE=local` (no login required, fixed dev user) with `LLM_PROVIDER=mock` and `SEARCH_PROVIDER=mock` — the whole demo workflow works with zero paid API keys. `api`, `worker`, and `beat` build from the same Dockerfile but are separate images — after adding a Python dependency, run `docker compose build api worker beat` (not just `restart`) or they'll crash with `ModuleNotFoundError` on stale images. Similarly, after adding an npm dependency to `apps/web`, a plain rebuild isn't enough either — `web`'s `node_modules` *and* `.next` are both persistent anonymous Docker volumes that survive `docker compose build web` and even a plain `docker compose restart web`. This means a new package can still 404, and — on Windows + Docker Desktop — an edited existing file can keep serving its pre-edit output after a restart, since the on-disk `.next` build cache isn't cleared by a restart. Run `docker compose rm -f -s -v web && docker compose up -d web` to actually pick up new packages or force a clean recompile. ## Running without Docker **Backend:** ```bash cd apps/api python -m venv .venv ./.venv/Scripts/activate # or `source .venv/bin/activate` on macOS/Linux pip install -e ".[dev]" cp ../../.env.example ../../.env # edit DATABASE_URL to the sqlite line if you don't have Postgres running alembic upgrade head uvicorn app.main:app --reload ``` **Frontend:** ```bash cd apps/web npm install npm run dev ``` ## Common commands ```bash # Database migrations cd apps/api && alembic upgrade head cd apps/api && alembic revision --autogenerate -m "description" # Run a Celery worker + beat (only needed outside Docker) cd apps/api && celery -A app.tasks.celery_app worker --loglevel=INFO -Q default,collection,analysis,notifications,maintenance,enrichment cd apps/api && celery -A app.tasks.celery_app beat --loglevel=INFO # Backend tests / lint / format cd apps/api && pytest cd apps/api && ruff check app tests cd apps/api && black app tests # Frontend tests / lint / format / typecheck cd apps/web && npm test cd apps/web && npm run lint cd apps/web && npm run format cd apps/web && npm run typecheck # End-to-end test (requires `docker compose up -d` already running) cd apps/web && npx playwright install --with-deps chromium # one-time cd apps/web && npm run e2e ``` ## Configuration **A fresh clone ships with zero API keys, on purpose.** `.env` is gitignored and never committed; `.env.example` — the only thing that *is* committed — leaves every paid provider blank. There's no "secret" hidden somewhere in the repo history either; nothing paid has ever been checked in. Clone it, `docker compose up`, and the entire demo workflow (register/login, add a company, run a monitor, get a report, get an alert) works immediately against mock LLM/search providers and console-logged notifications — no signups, no billing, no keys, ever required just to try it. Every paid/external provider defaults to a mock/console implementation this way. Automated tests always run against mocks too, and never call a paid API. ### Adding real provider keys Once you have your own API keys, there are two independent places to put them, and they serve different purposes: 1. **`.env` (deployment-wide defaults)** — see [`.env.example`](.env.example) for the full list with comments. The key ones: - `AUTH_MODE=local|jwt` — local single-user dev mode vs. real email/password accounts. - `LLM_PROVIDER=mock|anthropic|ollama|gemini` — set `ANTHROPIC_API_KEY`/`ANTHROPIC_MODEL`, `OLLAMA_BASE_URL`/`OLLAMA_MODEL`, or `GEMINI_API_KEY`/`GEMINI_MODEL` to go live. Gemini has a genuine free tier — grab a key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey) — so it's the cheapest provider to actually try against a real model. - `SEARCH_PROVIDER=mock|brave` — set `BRAVE_SEARCH_API_KEY` to go live. - `NINJAPEAR_API_KEY` / `USPTO_API_KEY` — optional company-enrichment and patent-search providers, both free-tier-friendly to start. - `SMTP_HOST`/`RESEND_API_KEY` — no bundled local mail sink; point SMTP at a real relay or set `RESEND_API_KEY` to actually test email delivery (alerts and security email both use this). - `NOTIFICATION_SMS_ENABLED=false` by default — set to `true` and provide `TWILIO_*` to enable SMS. `LLM_PROVIDER`/`SEARCH_PROVIDER` are provider *selection* and only live here — after changing them, rebuild/restart `api`, `worker`, and `beat` to pick up the change. 2. **The app's own Settings page (no `.env` edit, no restart)** — once the app is running, sign in and go to Settings: - **"Your API keys"** — every account can set its own Anthropic/Brave/NinjaPear/USPTO key, used for that account's own companies. Falls back to the `.env` value if you never set one. - **"Server secrets"** (admin accounts only) — Cloudflare Turnstile site key/secret and the Resend API key, shared server-wide, stored encrypted in the database, and take effect immediately for every visitor. This is the intended way to configure these three on a real deployment rather than editing `.env` directly. If `LLM_PROVIDER`/`SEARCH_PROVIDER` still show as `mock` in Settings → System configuration after you've set keys, that's the `.env`-level provider selection, not a missing key — it needs the `.env` edit + restart from option 1 above, not the Settings UI. ## Known limitations See [`KNOWN_LIMITATIONS.md`](KNOWN_LIMITATIONS.md).