docs: document fatal-log and training-log sync endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
TrochtaOndrej
2026-03-30 03:08:12 +02:00
parent 17b1171e9b
commit ab0acf60b4
@@ -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.