Task · OPN-0014

Unbound search_queries payload churn on 26.7: blocklist value rewritten, new category key

Description

Upstream Unbound/Api/OverviewController.php (26.7 series) now always overwrites the blocklist value in api/unbound/overview/search_queries rows with the display description, and adds a new category key. We model policy/status (the get_policies shape change is already handled in opnsense/unbound_dns.go), but anything keying on raw blocklist values changes silently, and category will surface as canary drift — pre-classify it as an opportunity key (knownExtraTopKeys) rather than letting the daily canary file it as unexplained. Found by upstream API-surface research 2026-08-30.

Acceptance Criteria

Definition of Done

Implementation Plan

Wave 1 execution: trace the unbound search_queries response through decoding and metric emission to determine whether raw blocklist is shipped; verify the 26.7 upstream category addition; add the narrow root-owned canary opportunity exemption only if it matches the response schema; run focused canary/schema checks and the integrated gate. Declarative ledger changes are validated rather than tested unless existing regression coverage has a clear extension point.

Wave 2 L10: complete the stable Unbound response-generation contract together with OPN-0055 using the frozen omit-when-unrecoverable decision and regression coverage.

Implementation Notes

Source inspection proves no Prometheus label carries blocklist, but the Unbound log source ships the API row blocklist value in its JSON body and Loki structured metadata. OPNsense 26.7 rewrites that value to the configured display value, so AC1 is not satisfied by the current pass-through contract. Added the narrow rows[].category knownExtraPaths opportunity exemption and validated both known-extra-path tests, just schemas, and just check. CodeRabbit was skipped because the landed repository change is a declarative JSON compatibility ledger plus tracker records. Follow-up OPN-0055 owns the stable identity contract.

Wave 2 implemented payload-shape detection using exact category key presence rather than lexical value inference; focused tests, schema checks, full indexed just check, and L13 review passed. Landing is blocked solely by two CodeRabbit connection failures with no complete event. Preserved in codex/wip-wave2-coderabbit-blocked.patch; resume by applying it, rerunning the gate, and obtaining a completed CodeRabbit review.

Landed on main in a482f637. AC1 confirmed: no Prometheus label or attribute carries the raw blocklist value. Exact category presence, not a version sniff, distinguishes the 26.7 response generation from the legacy one. The declarative canary exemption from the earlier partial close remains valid. AC1 was coupled to the OPN-0055 implementation, which landed in the same commit.

Final Summary

Parked with AC2 complete: rows[].category is classified as a nested opportunity path and the schema/canary checks plus just check pass. AC1 remains open because the shipped blocklist attribute changes meaning across response generations; resume via OPN-0055 by defining a recoverable stable identity or explicitly omitting the unstable value, with old/new-shape regression coverage.

View the source file on GitHub