The rebase onto main brought public dashboards (#14789), whose data
endpoint builds results through search_service and promql without any
licence check, so an anonymous link kept reading data while search was
blocked. The data read now answers 429 when the search gate is closed,
warm results included, with the generic rate-limit body so the licence id
never reaches an anonymous viewer; the meta org passes as in the search
route middleware. The three public dashboard build call sites are
classified as helpers behind that check.
POST /api/license/refresh and /rotate_key now return the attempt_at the enterprise refresh recorded for this call while it held the refresh lock, and attempt_recorded: false (also on 409) when the call recorded no attempt. The before/after status reads that guessed the attempt from actor and outcome are gone. The card clears a clicked outcome only on a strictly newer attempt than attempt_at, or than the pre-click attempt when attempt_recorded is false; a response with neither field keeps the clicked outcome until the next click or page load.
POST /api/license/refresh and /rotate now return attempt_at, the
refresh.last_attempt_at this click recorded, read from the refresh status
right after the click and kept only when it is newer than the pre-click
attempt and carries this user and this outcome. Clicks refused before an
attempt was recorded return none. The card keeps a clicked outcome until a
strictly newer attempt arrives, falling back to the pre-click attempt when
attempt_at is absent; the Date-header and browser-clock guesses are gone.
A click now clears its inline outcome only when GET /api/license reports an
attempt strictly newer than the click: newer than the server time the
response was sent (Date header, else the browser clock) for an answered
click, or newer than the pre-click attempt for a refused one. Older, equal
and in-flight attempts no longer clear it or get mistaken for the click's own.
The paused-search status, the search banner and the restored-by-waiver line
read "1 days" / "1 allowed days"; they now use vue-i18n plural forms.
A scheduled search_unblock waiver now shows "<start> → <end>" in the window
column; active and ended rows keep "until <end>".
The search call-site guard missed promql_service::search::search(, aliased
search::search( and bare imported search( calls, and keyed sites by file so a
second classification in one file collapsed. Sites are now (file, fn) pairs,
duplicates fail, and the newly visible PromQL rule, anomaly query, query
recommendation and service-graph reads are classified as background work.
GET /{org_id}/fleet/pipelines runs PromQL searches over the org metrics but its route sits outside the gated search group, so it kept searching while the licence blocked search. The handler now answers the same 429 as the other in-handler search callers.
The fleet audit read searches the meta org audit stream, which the gate already exempts, so it stays open like the other history reads. Both call sites are classified in SEARCH_CALLERS and /fleet/audit joins OPERATIONAL_READS.