# status-api Specification ## Purpose The server status endpoint: a cheap document with the writer schema status, the legacy migration and the writer health that every page of the dashboard polls at a fixed cadence, whatever the page and its auto-refresh. ## Requirements ### Requirement: Status endpoint Edition: CE. `GET /api/v1/status` SHALL require a bearer token, take no parameters and answer `200` with `generatedAt` (the MongoDB clock), `schema` and `migration` with the same shapes and sources as `GET /api/v1/info`, and `writer { lastSeenAt?, nodes, aliveNodes, batchIntervalMs? }`. A node's fresh window is `max(30 s, 1.5 × I + 5 s)`, I being its `batchIntervalMs` (10 s when absent). The response carries `Cache-Control: private, no-store`. #### Scenario: Running migration - **GIVEN** two nodes last seen 4 s and 12 s ago, both with `batchIntervalMs: 10000`, writer schema 1 equal to the dashboard's and a migration with `processed` 400000 of `total` 1200000 - **WHEN** the client requests `GET /api/v1/status` with a valid token - **THEN** the response is `200`, `writer.lastSeenAt` is the 4 s old timestamp, `writer.nodes` is `2`, `writer.aliveNodes` is `2`, `writer.batchIntervalMs` is `10000`, `schema.status` is `"ok"` and `migration` is `{ state: "running", progress: { processed: 400000, total: 1200000, startedAt } }` #### Scenario: Writer never seen - **GIVEN** empty `nodes` and `schema_meta` collections - **WHEN** the client requests the status - **THEN** `writer` is `{ nodes: 0, aliveNodes: 0 }`, `schema.status` is `"uninitialized"` and `migration.state` is `"none"` #### Scenario: A node stopped reporting - **GIVEN** `node-a` seen 4 s ago and `node-b` seen 50 s ago - **WHEN** the client requests the status - **THEN** `writer.nodes` is `2` and `writer.aliveNodes` is `1` #### Scenario: Each node judged by its own interval - **GIVEN** `node-a` with `batchIntervalMs: 60000` seen 80 s ago and `node-b` with `batchIntervalMs: 10000` seen 40 s ago - **WHEN** the client requests the status - **THEN** `writer.aliveNodes` is `1` (`node-a`, whose fresh window is 95 s) and `writer.batchIntervalMs` is `60000` #### Scenario: Largest interval of the last hour - **GIVEN** `node-a` with `batchIntervalMs: 10000` seen 5 s ago, `node-b` with `30000` seen 20 minutes ago and `node-c` with `60000` seen 2 hours ago - **WHEN** the client requests the status - **THEN** `writer.batchIntervalMs` is `30000` #### Scenario: Nodes without a reported interval - **GIVEN** one node seen 25 s ago whose document has no `batchIntervalMs` - **WHEN** the client requests the status - **THEN** `writer.aliveNodes` is `1`, judged with the 30 s window of a 10 s interval, and `writer` has no `batchIntervalMs` #### Scenario: No token - **WHEN** the client requests `GET /api/v1/status` without `Authorization` - **THEN** the response is `401` with code `UNAUTHORIZED` #### Scenario: Writer fields - **WHEN** a client with a bearer token requests `GET /api/v1/status` - **THEN** `writer.lastSeenAt` is the newest `nodes.lastSeenAt`, `writer.nodes` the node count, `writer.aliveNodes` the nodes whose `lastSeenAt` is within their own fresh window of `generatedAt`, and `writer.batchIntervalMs` the largest `nodes.batchIntervalMs` among the nodes whose `lastSeenAt` is within one hour of `generatedAt`, absent when none of them reports one ### Requirement: Cheap status reads Edition: CE. Each status request SHALL read only the `schema_meta` and `nodes` collections, both whole and bounded by the number of server nodes, plus the MongoDB clock; it SHALL compute no storage, database or retention facts, which stay in `GET /api/v1/info`. #### Scenario: Commands of one request - **GIVEN** a database generated by the data generator at scenario M - **WHEN** the client requests the status with MongoDB command monitoring on - **THEN** the only `find` commands target `schema_meta` and `nodes`, and no `aggregate`, `collStats` or `buildInfo` command is sent ### Requirement: Freshness windows Edition: CE. Freshness windows SHALL derive from a batch interval I (`writer.batchIntervalMs` of the status, 10 s when absent) through the typings helper `freshnessWindows`: fresh `max(30 s, 1.5 × I + 5 s)`, silent `max(60 s, 2 × I + 15 s)` and stale `max(90 s, 60 s + I + 15 s)`. Clients SHALL judge the writer fresh and silent by comparing `generatedAt − writer.lastSeenAt` with these windows, never with fixed thresholds. #### Scenario: Default interval - **GIVEN** a status whose `writer.batchIntervalMs` is `10000`, or absent - **WHEN** a client derives the windows - **THEN** fresh is 30 000 ms, silent 60 000 ms and stale 90 000 ms #### Scenario: Slow writer - **GIVEN** a status whose `writer.batchIntervalMs` is `60000` - **WHEN** a client derives the windows - **THEN** fresh is 95 000 ms, silent 135 000 ms and stale 135 000 ms #### Scenario: Last batch 70 s old at a 60 s interval - **GIVEN** a status with `writer.batchIntervalMs: 60000` and `writer.lastSeenAt` 70 s before `generatedAt` - **WHEN** a client judges the writer - **THEN** the writer is fresh and not silent