learning_ai_invt_trdg/docs/BACKEND_AUDIT_SCHEMA.md
Saravana Achu Mac d955d00c00 feat(contracts): shared socket namespaces, tab flags, cutover docs, README
- shared/realtime.ts: add SOCKET_NAMESPACES constants (/trading, /admin, root)
- shared/feature-flags.ts: add tabs.marketplace and tabs.membership to
  TradingFeatureFlagsResponse; add FEATURE_FLAG_KEYS constants
- .env.example: remove /api suffix from VITE/NEXT_PUBLIC trading URL vars
  (web appends /api itself); add tab visibility flag vars with comments
- web: add useTabFeatureFlags hook + DOM test; wire tab visibility into App.tsx
- web/vite.config.ts: finalize build config
- mobile/providers/TradingDataProvider.tsx: deriveSocketParams for proxy-safe
  socket origin/path resolution (already landed upstream, conflict resolved)
- docs: add CUTOVER_WEB.md, CUTOVER_MOBILE.md checklists; update OPERATIONS.md
  with Docker commands and resolved gap log; update ROADMAP.md to Done;
  add BACKEND_AUDIT_SCHEMA.md, BACKEND_API_DEPRECATION.md, CONVENTIONS.md;
  add audit-events container entry to AZURE_INFRASTRUCTURE.md
- README.md: full rewrite with workspace table, arch summary, env var reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 19:35:53 -04:00

143 lines
5.2 KiB
Markdown

# Backend Admin Audit Schema
## Purpose
This document defines the `TradeAuditEvent` schema used by the trading backend to log all
admin-facing, operator-facing, and safety-critical state changes. Every audit event is
written to the structured logger via `logger.info('[AUDIT] {...}')` with a UTC timestamp
prepended.
Audit events are not yet persisted to Cosmos — they are log-only. Downstream log aggregation
(e.g., Azure Monitor, Log Analytics) is the durable store for audit history.
---
## TradeAuditEvent Schema
```typescript
interface TradeAuditEvent {
event: string; // Required. Identifies the audit event type (see catalogue below).
userId?: string; // Auth user ID performing or triggering the action.
profileId?: string; // Trading profile ID relevant to the action (if applicable).
symbol?: string; // Asset symbol relevant to the action (if applicable).
outcome?: 'accepted' // Action was applied.
| 'rejected' // Action was blocked by a rule or safety guard.
| 'error'; // Action failed due to a runtime error.
reason?: string; // Human-readable explanation for the outcome.
details?: Record<string, unknown>; // Structured metadata specific to the event type.
}
```
Log line format (written via `logger.info`):
```json
{
"ts": "2026-04-07T10:00:00.000Z",
"event": "manual_order_created",
"userId": "user-abc",
"profileId": "profile-xyz",
"symbol": "BTC/USDT",
"outcome": "accepted",
"reason": "within risk limits",
"details": { "side": "BUY", "qty": 0.01, "allocatedCapital": 1000 }
}
```
---
## Event Catalogue
All events are emitted via `auditTradeEvent()` in `backend/src/services/apiServer.ts`.
| Event | Trigger | Key details fields |
|---|---|---|
| `manual_order_created` | POST `/api/orders/manual` — order submitted | side, qty, symbol, profileId |
| `manual_order_rejected` | Manual order blocked by risk/capital guard | reason, guard name |
| `manual_order_error` | Manual order failed at execution | error message |
| `profile_control_create` | Chat AI creates a new trading profile | profileName, allocatedCapital |
| `profile_control_update` | Chat AI updates an existing profile | profileId, updatedFields |
| `profile_control_error` | Chat AI profile action fails | error message |
| `chat_profile_control` | Generic chat-initiated profile action | action type, profileId |
| `trading_paused` | POST `/internal/trading/pause` — admin pauses trading | pausedBy, reason |
| `trading_resumed` | POST `/internal/trading/resume` — admin resumes trading | resumedBy |
| `backfill_reverted` | POST `/internal/trading/revert-backfill` — admin reverts exit backfill | symbol, profileId |
| `reconciliation_audit` | GET `/api/reconciliation/backfill/audit` — admin reads reconciliation audit | — |
| `position_closed_manual` | Admin/operator manually closes a position | symbol, profileId, tradeId |
| `order_failure` | Order execution failure recorded | side, qty, reason, tradeId |
> Not all events use every field. Absent fields are omitted from the log payload.
---
## Audit Middleware
The `auditTradeEvent()` private method in `ApiServer` writes directly to the logger:
```typescript
private auditTradeEvent(evt: TradeAuditEvent): void {
const payload = { ts: new Date().toISOString(), ...evt };
logger.info(`[AUDIT] ${JSON.stringify(payload)}`);
}
```
All audit calls are synchronous and never throw — audit failure must not block the primary
operation.
---
## Admin-Scoped Endpoints
Routes that require `requireAdmin` middleware are the primary sources of audit events:
- `POST /internal/trading/pause`
- `POST /internal/trading/resume`
- `POST /internal/trading/revert-backfill`
- `GET /api/reconciliation/backfill/audit`
- `POST /api/admin/dynamic-config`
All admin actions must produce an audit event with `userId` set from `authUserId` on the
authenticated request.
---
## Persistence
Audit events are written to two sinks simultaneously:
1. **Structured log** — always, via `logger.info('[AUDIT] ...')`. Zero runtime dependency.
2. **Cosmos `audit-events` container** — best-effort, via `persistAuditEvent()` in
`auditRepository.ts`. Silently skipped if Cosmos is not configured or the write fails.
### Activating Cosmos Persistence
Create the container in your Cosmos database:
| Setting | Value |
|---|---|
| Container name | `audit-events` |
| Partition key | `/productId` |
| TTL (Time To Live) | `7776000` seconds (90 days) |
| Throughput | Shared or dedicated — start with 400 RU/s |
Once the container exists and `COSMOS_ENDPOINT` / `COSMOS_KEY` are configured, all
`auditTradeEvent()` calls will persist records automatically.
## Admin Audit Endpoint
`GET /api/admin/audit` (admin-only) queries the `audit-events` container:
```
GET /api/admin/audit?userId=user-123&event=manual_order_created&since=1712500000000&limit=50
```
Query parameters (all optional):
| Parameter | Type | Description |
|---|---|---|
| `userId` | string | Filter by user ID |
| `event` | string | Filter by event name (exact match) |
| `since` | number | Unix epoch ms — return events newer than this timestamp |
| `limit` | number | Max records to return (default 100, max 500) |
Response: `{ records: AuditEventDocument[], count: number }`