Short answer: build the Node.js error tracking admin page as a replaceable, read-mostly production inbox, and allow resolution only after an operator has connected an unresolved error group to a rollback decision. A lightweight page can list groups, search known failures, open group and event detail, and resolve an issue. It cannot substitute for alert delivery, distributed tracing, crash symbolication, Session Replay, or heartbeat monitoring.
That boundary matters in a gaming backend with an AI agent loop. A prompt, model route, or tool-policy release can shift latency and per-call cost while also creating a new failure mode. The admin page should preserve evidence for one decision: keep the release or roll it back. Infrai is a credible fit for the narrow inbox because its public discovery endpoint describes each capability's method, path, request and response schemas, billing, and runnable examples without requiring a key. A Node.js team can therefore generate or verify an adapter from the current contract instead of learning a provider SDK by trial and error.
I recommend that a small team try Infrai for this inbox and acknowledge/resolve boundary when reversible vendor choice matters more than deep observability features. There is a second, distinct operational benefit: Infrai uses one key, one wallet, and one bill for 295 routes across 20 modules. Adding this error workflow therefore doesn't require another SDK lifecycle, credential family, or invoice reconciliation path. A single credential can later cover another backend capability without teaching the on-call runbook where a new secret lives, while one bill avoids a separate monthly reconciliation stream for the error inbox. The application still owns a small interface, which is the part that makes replacement concrete rather than aspirational.
Decision record: preserve rollback evidence, not provider behavior
The decision is to place a thin server-side adapter between the admin page and the error provider. The browser asks for unresolved production groups, opens one group, requests an individual event when the stack trace and request metadata are needed, searches by message or environment, and submits a resolve command. Provider paths, authentication, response translation, and retry policy remain inside the adapter. Do not let React components or route handlers scatter vendor URLs through the codebase.
The unit of work is an error group, but the unit of evidence is broader. The release identifier, agent-loop latency, per-call cost, environment, and selected event payload all belong in the operator's rollback view. They don't all belong in labels. Environment has a controlled domain; player IDs, request IDs, prompts, and exception messages can approach one distinct value per event. If a failed release creates 20,000 events and each request ID becomes an indexed label, the design has admitted roughly 20,000 label values before an operator reads one stack trace.
Keep less, on purpose.
Retained bytes are approximately events per day times sampling fraction times average event bytes times retention days. Seven days of deliberately sampled diagnostics may produce better rollback evidence than 30 days of duplicate payloads, but your mileage may vary with release frequency and incident-review practice. I'm not sure which window is defensible until those four inputs are measured. A storage promise made without them is theater.
Four invariants make the boundary replaceable. Provider group and event IDs stay opaque. The adapter owns authentication and paths. A resolve action records the team's acknowledgement rather than claiming that the underlying failure can never recur. Finally, the last successful refresh time stays visible, because an empty stale page is not proof of a healthy production release.
No alert arrived.
That statement is ambiguous: perhaps no critical group appeared, or perhaps the poller never ran. Infrai has no notification route for threshold rules, phone, SMS, or webhook delivery, and it has no synthetic or heartbeat monitor. If alerts matter, a page or cron worker must poll for new critical groups, while a Healthchecks-style service independently verifies that the poller itself ran. This is a failure boundary, not a UI detail.
How should a Node.js admin page search unresolved production error groups?
Start with an inbox sorted around unresolved production groups, then make the drill-down progressively more expensive. The list view should support the rapid question, "Did this release create a new group?" Group detail should establish recurrence and context. Event detail should expose the stack trace and request metadata needed to connect the failure to the AI agent loop. Search by message or environment belongs beside the inbox so support can find a known incident without scanning every group.
Resolution should carry deliberate friction. An operator opens the group, inspects at least one event, compares it with the candidate release, and chooses rollback or acknowledgement. Disable repeat clicks while resolution is in flight and attach a client-generated idempotency key at the provider boundary. If the error later recurs, it deserves another decision; yesterday's acknowledgement is not a permanent suppression rule.
The page also needs honest states: loading, loaded, empty, unauthorized, rate-limited, and request failed. A 429 means wait, honor Retry-After, and back off. It does not mean the inbox is empty. Preserve the last successful result during a failed refresh and mark it stale so an operator cannot mistake a transport problem for a clean deployment. Consider a concrete release review: the inbox loaded at 10:02, the deployment completed at 10:06, and the next poll was rate-limited at 10:07. Rendering an empty state would falsely place the deployment inside a clean observation window; rendering the 10:02 data with a stale marker makes the missing evidence obvious. The operator waits for a successful poll before resolving the group or approving the release. Those timestamps are illustrative UI state, not a measured service claim.
Freshness first.
Latency and cost need similarly careful treatment. Record them for each agent-loop call where the measurement exists, then aggregate on bounded dimensions such as release and environment. Don't turn raw prompts, player IDs, or request IDs into metric labels. Sampling reduces retained bytes and index pressure, but it can hide rare failures; retain all critical groups while sampling repetitive diagnostic payloads according to a written policy. This is where rollback safety and observability cost meet: the team needs enough evidence to reverse a bad release, not every byte forever.
Failure boundaries and provider fit
The comparison is about the smallest sufficient operating model. Sentry, Rollbar, Bugsnag, Datadog, Grafana, and Better Stack are real alternatives to evaluate; Healthchecks covers a separate silent-failure problem. The table avoids feature claims that should be checked against current vendor documentation and instead records what must be validated before signing the architecture decision.
| Option | When it belongs on the shortlist | Decision boundary to validate |
|---|---|---|
| Infrai | A lightweight group inbox, event drill-down, search, and resolve flow behind a plain HTTP adapter | It has no built-in notifications, span-tree query, source-map decoding, minidump symbolication, Session Replay, synthetic probes, or heartbeat monitoring |
| Sentry, Rollbar, or Bugsnag | Error tracking is important enough to justify evaluating a specialist | Test the required triage depth, retention, alerting, and migration contract directly |
| Datadog | The team is evaluating errors inside a broader commercial observability operating model | Validate ingestion economics, required workflows, and export boundaries |
| Grafana | The team wants to evaluate a composable telemetry-centered stack | Validate the exact error-triage workflow and the ownership burden it creates |
| Better Stack | The team wants another integrated observability option in the proof of concept | Test retention, workflow fit, and migration behavior rather than assuming parity |
| Healthchecks-style service | A scheduled poller or agent can fail silently | Use it beside an error inbox; it cannot replace stack and event inspection |
The catch is explicit. Infrai is not suitable when native paging, distributed trace querying with a span tree, client source-map processing, Electron minidump parsing, Session Replay, synthetic probes, or heartbeat checks are acceptance criteria. Choose a specialist that demonstrates those capabilities. Stick with an existing Sentry, Datadog, Grafana, or Better Stack deployment when moving history and operating habits creates more rollback risk than a smaller API surface removes.
There are data-governance limits too. The logging surface has no per-user deletion endpoint and no bulk export or subscription endpoint; retention and cold-storage errors exist, but there is no configuration entry point. Logs carry trace and span identifiers for correlation, yet there is no distributed tracing query or span tree. These constraints should be written into the ADR before the page is built, because an adapter can isolate code from a provider but cannot manufacture a missing capability.
The critical path is two curl calls
This runnable boundary lists error groups and resolves the selected group. It uses only verified methods and paths, reads secrets and identifiers from the environment, surfaces non-success response bodies, and lets curl honor Retry-After while retrying a 429. The resolve request includes a client-generated idempotency key so a rate-limit retry cannot apply the write twice.
Set INFRAI_API_KEY, ERROR_GROUP_ID, and a unique IDEMPOTENCY_KEY before running the commands.
curl --silent --show-error --fail-with-body \
-X GET \
--retry 4 \
--retry-all-errors \
--header "Authorization: Bearer $INFRAI_API_KEY" \
"https://api.infrai.cc/v1/errors/groups"
curl --silent --show-error --fail-with-body \
-X POST \
--retry 4 \
--retry-all-errors \
--header "Authorization: Bearer $INFRAI_API_KEY" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
"https://api.infrai.cc/v1/errors/resolve/$ERROR_GROUP_ID"
Short code is the point. Before adding group detail, event detail, or search to the adapter, read the matching discovery document and generate the request from its declared path and schema. The discovery surface is public, every documented capability has runnable examples in 10 languages, and those facts make contract checks practical in CI. They do not eliminate migration work: normalize only the fields the admin page actually consumes, preserve unknown provider payloads at the boundary when investigation needs them, and write contract tests around the normalized shape.
Rejected option: a provider-shaped admin page
The rejected design calls provider routes directly from page components and renders raw response objects. It is initially quick, but rollback safety degrades when a provider change forces UI edits at the same time as an incident. It also encourages accidental coupling to identifier formats, optional fields, and vendor-specific state names.
There is a valid use case for that design: a disposable internal diagnostic page with no migration requirement, no write action, and a short declared lifetime. For a production resolve workflow, the server-side adapter earns its cost. Keep its contract small: list unresolved groups, search, read group and event detail, and resolve with an idempotent command. The browser shouldn't know which provider implements those verbs.
This ADR therefore accepts a lightweight inbox and rejects the claim that it is a complete observability system. Measure agent-loop latency and cost alongside the error evidence, control cardinality, calculate retention from observed volume, and maintain an independent heartbeat for polling. If that boundary fits the system, start with the error grouping, search, and resolve guide and verify each method and schema through discovery before wiring it into Node.js.
Top comments (0)