Recent engine events, newest-window, cursored by `at_ns`.
Overview
TRAP, and why this endpoint does NOT touch Request::DrainEvents
The engine's DrainEvents request is DESTRUCTIVE and the Godot client is
its consumer. An events endpoint built on it would silently steal events
from the running game, and the symptom — a player's event feed
intermittently missing entries whenever a dashboard was open — would be
maddening to trace back to an HTTP route.
This reads WorldSnapshot::recent_events, which sim-project fills from
engine.recent_events(120): a bounded READ of the tail, never a drain. The
plan called for building a separate mirrored ring for this; that turned out
to be unnecessary because the non-destructive window already exists and
at_ns is already a monotonic cursor. Fewer moving parts, same guarantee.
The window is bounded, so a slow poller can miss events. That is reported
via possible_gap rather than hidden — see EventsPage.
Parameters
Return only events strictly newer than this at_ns. Omit for the whole
retained window.
int640 <= valueMaximum events to return. Clamped to 1000.
0 <= valueCode example
curl -X GET "https://example.com/api/v1/events"Response example
{ "items": [ { "at_ns": 0, "kind": "string", "msg_key": "string", "msg_args": [ { "key": "string", "text": "string", "number": 0.1, "is_number": true } ], "severity": "string", "entity_kind": "string", "entity_id": 0 } ], "next_since_ns": 0, "oldest_retained_ns": 0, "possible_gap": true, "count": 0}Response body
200Events newer than `since_ns`GET /api/v1/events body.
Not a plain Page: the gap fields are the point. The engine keeps a
bounded recent-event window, so a consumer polling slower than events
arrive WILL miss some, and a response that quietly returned fewer rows
would look identical to a quiet period. possible_gap says which it was.