1.24.27 — Metrics anonymous read surface hardened
Editorial identity incomplete
2026-09-10
Three fixes on the anonymous /api/metrics/* read surface: a bounded date range (one request could previously materialise ~105M Redis keys), value/get answers 400 instead of an anonymous 500 that filed a level-12 incident per call, and a configured view permission on the public account is now actually enforced. No breaking changes; the unconfigured public default stays open.
Added
METRICS_MAX_RANGE_BUCKETSsetting (default10000): the maximum number of time buckets a single metrics read may span. Checked inside the shared range helper before any key is generated, so every read endpoint (fetch,series,value/get,categories,category_slugs,category_fetch,discover) inherits it.utils.count_buckets()is the public estimator.value/getaccepts singularslugalongsideslugs, matchingfetch.
Fixed
value/getwith none ofslug/slugs/categoryraisedUnboundLocalError; the generic handler turned that into a level-12rest_errorincident with request data and a stack trace, once per request, from an endpoint an anonymous caller can reach. It is now a 400ValueException.value/getresolved category slugs from Redis before runningcheck_view_permissions; the permission check now runs first.POST /api/metrics/permissionswith only one ofview_permissions/write_permissionsoverwrote the other list with[""](an absent key split to a truthy single empty string). Each list is written only when its key is present; an empty value clears it; the response reports stored state.
Security
- A date range wider than the cap is refused with a 400 rather than served. Previously
get_date_rangeclamped nothing when both bounds were supplied, so one unauthenticatedfetchat minute granularity over a century allocated many gigabytes of key strings before the MGET — a single-request memory-exhaustion vector on the openpublicaccount. Set the cap to0only on a host that is not internet-reachable. - A view permission configured on the
publicmetrics account is now enforced on reads.check_view_permissionspreviously ended its chain before consultingget_view_perms("public"), so an operator locking public reads down got{"status": true}and no enforcement. When nothing is configured the account stays open, as before.
Upgrade notes
- No migration. The one behaviour change a client can notice is the range cap: a legitimate read wider than 10,000 buckets (about 7 days at minute granularity, 27 years at daily) now 400s and should narrow its window or raise the setting.