BriefBoard is a small but complete AI SaaS: tenants upload documents, then ask questions or get summaries over their own documents. It demonstrates the concerns a real AI product has beyond the model — API-key auth, per-tenant data isolation, streaming responses, usage metering, and a cost guardrail — with a working web UI.
- Multi-tenant: every request is scoped to the tenant resolved from its API key; one tenant can never see another's documents.
- Streaming: answers stream token-by-token over Server-Sent Events.
- Metered + budgeted: every call records input/output tokens and cost; a per-tenant monthly budget hard-stops spending (HTTP 402).
- Real model: Claude via the Anthropic SDK; RAG over the tenant's own docs.
flowchart LR
UI[Web UI / API client] -->|X-API-Key| AUTH[auth: resolve tenant]
AUTH --> G{cost guardrail}
G -->|over budget| B[402]
G -->|ok| R[per-tenant RAG retrieval]
R --> LLM[Claude stream]
LLM -->|SSE tokens| UI
LLM --> M[record usage + cost]
M --> DB[(SQLite: tenants · documents · usage)]
The multi-tenancy boundary is row-level: documents and usage are always
queried with WHERE tenant_id = ?. See docs/architecture.md.
| Method | Path | Purpose |
|---|---|---|
| POST | /documents | Upload a document (tenant-scoped) |
| GET | /documents | List the tenant's documents |
| POST | /ask | Ask across the tenant's docs — streams SSE |
| POST | /summarize | Summarize one of the tenant's docs |
| GET | /usage | Month-to-date tokens, cost, budget remaining |
| GET | / | Web UI |
All except / and /health require the X-API-Key header.
make setup
make test# full suite — no API key needed (fake LLM)
cp .env.example .env # add ANTHROPIC_API_KEY to actually answer/summarize
make run # UI at http://localhost:8000Seeded demo tenants: demo-acme-key and demo-globex-key (each with a $5/mo budget).
curl -s -X POST localhost:8000/documents -H 'X-API-Key: demo-acme-key' \
-H 'content-type: application/json' -d '{"title":"Notes","text":"The launch is in March."}'
curl -N -X POST localhost:8000/ask -H 'X-API-Key: demo-acme-key' \
-H 'content-type: application/json' -d '{"question":"When is the launch?"}'briefboard/
├── app/
│ ├── main.py # FastAPI app + endpoints
│ ├── auth.py # API-key -> tenant
│ ├── db.py # SQLite, tenant-scoped queries
│ ├── metering.py # cost calc + budget guardrail
│ ├── retrieval.py # per-tenant TF-IDF RAG
│ ├── llm.py # Claude streaming + summarize (Anthropic SDK)
│ ├── schemas.py · config.py · seed.py
├── static/index.html # web UI (vanilla JS, consumes the SSE stream)
├── tests/test_api.py # auth, isolation, streaming, metering, budget
└── docs/architecture.md
- The UI is a self-contained functional demo (vanilla JS). A production build would typically be a Next.js frontend against the same API; the backend contract is identical.
- SQLite keeps the demo one-command-runnable; the data layer is small enough to port
to Postgres (with a
tenant_idFK and row-level security) without changing the API.
MIT — see LICENSE.