# On-prem LMS API contract (RestDriver)

The portal and runtime talk to storage only through `Store` (`xr/store.js`). Setting **Settings → Storage** (or `localStorage['jda.rest.base']`)
switches the driver from IndexedDB to REST. Optional bearer token: `localStorage['jda.rest.token']`. All bodies are JSON; all data stays on the academy network.

Base: `{base}/api/v1`

| Method | Path | Purpose |
|---|---|---|
| GET | `/{collection}` | list all |
| GET | `/{collection}?{index}={value}` | filter by indexed field |
| GET | `/{collection}/{id}` | one record |
| PUT | `/{collection}/{id}` | create / replace (id = `id`, `settings` uses `key`) |
| POST | `/{collection}/batch` | upsert an array |
| DELETE | `/{collection}/{id}` | delete one |
| DELETE | `/{collection}` | clear (admin only) |

Collections & indexed fields
* `users` — `{ id, role: 'admin'|'instructor'|'student', name:{en,ar}, rank:{en,ar}, serviceId, cohort, color }`
* `scenarios` — scenario JSON (see SCENARIO_SCHEMA.md) + `createdAt, source`
* `sessions` (`code`, `status`) — `{ id, code, scenarioId, instructorId, mode:'live'|'solo', net:'local'|'peer'|'solo', status:'scheduled'|'live'|'done', scheduledAt, startedAt, endedAt, participantIds[] }`
* `attempts` (`sessionId`, `userId`, `scenarioId`) — `{ id, sessionId, userId, scenarioId, role, mode, startedAt, endedAt, breakdown }`
* `events` (`attemptId`) — `{ id, attemptId, t, type, step, target, ... }`
* `certificates` (`userId`) — `{ id, userId, type:'academy'|'ixrdl', scenarioId, issuedBy, issuedAt, serial }`
* `settings` — `{ key, value }`

Recommended production additions (outside this contract): SSO (SAML/LDAP) issuing the bearer token, server-side recomputation of `breakdown`
from `events` with `scoring.js` (same module runs in Node), audit log, and a nightly export.

Sync transport for headsets is separate from storage: run a PeerJS signaling server on the LAN — `npx peer --port 9000 --path /jda` — and set
`localStorage['jda.peer.opts'] = '{"host":"lms.academy.local","port":9000,"path":"/jda","secure":true}'` on each device (see DEPLOY_QUEST3.md).
