docs: add Claude AI assistant rules and configuration
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:
57
README.md
57
README.md
@@ -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 🚧
|
||||
|
||||
|
||||
Reference in New Issue
Block a user