docs: document fatal-log and training-log sync endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# Fatal Log & Training Log Sync Endpoints
|
||||
|
||||
## What was implemented
|
||||
|
||||
Two new REST endpoints on the LicenseServer to receive and store crash/training data from AITM clients.
|
||||
|
||||
## Files changed (LicenseServer project)
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `src/shared-types.ts` | Added `FatalLogRecord`, `TrainingLogRecord`, `FatalLogRow`, `TrainingLogRow` types |
|
||||
| `src/constants.ts` | Added `FATAL_LOG_DEDUP_WINDOW_MS = 5 * 60_000` (5-minute dedup window) |
|
||||
| `src/db/GlobalDatabase.ts` | Added `fatal_logs` + `training_logs` tables; methods: `insertFatalLog`, `checkFatalDuplicate`, `insertTrainingLog`, `insertTrainingLogs` |
|
||||
| `src/rest/authMiddleware.ts` | New: JWT Bearer token extraction middleware — extracts `licenseId` from payload via base64url decode (no signature verification) |
|
||||
| `src/rest/SyncApi.ts` | New: `POST /api/v1/sync/fatal` and `POST /api/v1/sync/logs` handlers |
|
||||
| `src/server.ts` | Wired `setupSyncApi(app, ctx)` after `setupRestApi` |
|
||||
| `src/__tests__/SyncApi.test.ts` | 11 unit tests (all passing) |
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/v1/sync/fatal
|
||||
|
||||
Receives automatic fatal crash reports from AITM clients.
|
||||
|
||||
**Auth**: JWT Bearer token in `Authorization` header — `licenseId` extracted from payload.
|
||||
|
||||
**Request body** (`FatalLogRecord`):
|
||||
```json
|
||||
{
|
||||
"fatalError": "TypeError: Cannot read property 'x' of undefined",
|
||||
"fatalSource": "architect",
|
||||
"fatalTimestamp": "2026-03-30T00:00:00Z",
|
||||
"contextWindow": [...],
|
||||
"environment": {
|
||||
"nodeVersion": "v20.0.0",
|
||||
"processUptime": 3600,
|
||||
"memoryUsage": 512
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{ "ok": true, "id": "<uuid>" }
|
||||
```
|
||||
|
||||
**Deduplication**: if the same `fatalError` + `fatalSource` arrives from the same `license_id` within 5 minutes, the record is stored with `is_duplicate = true`.
|
||||
|
||||
---
|
||||
|
||||
### POST /api/v1/sync/logs
|
||||
|
||||
Receives user-submitted training logs (for AI model improvement).
|
||||
|
||||
**Auth**: JWT Bearer token (same as above).
|
||||
|
||||
**Request body**:
|
||||
```json
|
||||
{
|
||||
"logs": [
|
||||
{
|
||||
"projectId": "cr-fe",
|
||||
"taskNumber": 42,
|
||||
"logType": "prompt",
|
||||
"content": "<base64-encoded bytes>",
|
||||
"outcome": "success",
|
||||
"relatedFiles": ["src/foo.ts"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{ "ok": true, "count": 3 }
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### `fatal_logs`
|
||||
|
||||
```sql
|
||||
CREATE TABLE fatal_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
license_id UUID REFERENCES licenses(id),
|
||||
project_id TEXT,
|
||||
task_number INTEGER,
|
||||
fatal_error TEXT NOT NULL,
|
||||
fatal_source TEXT NOT NULL,
|
||||
fatal_timestamp TIMESTAMPTZ NOT NULL,
|
||||
context_window JSONB NOT NULL,
|
||||
context_size INTEGER NOT NULL,
|
||||
node_version TEXT,
|
||||
process_uptime INTEGER,
|
||||
memory_usage INTEGER,
|
||||
active_task_ids JSONB,
|
||||
root_cause TEXT,
|
||||
resolution TEXT,
|
||||
category TEXT,
|
||||
is_duplicate BOOLEAN DEFAULT false,
|
||||
received_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX idx_fatal_license ON fatal_logs(license_id);
|
||||
CREATE INDEX idx_fatal_source ON fatal_logs(fatal_source);
|
||||
```
|
||||
|
||||
### `training_logs`
|
||||
|
||||
```sql
|
||||
CREATE TABLE training_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
license_id UUID REFERENCES licenses(id),
|
||||
project_id TEXT NOT NULL,
|
||||
task_number INTEGER,
|
||||
log_type TEXT NOT NULL,
|
||||
content BYTEA NOT NULL,
|
||||
outcome TEXT,
|
||||
related_files JSONB,
|
||||
submitted_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
## Key design decisions
|
||||
|
||||
- **No JWT signature verification** — the license server trusts its own issued tokens; `licenseId` is extracted by base64url-decoding the payload only. Full verification is handled by the auth layer.
|
||||
- **`contextWindow` as JSONB** — stored as JSON string (TEXT in SQLite dev, JSONB in PostgreSQL prod) for flexible querying.
|
||||
- **`content` as BLOB** — training log content stored as raw bytes (`BYTEA` in PostgreSQL).
|
||||
- **`globalDb` unavailability** — if the global database is not configured, both endpoints respond with `503 Service Unavailable` (non-crashing, with a startup warning).
|
||||
- **Dedup window**: `FATAL_LOG_DEDUP_WINDOW_MS = 300_000` (5 minutes), indexed by `(license_id, fatal_error, fatal_source)`.
|
||||
|
||||
## Limitations
|
||||
|
||||
- No request body size limit is configured. Large `contextWindow` arrays may exceed Express's 100 KB default. Add `express.json({ limit: '5mb' })` to sync routes if payloads grow.
|
||||
Reference in New Issue
Block a user