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

@@ -1,13 +1,11 @@
"""WebSocket API — Real-time call events and audio classification stream."""
import asyncio
import logging
import secrets
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from api.deps import get_gateway
from config import get_settings
from auth import is_owner, resolve_from_header_or_query
from db.database import session_scope
from models.events import EventType, GatewayEvent
logger = logging.getLogger(__name__)
@@ -17,21 +15,21 @@ router = APIRouter()
async def _authorize(websocket: WebSocket) -> bool:
"""
Check the static bearer token before accepting the socket.
Require the owner before accepting the socket.
Browsers can't set headers on WebSocket connects, so a `token`
query parameter is accepted alongside the Authorization header.
Browsers can't set headers on WebSocket connects, so the Casdoor JWT (or
a PAT) is accepted on the `?token=` query param alongside the Authorization
header — the same narrow fallback the recording download uses. In dev mode
the owner resolves tokenlessly. A non-owner or absent credential closes the
socket with code 4401.
"""
token = get_settings().api_token.get_secret_value()
if not token:
q_token = websocket.query_params.get("token")
auth_header = websocket.headers.get("authorization")
async with session_scope() as session:
user = await resolve_from_header_or_query(session, auth_header, q_token)
if user is not None and is_owner(user):
return True
supplied = websocket.query_params.get("token", "")
auth = websocket.headers.get("authorization", "")
if auth.lower().startswith("bearer "):
supplied = auth[7:]
if secrets.compare_digest(supplied, token):
return True
await websocket.close(code=4401, reason="Missing or invalid bearer token")
await websocket.close(code=4401, reason="Owner authentication required")
return False