Unreal Ops Monitor

Service limitations

Monitor works, and it is one person's deployment on free and starter tier infrastructure. Both of those are true at once. This page is the second half, in full, so that nobody finds it out at the wrong moment.

Last read against the code on 29 August 2026. Written by the person who wrote the software, from the software. No lawyer has reviewed it, and it contains no legal terms that one would have written.

There is no service level guarantee

Not a low one. None.

There is no uptime commitment, no availability target, no credit if it is down, and no contract that would make one enforceable. If ingest is unavailable, the events your app tried to send during that window are gone: the SDK retries within its own transport queue for a short time and then drops them, and nothing at this end buffers, queues or backfills. Monitor is not a system of record and must not be the only place a fact about your application exists.

One person's provider accounts, and what that means

Three services, named, with what each one would do if it went wrong.

VercelHosting

Runs the application, the ingest endpoint and the two scheduled jobs. It is also the only service in this stack that can bill rather than refuse: on the plan with no payment method attached the project is paused at the limit, and on a paid plan a spend cap is a setting somebody has to have remembered. Nothing in this codebase can stop a bill from a host.

TursoDatabase

Holds everything: events, issues, sessions, symbol indexes and every account row. On a starter plan with overages disabled, so past a quota a write fails and nothing is charged. See the next section, because this database is shared.

ResendEmail

Sends alerts. On a free tier that stops sending past its allowance and returns an error, which is recorded and moved on from. Alerting caps itself well before that anyway. If mail delivery is not configured on the deployment, alerting is simply off, which is a supported state rather than an error.

The database is shared with other applications

And the quota that matters is the account's, not Monitor's.

Monitor's database sits in a Turso account that also holds the operator's other databases, most of them client sites. The plan's quota is account wide, so every byte and every row Monitor spends is one those cannot have. The ceilings inside Monitor are therefore a deliberate minority share rather than round numbers: one gigabyte of the account's five for storage, two million of ten million monthly row writes, fifty million of five hundred million monthly row reads, and a quarter of Monitor's storage, 250 MB, for symbol indexes across the whole deployment.

What that buys you is the property the whole product is built on: at a ceiling, the database refuses the write and nothing is charged. What it costs you is that you are sharing a quota with work that has nothing to do with you, and that a bad month elsewhere in that account is a constraint here. The deployment wide symbol ceiling is checked on every accepted upload precisely because past it the refused writes would land on somebody else's database.

Backups

What exists is the provider's, and it is short.

Nothing in this deployment takes a scheduled backup. There is no snapshot job, no second region and no continuous copy. What sits underneath is the database provider's own point in time recovery, and on the plan this runs on that window is one day. A mistake older than a day is not recoverable that way, and an account level problem takes the recovery window down with the database it was meant to protect, which is the case the shared account section above is about.

An operator can also take a dump of the database by hand and restore it somewhere else. Whether one exists for the day you need is a question of whether somebody ran it, not a property of the service, so it is not something to plan around.

Since there is also no export feature, there is currently no way for you to hold your own copy of your events. If that matters to you, keep the mirror pointed at a second backend: see privacy for exactly what mirroring forwards.

The free tier is capped at ten accounts

A number, rather than a hope, and it is derived rather than chosen.

Self serve signup is the first thing here that lets somebody who is not the operator spend the operator's database quota. So there is a ceiling in front of it: ten free organizations on this deployment. It comes from the free tier's share of the symbol storage budget, a fifth of 250 MB, divided by the 5 MB a free organization may hold, which is about one real release of either supported stack.

Past that, signup is refused with a message saying the deployment is full, rather than accepted and then degraded for everybody already here. If you hit it, the answer is to ask rather than to try again tomorrow.

Platforms are listed only where a real project has proved them

A Sentry SDK being able to send a compatible envelope is not support.

FlutterAvailable

Verified by doing it: real envelopes grouped at the live endpoint, symbols uploaded at build time and traces returned readable, release health per release, and an alert email actually delivered.

Next.jsAvailable

Verified end to end on both halves of a real build. A deliberate crash from a deployed, minified bundle came back resolved to the file and line in the repository, and so did one thrown from a route handler on a serverless host. Server source maps are a per project setting and are off by default, because they spend your own pooled symbol allowance.

