{"ns":"observe","title":"Beacon Observe","version":"1.0","summary":"An observability service for engineering teams: metric time series for services and environments, stat panels, deploy annotations, a searchable log explorer with level facets, and alert rules that evaluate live, with silences. This release exposes the service as an HTTP API over a frozen synthetic dataset whose clock stands at 30 Sep 2026 12:00 UTC.","problem":"During an incident, on-call engineers need to see quickly what changed, when and where, and trust that charts, stats, logs and alerts all tell the same story on the same time axis.","personas":[{"name":"On-call engineer","need":"See the error spike, the deploy that preceded it and the logs that explain it, then confirm the alert and its state."},{"name":"Service owner","need":"Define alert rules that fire only on sustained breaches and silence them during maintenance."},{"name":"Dashboard builder","need":"Pull series with a sensible resolution for any time range."}],"goals":["Time series are aligned to the requested window and use a resolution that keeps each series to a few hundred points.","Alerts fire only when the whole for-window breaches the threshold, and silences hide only active alerts.","Log facets always describe the whole result set for the other filters, not just the visible level.","Each caller session keeps its own rules, silences and dashboards."],"features":[{"id":"META","title":"Environment metadata","summary":"Published facts the client needs to build its controls.","reqs":[{"id":"FR-META-01","weight":"Must","text":"The meta endpoint publishes the clock, services, environments, metrics, ranges and the incident window.","accept":["The frozen now is 1790769600000 (30 Sep 2026 12:00:00 UTC)","Services are api-gateway, checkout, search and auth; environments production and staging; metrics latency_p95, rps, error_rate and cpu","There are 8 relative ranges from 15m to 7d","The incident is on service \"checkout\" from 09:40Z to 10:05Z on that day"]}]},{"id":"SER","title":"Time series","summary":"Metric values over a time window, per service and environment.","reqs":[{"id":"FR-SER-01","weight":"Should","text":"The resolution step adapts to the range so a series has at most about 720 points.","accept":["15m: step 15000 ms and 61 points","1h: step 15000 ms and 241 points; error_rate carries unit \"%\"; the last checkout value is 0.61","6h: step 60000 ms; without a service filter four series come back, one per service, 361 points each","7d: step 900000 ms and 673 points","Steps are 15 s up to 3 h, then 1 m, 5 m, 15 m and 1 h as the range grows"]},{"id":"FR-SER-02","weight":"Must","text":"Series timestamps are aligned to the window: the first point is at from and the last is at to, never beyond now.","accept":["For range 1h the first timestamp is now minus 3,600,000 ms and the last timestamp is now (1790769600000)","Every timestamp is a whole number of steps after the first, so a spike plots at its real time"]},{"id":"FR-SER-03","weight":"Should","text":"The checkout incident shows in the error rate only inside its window and only in production.","accept":["Before 09:40Z values are about 0.5-0.7; from 09:40Z to 10:05Z every value is between 7.5 and 11.5 (peak 11.42); after 10:05Z they return to about 0.5-0.7","In staging there is no spike (maximum 0.62)"]},{"id":"FR-SER-04","weight":"Should","text":"An unsupported metric, an unknown service or an inverted window is rejected with 422.","accept":["Metric mem: 422 \"metric must be one of latency_p95, rps, error_rate, cpu\"","Service billing: 422 Unknown service \"billing\"","from after to: 422 \"from must be before to\""]},{"id":"FR-SER-05","weight":"Should","text":"The window is clamped: the end never exceeds now and the start never goes back more than 7 days.","accept":["An end in the future is returned as now (1790769600000)","A start earlier than 7 days before now is returned as now minus 7 days"]}]},{"id":"STA","title":"Stat panels","summary":"Current, average and maximum of a metric per service over a range.","reqs":[{"id":"FR-STA-01","weight":"Should","text":"Stats report current, average and maximum per service for the chosen range.","accept":["error_rate over 6h (current / avg / max): checkout 0.61 / 1.24 / 11.42; api-gateway 0.37 / 0.4 / 0.46; search 0.32 / 0.3 / 0.34; auth 0.19 / 0.2 / 0.23","Over 1h checkout is 0.61 / 0.59 / 0.68 because the incident is outside the window; other services are unchanged"]}]},{"id":"ANN","title":"Deploy annotations","summary":"Deploy markers to overlay on charts.","reqs":[{"id":"FR-ANN-01","weight":"Could","text":"Annotations list the deploys within the range, bracketing the incident.","accept":["Over 6h there are two deploys, both of checkout: one by release-bot at 09:38Z and a rollback fix by m.ito at 10:02Z","Over 7d there are four deploys, adding one for search on 29 Sep and one for auth on 28 Sep"]}]},{"id":"LOG","title":"Log explorer","summary":"Search logs by service, time, level and text, with level facets.","reqs":[{"id":"FR-LOG-01","weight":"Should","text":"Logs for a service and window come back newest first and expose the root cause of the incident.","accept":["Checkout logs from 09:40Z to 10:05Z: 68 items, facets DEBUG 0, INFO 0, WARN 0, ERROR 68","The first item is level ERROR, message \"NullPointerException in CartPricer.apply\", traceId \"9cfe2913\""]},{"id":"FR-LOG-02","weight":"Should","text":"A level filter narrows the items but the facets still count every level; text search matches message or trace id; limit caps the items.","accept":["Last hour, level ERROR: 15 items, all ERROR; facets DEBUG 135, INFO 510, WARN 61, ERROR 15","Text \"502\" in the last hour: 2 items; facets INFO 1, ERROR 1","Over 6h with limit 5: exactly 5 items"]}]},{"id":"ALR","title":"Alert rules and silences","summary":"Rules evaluated live against the series, with a for-window and silences.","reqs":[{"id":"FR-ALR-01","weight":"Must","text":"The seeded rules are evaluated at now and report state, current value and when they last fired.","accept":["AR-0001 \"Checkout error budget burn\": OK, current 0.61, last fired 10:05Z (1790762700000)","AR-0002 \"Gateway latency\": OK, current 134.45, never fired","AR-0003 \"Search CPU saturation\": OK, current 61.25, never fired"]},{"id":"FR-ALR-02","weight":"Could","text":"An unsaved rule can be previewed without being stored; the operator is validated.","accept":["Checkout error_rate > 5 for 5 minutes previews as OK with current 0.61 and last fired 10:05Z","api-gateway latency_p95 < 1000 for 5 minutes previews as Firing with current 134.45","An operator other than > or < is rejected with 422 \"service, metric, op (> or <) and numeric threshold are required\"","Listing rules afterwards still shows only AR-0001 to AR-0003"]},{"id":"FR-ALR-03","weight":"Must","text":"A rule can be created and silenced; while a silence extends beyond now the rule shows as Silenced.","accept":["Creating \"Auth CPU low\" (auth cpu < 100 for 5 minutes) answers 201 with a new id AR-NNNN and the rule is Firing","Creating a silence with until 2026-09-30T18:00:00Z answers 201 with an id SIL-NNNN","The rule then shows Silenced with silencedUntil \"2026-09-30T18:00:00Z\""]},{"id":"FR-ALR-04","weight":"Should","text":"A silence that ended before now has no effect.","accept":["A silence until 2026-09-30T11:00:00Z is accepted with 201","The rule still shows Firing and silencedUntil is null"]},{"id":"FR-ALR-05","weight":"Must","text":"A rule fires only when every sample over its for-window breaches the threshold.","accept":["The rule \"Auth latency creeping\" (auth latency_p95 > 100 for 5 minutes) is OK with current 112.31, because the last two minutes breach 100 but the minutes 11:55 to 11:57 do not","The rule becomes Firing only once all samples of the 5-minute window breach"]},{"id":"FR-ALR-06","weight":"Should","text":"Rules and silences reject missing required fields with 422.","accept":["A rule without service, metric, op and threshold: 422 naming the missing fields","A silence without ruleId and until: 422 naming the missing fields"]}]},{"id":"SES","title":"Session isolation","summary":"Callers can keep private rules, silences and dashboards.","reqs":[{"id":"FR-SES-01","weight":"Should","text":"Rules and silences created under one session identifier are not visible to another.","accept":["A rule created under session A does not appear under session B, which sees only AR-0001 to AR-0003"]}]},{"id":"DSH","title":"Dashboards","summary":"Saved collections of panels.","reqs":[{"id":"FR-DSH-01","weight":"Could","text":"A seeded overview dashboard has one panel per metric.","accept":["One dashboard, DB-0001 \"Service overview\", with panels latency_p95, rps, error_rate and cpu"]}]}],"nfr":[{"id":"NFR-01","weight":"Must","text":"The dataset is deterministic: the same request at the frozen clock always returns the same values, and times are exchanged as epoch milliseconds."}],"glossary":[{"term":"For-window","meaning":"The number of minutes a threshold must be breached continuously before a rule fires."},{"term":"Facet","meaning":"A count of log items per level."},{"term":"Step","meaning":"The spacing between points in a series, in milliseconds."}],"outOfScope":["Dashboard user interface","Real data ingestion; data is synthetic and frozen"]}