docs: add Claude AI assistant rules and configuration
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 45s
CVE Scan & Docker Build / build-and-push (push) Successful in 1m53s

Add comprehensive rule documentation for AI-assisted development covering
authentication surfaces, outbound-call safety invariants, and other project
conventions to guide Claude's understanding of critical system behaviors.
This commit is contained in:
2026-07-28 19:01:38 -04:00
parent 016d8be71d
commit 4a3c14d4af
40 changed files with 2851 additions and 202 deletions

View File

@@ -172,13 +172,17 @@ pip install -e ".[dev]"
```bash
cp .env.example .env
# Edit .env with your SIP trunk credentials, LLM endpoint, etc.
# Required: DATABASE_URL, and API_TOKEN unless HOST=127.0.0.1
openssl rand -hex 32 # → API_TOKEN
# Required: DATABASE_URL, plus either the Casdoor SSO settings
# (CASDOOR_* + OWNER_NAME) or CASDOOR_ENABLED=false with HOST=127.0.0.1.
```
All REST, WebSocket, and MCP access requires `Authorization: Bearer
$API_TOKEN` (WebSocket also accepts `?token=...`). An empty token is only
permitted when bound to loopback.
The gateway is **owner-only**. The browser dashboard signs in via **Casdoor
SSO** (short-lived JWT); MCP and CLI clients use a **Personal Access Token**
(`hs_pat_…`) minted from the dashboard's *API Tokens* menu. Both are presented as
`Authorization: Bearer <token>` (WebSocket and `<audio>` recording downloads also
accept `?token=…`). Only the user whose Casdoor username matches `OWNER_NAME` may
use any surface — everyone else gets 403. With `CASDOOR_ENABLED=false` the gateway
runs in dev-owner mode, permitted **only** on a loopback bind.
### 3. Build the dashboard (optional but recommended)
@@ -204,6 +208,27 @@ uvicorn main:app --host 0.0.0.0 --port 8000
pytest tests/ -v
```
## Docker
A single image bundles the FastAPI process and the built dashboard (the node
stage compiles the SPA; `pjsua2` is deliberately not built, so the media
pipeline runs in stub mode — see the `Dockerfile` header). `docker-compose.yaml`
brings up the app plus its own PostgreSQL:
```bash
cp .env.compose.example .env
# Fill in HS_DB_PASSWORD, and CASDOOR_CLIENT_ID/SECRET + OWNER_NAME.
docker compose up --build
# → http://localhost:21081
```
Because the published port binds the app to `0.0.0.0`, the compose stack must run
with **Casdoor SSO enabled** — dev-owner mode (`CASDOOR_ENABLED=false`) is
loopback-only and is refused at startup here. Register a `hold-slayer` app in
Casdoor (org `heluca`, redirect URI `<PUBLIC_BASE_URL>/auth/callback`) first. The
image runs with `USE_MOCK_SIP=true` by default (a real trunk needs the
`SIP_TRUNK_*` vars and `USE_MOCK_SIP=false`).
## Usage
### REST API
@@ -212,7 +237,7 @@ pytest tests/ -v
```bash
curl -X POST http://localhost:8000/api/v1/calls/hold-slayer \
-H "Authorization: Bearer $API_TOKEN" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"number": "+18005551234",
@@ -264,7 +289,7 @@ curl -X PATCH http://localhost:8000/api/v1/routing/devices/dev_abc123/dnd \
### WebSocket — Real-Time Events
```javascript
const ws = new WebSocket(`ws://localhost:8000/ws/events?token=${API_TOKEN}`);
const ws = new WebSocket(`ws://localhost:8000/ws/events?token=${token}`);
ws.onmessage = (msg) => {
const event = JSON.parse(msg.data);
// event.type: "human_detected", "hold_detected", "ivr_step", etc.
@@ -276,11 +301,12 @@ ws.onmessage = (msg) => {
### MCP — AI Assistant Integration
The MCP server is served over **streamable HTTP at `/mcp/`** (note the
trailing slash) and authenticates with the same bearer token:
trailing slash) and authenticates with an owner-minted Personal Access Token
(mint one from the dashboard's *API Tokens* menu — it starts with `hs_pat_`):
```bash
claude mcp add hold-slayer --transport http http://localhost:8000/mcp/ \
--header "Authorization: Bearer $API_TOKEN"
--header "Authorization: Bearer hs_pat_..."
```
It exposes 15 tools and 3 resources (`gateway://status`,
@@ -333,7 +359,14 @@ All configuration is via environment variables (see `.env.example`):
| Variable | Description | Default |
|----------|-------------|---------|
| `DATABASE_URL` | PostgreSQL connection string | — (required) |
| `API_TOKEN` | Static bearer token for REST/WS/MCP | — (required unless `HOST=127.0.0.1`) |
| `CASDOOR_ENABLED` | Enable Casdoor SSO (false → dev-owner, loopback only) | `false` |
| `CASDOOR_ENDPOINT` | Casdoor base URL | `https://id.ouranos.helu.ca` |
| `CASDOOR_CLIENT_ID` | Casdoor application client ID | — (required if SSO on) |
| `CASDOOR_CLIENT_SECRET` | Casdoor application client secret | — (required if SSO on) |
| `CASDOOR_ORG_NAME` | Casdoor organization | `heluca` |
| `CASDOOR_APP_NAME` | Casdoor application name | — |
| `OWNER_NAME` | Casdoor username of the single operator (owner) | — (required if SSO on) |
| `PUBLIC_BASE_URL` | Public base URL for OAuth discovery (else derived from headers) | — |
| `MAX_CONCURRENT_CALLS` | Cap on simultaneous outbound calls | `4` |
| `SIP_TRUNK_HOST` | Your SIP provider hostname | — |
| `SIP_TRUNK_USERNAME` | SIP auth username | — |
@@ -408,13 +441,13 @@ Full documentation is in [`/docs`](docs/README.md):
### Phase 4: Production Hardening 🚧
- [x] Alembic database migrations (baseline + upgrade-on-boot)
- [x] API authentication — static bearer token across REST/WS/MCP
- [x] API authentication — Casdoor SSO (browser JWT) + owner-minted PATs, owner-only across REST/WS/MCP
- [x] Emergency-number guard + concurrent-call cap on outbound calls
- [ ] Rate limiting on API endpoints
- [ ] Structured JSON logging
- [x] Honest /health — engine mode, DB ping, trunk registration, STT/TTS availability
- [ ] Graceful degradation (classifier works without STT, etc.)
- [ ] Docker Compose (Hold Slayer + PostgreSQL)
- [x] Docker Compose (Hold Slayer + PostgreSQL)
### Phase 5: Additional Services 🚧