React Native and ExpoComing Soon

Planned, with no code written. Deliberately not a blocker for anything above.

Native iOS, native Android, Node, Python and everything else are absent from that list on purpose and will stay absent until a real project proves them. An envelope from one of those SDKs may well be accepted and stored; that is not the same as the symbolication, grouping and release handling having been checked against a build somebody shipped.

What Monitor does not do at all

Absent because it is not built, rather than because it is elsewhere.

Session replay and profiling

Performance tracing and structured logs are stored now, and they spend the same pooled allowance your errors do: a transaction costs one event, ten log lines cost one event, and the two together are capped at two fifths of a month so a month of tracing cannot become a month with no crash reports.

Replay and profiling are not stored, and that is a decision with arithmetic behind it rather than a gap. One ten minute replay costs roughly what three hundred and ninety stored events cost, and about fifteen hundred sessions would fill this deployment's entire storage slice. The argument that actually decided it is not the money: a stack trace describes a program, and a replay records a person's screen, and that person is your user rather than ours. If you need replay, point Monitor upstream at Sentry and keep it there; the mirror forwards the original bytes.

An API you can read your data through

The dashboard reads an internal control plane API that is not documented, not versioned for outside callers, and free to change without notice. There is no export and no download.

Paging and on call rotas

Alerts go to email addresses you list, to a Slack incoming webhook, and to signed outgoing webhooks that carry an HMAC and a timestamp so a receiver can prove the message came from here. What does not exist is anything that will wake a particular person up at three in the morning: no rota, no escalation, no acknowledgement, and no second attempt at a different human when the first one sleeps through it.

A status page for Monitor itself

Monitor can host one for you, with components, incidents, maintenance windows and subscribers. It does not yet host one for itself, which is the harder problem: a status page served by the thing it reports on cannot be trusted to say that thing is down. How you hear from us is on support and incidents, and it is still less than a status page.

Anything you can buy

Payment is not configured on this deployment. Every account is on the free plan. See terms and billing.

Ceilings refuse, and a refusal loses events

The good half of this is well advertised. The other half belongs here.

Two ceilings sit in front of ingest and are checked before an envelope is parsed or stored: the organization's pooled monthly event allowance, and a per minute brake on each project. Over either one, ingest answers 429 with Retry-After and X-Sentry-Rate-Limits, the SDK stops sending, and the drop is counted against a reason and shown on the usage screen. Nothing is billed, because nothing underneath can bill.

The honest consequence: those events are lost. They are counted, so you can see exactly how many and why, and they are not stored later. A burst breach also suppresses that project for thirty seconds and a pool breach suppresses every project in the organization, because the pool is empty for all of them. A crash loop in one app can therefore cost you the visibility on a different app in the same organization, and the pool is shared deliberately because that is what the pricing sells.

There is also a breaker that can refuse one project automatically when it can prove that project is at fault, and switches an operator can flip without deploying to stop ingest for the whole deployment, for one organization, or to stop the background jobs. Events refused while any of those is armed are counted as a deliberate loss and are not recoverable.

Grouping is a heuristic and it is wrong sometimes

Worth knowing before you rely on an issue count.

Identical faults collapse into one issue. On a minified JavaScript build the function names are excluded from the grouping input, because a minifier renames the same function differently on every build and including the name would make every deployment a fresh copy of every open issue. The stated cost, written where the rule lives: two different bugs in the same chunk, with the same exception type and the same stack shape, can group together. The dashboard shows the grouping inputs so a surprising group can be explained rather than argued with, and an explicit fingerprint from your SDK overrides it.

Symbolication can also be incomplete. A frame that could not be resolved says so and names the artifact it wanted, rather than showing an address and letting you assume that was the best available. Inlined frames are not recovered, and because the uploaded artifact is discarded after parsing, a future parser improvement will not reach builds that have already shipped without CI uploading again.

One person, no rota

Which is the limitation underneath all of the others.

This service is built, deployed and operated by one person, who is asleep for part of every day and occasionally on a plane. There is no second pair of hands, no handover and no escalation path. What that means in practice for a problem you report is on support and incidents.