This release requires four PostgreSQL migrations (poetry run flask --app website.app db upgrade).
One of them is not optional for instances serving the pub/sub stream: it grants the new
stream:subscribe permission to every role holding admin access, which seed-rbac alone
would not do. The Kvrocks CISA and CNW known-exploited feeders are retired: drop their
[feeder:cisa_known_exploited] and [feeder:cnw_known_exploited] sections from
config/modules.cfg and run tools/purge_kvrocks_kev_feeders.py to delete their keys.
Instances publishing as a GNA should run poetry run backfill_gcve_vulnid once to complete
their stored records with the amended GCVE-BCP-05 identifier placement (see docs/update.md),
and one index_vulnerabilities --source <local instance> pass to backfill the published index.
Instances importing FSTEC should run tools/fix_fstec_alias_links.py --yes to repair the
alias links written before the comma fix. pyvulnerabilitylookup >= 4.5.0 is now required. The
VARIoT feeder is disabled in the sample configuration, the service being unreachable.
What's New
- new: [vlai] Retrieval with the ATT&CK bi-encoder. Two similarity searches over the
CIRCL/vulnerability-attack-technique-biencoder
space served by ML-Gateway, which owns the vectors and the search (no ML dependency is added here):
GET /api/vlai/attack-techniques/<technique_id>/vulnerabilitiesranks the indexed vulnerabilities for
one MITRE ATT&CK technique, andGET /api/vlai/related/<vulnerability_id>lists the vulnerabilities
nearest to one record, searching on its stored vector or, when it is not indexed yet, on its
description (the response says which). Both are labelled as similarity searches, not classifications:
the vulnerability page gains a "Related by attack behaviour" block under the ATT&CK suggestions, and a
new/attack/technique/<technique_id>page, linked from every suggestion card, lists the
vulnerabilities for a technique with an "outside the trained vocabulary" badge when the model was not
trained on it; a technique index at/attack/technique/(navbar entry ATT&CK, backed by
GET /api/vlai/attack-techniques/catalog, which needs an ML-Gateway that serves
GET /retrieve/attack-biencoder/techniques) is the entry point. A newindex_attack_embeddingsconsumer of thevulnerabilitychannel keeps the
gateway index in step with the feeders (batched, retried with backoff, bounded, never blocking a
feeder), started bypoetry run startwhenATTACK_EMBEDDING_INDEXERis true in
config/website.py; its batch size adapts to the gateway's pace (halved on a read timeout, grown back
after consecutive full batches) so a CPU-bound gateway is never re-sent the same oversized batch forever.
The existing corpus is loaded on the gateway host from the dumps. The classification head remains the
default for CVE -> technique suggestions, and the ATT&CK tab links to the CVE-ATTACK working draft paper.
5d7df2f7, 48163180, 6e0864e2 - new: [metrics] Prometheus endpoint. An opt-in
/metricsendpoint (WEB_MODULES["metrics"], off by
default, with an optionalMETRICS_TOKENbearer check and a 30/min rate limit) exposes what nothing
else could alert on: Kvrocks memory, disk, RocksDB estimates and pending compactions; whether the
derived state indexes still account for the corpus; EUVD identifier integrity from a stored
euvd --integrity-scansnapshot that carries its own age; per-source ingestion freshness (last
import, record count, whether a feeder is enabled, and the staleness threshold itself, so the alert
rule lives in one place:METRICS_FRESHNESS_THRESHOLDS/METRICS_FRESHNESS_DEFAULT_SECONDS); and a
request duration histogram and counter labelled by endpoint name and status class, kept in the cache
Redis so a scrape reaching one of many gunicorn workers reads the whole traffic. Collectors are isolated
so one failing cannot silence the others, the feeder-enabled gauge bridgesmodules.cfgsection names
to the source names feeders actually store under (including the per-GNA fan-out ofgcve_vl), and the
scrape is servedno-storeso an edge cache cannot freeze it. Documented indocs/metrics.md.
Contributed by @archakisn in #615 and #630.
2dbf7022, af0c8ed9, a823b659, 754e9b87, 6066ab92 - new: [kev] Withdrawn KEV assertions (GCVE BCP-07 2.3). A producer can take back its own assertion:
status_reason: withdrawnwithexploited: false, kept as a tombstone rather than deleted so mirrors
discover the withdrawal. Everyexploited: falserecord is left out of the current KEV set: catalog
pages, counts, the activity grid, coverage statistics, search badges and the API listing filter on it,
the list endpoint'sexploitedfilter becomestrue(default) |false|all, and the remote sync
asks for all so an upstream withdrawal reaches mirrors. The catalog page gets a "Show withdrawn"
toggle; the entry page and the vulnerability header render a withdrawn entry as a muted tombstone
pill with the withdrawal date instead of a red "listed as exploited" badge. Theexploitedsighting an
entry carries is created only for asserted entries and removed on withdrawal. Administrators can
withdraw an entry of any catalog from its page (the edit form stays confined to the local catalog,
which now has an edit button on the entry page). The shipped BCP-07 schema is refreshed and enforces
the invariant on POST and PUT. Requires a DB migration, which adds the enum member.
6aafe800, e81188c8, cdeebb74, b734ecd4 - new: [web] KEV catalogs page: evidence, profile and trends. Three new cards on
/kev-catalogsread
what the listings rest on. Evidence behind the listings: per catalog, the mix of BCP-07 evidence
types (first-hand observation or reports) with the mean confidence and the share of entries without
evidence; per vulnerability, the strongest signal any catalog attaches and whether observation and
reports corroborate each other, which no single catalog can tell. Profile of the listed
vulnerabilities: the age of a CVE when each catalog picks it up, how many entries preceded
publication, the median days from publication to first listing by publication year, and the EPSS bands
and critical CVSS share of each catalog's entries today. Weekly trends: the last 26 weeks as five
small multiples (new listings per catalog, pre-publication listings, median lead time, evidence types,
which catalog listed first), with configurable event markers drawn as a dashed line on every panel:
the sampleKEV_EVENT_MARKERScarries the CRA Article 14 reporting obligation applying from
2026-09-11, the question this card is built to answer. The corroboration buckets and the
pre-publication and low-EPSS sets filter the coverage table through newevidenceandfocus
parameters. Everything is computed from one Kvrocks pass and cached for an hour.
c6090770, 5afce95e, a9b4959e - new: [gcve] Amended GCVE-BCP-05 identifier placement, AI provenance extensions and relationships.
GCVE-BCP-05, amended in September 2026, puts a record's identifier in thevulnIdof its single
recordType: advisoryentry ofcontainers.cna.x_gcveinstead of the non-standard
cveMetadata.vulnId. This is the compatible half of the migration: every reader (CNA API, GNA
service, reindexers, full-text indexer,gcve_vlfeeder, Vulnogram) accepts both placements, writers
emit the new one and keep the legacy field, the GNA service refuses a record naming two different
identifiers, and the dashboard's GNA cards count the records a remote GNA still publishes in the
legacy shape so the deprecation can be timed on data.poetry run backfill_gcve_vulnidcompletes the
stored records without bumping dates, rescoring indexes or notifying anyone. The record view renders
the BCP-05 extensions GNAs now push in a folded "GCVE extensions" panel: BCP-05-X-01 AI annotations
(level, review status, models table) and BCP-05-X-02 patch2vuln provenance (generator, patches,
rationales, model comparison), any other extension key falling back to JSON. The Relationships row
groups relationships under the x_gcve entry that states them, with the type as a badge carrying the
BCP-05 verb definition, and GCVE records assigned by this instance's own GNA get a "This instance"
pill. Thegcve_vlfeeder now tops the BCP-03 static feed up from the publication API on every run,
newest first until a whole page is already indexed, so a publication reaches peers without waiting for
the next dump (#631).
c9b62a16, 0a76be62, ea33edb1, eb371218, c1f6c6dc, 5b472678 - new: [gcve] Correlations through x_gcve relationships. The relationships a GCVE record states in
its x_gcve entries now feed the{id}:linksets behind the "Related vulnerabilities" tab, both ways
and for every relationship type, the source being the namedsrcIdor the record itself. The
gcve_vlfeeder, the GNA service and the reindexer write the pairs and drop the ones a new edition no
longer states; stored records pick the links up on--reimportor a reindex (#638).
69509096 - new: [gcve_vl] GCVE BCP-03 static feeds. The feeder reads a GNA's complete published history from
its BCP-03 static feed, falling back to the pull API when the feed cannot be read, and the import loop
was adapted to a lazily read full history: per-organisation error guards andlast_updatesstamps,
text/htmlbodies rejected and the first record validated eagerly so a catch-all page is not
imported as an empty feed, the producer's dump envelope keys stripped, records already indexed with
the same modification timestamp skipped, conditional fetches on ETag / Last-Modified, and a stop
request honoured mid-feed. A GNA that publishes only a dump directory (gcve_dumpin the registry,
as VULNARCHIVE does) is now pulled from it instead of being silently skipped. Contributed by
@adulau in #619, with #632.
ca8191d5, 364e3872, 0b628ebe, a9e213e2, 4da28e6b, 6290e1e9, 61db2209 - new: [dashboard] GNAs tab. A fifth dashboard tab covers the GCVE side the way the CNA Roots tab
covers the CVE Program: a card per feed (this instance's own GNA first, then the pulled ones) with the
five latest publications, their vendor/product pairs and feed links, and a filterable table of every
GNA in the signature-verified registry copy with its services and publication counts (#620).
d0ff9a57 - new: [website] Site notice. An administrator can announce maintenance or an incident from
/admin/banner; the notice (bold, italic and link Markdown allowed) is rendered on every page and
served byGET /api/banner, withPUT /api/bannerfor automation and/api/euvd/bannerkept as an
alias for the EUVD frontend. It is stored in a newapp_settingstable of administrator-set JSON
documents, projected through one cache entry (APP_SETTINGS_CACHE_TIMEOUT) that the writes refresh,
so a notice is visible on the next request; a failed read costs the notice and nothing else, and is not
cached as an answer. The same store serves the EUVD curated assigner list (GET/PUT /api/euvd/assigners/names), an editorial subset rather than the derived set. Requires a DB migration.
Contributed by @archakisn in #605.
3897a629, 0a790e98, b7f0fa18, faf68e5a, e15be74d, f620d671, f4663f83 - new: [euvd] Analytics figures. The EUVD statistics surfaces read prepared figures instead of
recomputing them per visitor: an EUVD pass ofindex_vulnerabilitieswrites the per-source counters
and leaderboards under theeuvdsource name (answered by the existing/api/stats/*?source=euvd)
plus a CVSS distribution by band and version derived from each linked CVE record, and
euvd --stats-scanstores an average and median time to exploit with its denominator, refusing to
overwrite a good snapshot when nothing can be dated and naming which of the two causes it hit. The
"first listed as exploited" date rule is unified on the KEV entry model aslisted_at, so the KEV
timeline and the EUVD exploited dates agree. Contributed by @archakisn in #626.
faa2f503, 9eed2e08, fae01a48, 36ccf022 - new: [dump] BCP-07 KEV export. The known-exploited export in
dumps/is the GCVE BCP-07
serializationGET /api/kevreturns, one record per assertion with its origin, evidence and status,
every row included (a negative assertion is how BCP-07 expresses a retraction), ordered so two runs
over unchanged data are byte-identical, with evidence fetched once rather than per entry. Every dump
is now written to a temporary file and renamed over the destination, so a reader never sees a
half-written file. Contributed by @archakisn in #604.
7a312ec6, 683e0da0, 3497506d - new: [feeders] Three OSV sources. The Homebrew advisory database
(BREW-<formula>-<upstream id>records for homebrew-core formulae, CC0), the
openEuler security advisories (OESA records served
from a plain HTTP directory with anall.jsonmanifest, fetched conditionally and only for the records
whose timestamp changed) and the BellSoft OSV database
(BELL-CVE-*records for Alpaquita Linux, MIT). Their upstream ids are cross-linked by the generic OSV
parser, and the release suffix is stripped from the ecosystem for the vendor keys (#645, #646, #652).
6923d683, bcae490b, 44da60c5 - new: [vuln] Withdrawn advisories. A source that publishes an advisory can retract it (GitHub
withdraws a few percent of its advisories; the OSV feeds carry a top-levelwithdrawntimestamp),
and the field was stored and shown nowhere. One badge macro now marks a withdrawn advisory wherever
the record is drawn: its own page, the related-advisory lists, the/recentrows and the feeds, where
an entry titles itself "Withdrawn: " since a withdrawal bumpsmodifiedand would otherwise
arrive at the top as news. The marker matches the KEV tombstones. Contributed by
@archakisn in #636.
0bbf0c31, 7e84186b - new: [organization] What an organization published and assigned. An organization page carries the
three relations it can have to a vulnerability as independently paged tabs: affecting the CPE names of
its products, published as a GNA (fromindex:gna-n) and assigned as a CNA (from a new,
administrator-setcna_short_namecolumn, never derived from the name). Each list gets its Atom/RSS
feed at/organization/<uuid>/recent.<format>?tab=..., and a product page advertises its own
vulnerabilities as a feed at/product/<uuid>/recent.<format>. The organization, organizations and
product views share one grammar and only render the facts a record carries, so a GNA-only
organization is a compact card rather than an empty product grid. Requires a DB migration.
15c6d32c, 5ceb28b4, fc3c3d7d - new: [search] An empty search form lists the selected sources' entries newest first instead of
returning the bare form, reading the per-source date index the results are sorted by, so entries
without a CVSS score are not dropped.
dcdae5dd - new: [web] EUVD records minted by the allocator are rendered as what they are, an alias whose
description, severity and references belong to the linked CVE, instead of an "Other source" raw JSON
dump (#644).
bb144b84 - new: [doc] Generated OpenAPI snapshot.
flask --app website.app dump_openapirenders the API
reference straight from the API object: sorted so the diff only moves when the API does, freed of the
producing instance's UUID, and refusing to write a partial reference when optional modules are off. A
newopenapi.ymlworkflow checks the snapshot on every pull request, and a pre-commit hook regenerates
it. Array body arguments keep theiritems, enum, default and help text in the specification, which
also unblocks the Sphinx build (#643).
b4d574a8, 8162aba1, 1db2928d - new: [website] The landing page search placeholder is replayed letter by letter, then the field is
focused; skipped under reduced motion or when the field is already in use.
3fb449f0
Changes
- chg: [website] Dashboard tabs reorganised. The Activity tab is gone: its Comments, Bundles and
Sightings lists duplicated the Community pages and its KEV entries the KEV Catalogs page. What was
only visible there gets a proper place instead. A Ghost CVEs tab lists the identifiers sighted in
the wild but absent from the CVE registries (the former switch of the Vulnerabilities list and the
"Unpublished advisories" option of the Trending tab), one row per identifier with its recent
sightings, the sites that sighted it and its last sighting. An Other sources tab shows the latest
records of every feed outside the CVE and GCVE programs (GitHub, PySec, OSV, national databases, CSAF
providers...) as per-source cards grouped like the recent vulnerabilities page, the CVE Program mirrors
being covered by the CNA Roots tab and the GNA feeds by the GNAs tab, with a toolbar to narrow the
cards by group or by name. Dashboard tabs can be linked to with a URL hash (/#tab-ghost,
/#tab-other-sources, ...).
850f7a4e, b10a4450 - chg: [feeders] Kvrocks CISA and CNW KEV feeders retired.
cisa_known_exploitedand
cnw_known_exploitedmirrored the two lists outside the BCP-07 model, append-only and keyed by meta
uuid, so retractions stayed forever and re-minted uuids produced duplicates. The same lists are
imported as BCP-07 catalogs by gcve-eu-kev, which reconciles retractions, so the feeders, their
/api/cisa_kev/and/api/cnw_kev/endpoints (served by/api/kev/filtered on
vulnerability_lookup_origin) and their storage accessors are removed; the classic
/known-exploited-vulnerabilities.{atom,rss}feed redirects permanently to
/kev-catalogs/feed.{format}.tools/purge_kvrocks_kev_feeders.pydeletes the leftover keys.
pyvulnerabilitylookup 4.5.0 is the client release that stops calling the removed endpoint
(#640, #641).
f4125a28, 696889ef, 7b77262a - chg: [feeders] The VARIoT feeder and source are disabled in the sample configuration, the service
being unreachable.
2c51de8f - chg: [vuln] A vulnerability description is rendered with its own line structure (about one CVE in
five wraps its lines, and advisories built from mailing-list posts carry headings and numbered steps),
escaped character by character with bare http(s) URLs linked, and with the escapes HTML-to-text
extraction leaves behind decoded once. Applied to the CVE 5 and FKIE NVD summaries.
72542d7b - chg: [website] The FSTEC card is rebuilt on the model of the CVE one through a normalising filter:
ISO dates, a link to the BDU record, status and KEV badges, severity chips with score, rating and
calculator link for CVSS 2.0/3.x/4.0, linked CWEs, aliases linked to their records, collapsible
references and impacted products, mitigations and credits, closed-vocabulary values translated.
1d89524e - chg: [website] The generic OSV card, shared by the eleven OSV-format sources, has the header of the
CVE one (icon badge, first upstream id, source and dates, withdrawn / VLAI / KEV badges) and the VLAI
severity classification.
36d90c97 - chg: [notifications] Each scheduler cycle takes at most ten due notifications, longest-waiting first,
and a run that started late by more than a quarter of its interval is re-anchored at a random earlier
point, so the population due after a downtime drains over a few cycles instead of landing again in
the same few minutes one interval later.
fd6e8cc3 - chg: [config] Every source of
SOURCES_TO_SHOWwhose identity is unambiguous has a one-line
description.
64bf8f7f - chg: [website] The user profile links to the credits page filtered on the user's name; the admin
users list highlights the active filter.
2cbac703, cbee4716 - chg: [lint] The ruff rule set is pinned in
pyproject.toml(E4,E7,E9,F): 0.16 widened
the default set from four families to some forty, turning a clean tree into 1167 findings, and the
pre-commit hook was pinned to a version that does not exist. CI and the hook take 0.16.7. The code
base uses PEP 585 builtins andcollections.abcinstead of the deprecated typing aliases, and the CSAF
andgcve_vlfeeders stamp their backoff andlast_updatesvalues with timezone-aware datetimes.
a5bc5960, f18c361d, 83a96ff7, fc4b7065 - chg: [dependencies] Updated Python and GitHub Actions dependencies, including alembic.
5dddd2c1, 2c07c321, 5d7a66f0, 208014be, 2a1b3965, 0a164da5, 815b06a3, ea025e06, 1b977b61
Fixes
- fix: [web] CSAF advisories render to pages of bounded size. A SUSE or Red Hat kernel advisory
repeated its full product table under every CVE (126,705 rows and 245 MB for one RHSA), inlined a
36 MB raw JSON block, and a kernel CVE page embedding ten such advisories reached 680 MB, which is
what Redis refused to cache: the recurringBrokenPipeErrorunder flask_caching was Redis closing
the connection on a value overproto-max-bulk-len, not a stale connection. A product list is now
rendered once per advisory and capped at 1000 rows, raw JSON above 1 MiB is fetched lazily, related
CSAF advisories are summary cards, and aBoundedRedisCacheskips and logs cache writes above
CACHE_MAX_VALUE_BYTES(8 MiB). Measured: 245 MB to 3.6 MB. The recent API clamps its page size to
100 like the other listings.
e8eff6d1, f915c374 - fix: [website] Raw JSON blocks no longer emit anchors with the JSON tail glued on: the nvd and
vulnrichment metas are decoded before pretty-printing so every URL ends at a real quote, and URL
linkification is switched off in every raw JSON viewer, first on the vulnerability page and then on the
six remaining call sites, with a regression test that sweeps the templates for any viewer without it.
Googlebot was requesting advisory URLs such as.../VDE-2025-052/",/"source/":..., as reported by
CERT@VDE (#648, #654).
cae1aaa3, cbbf15c4, 1af685a1 - fix: [web] The VEX badge and tab were hidden on every vulnerability carrying VEX metadata: a
\/
inside the lodash template's backtick literal made_.template()throw and abort the whole page
script.
938b2af4 - fix: [web] CVSS vectors wrap between their metrics instead of overflowing the severity card on
Chrome, withoverflow-wrap: anywhereas the fallback for a segment too long for a line.
ec1ba747, fd39016e - fix: [fulltext] Four defects that had frozen the production index: CSAF tracking ids carrying a colon
(RHSA-2026:12282) failed every batch withinvalid_document_id, so ids are sanitized on write and
hits linked by the original; an index creation task never run by a stalled Meilisearch scheduler
escaped the main loop and lost the buffer once per record, so creation is its own step remembered by
task uid; the backpressure probe ran once per pub/sub message (oneGET /tasksand one warning line
every 5 ms during a burst), now at most every 5 s with the warning repeated at most once a minute; and
the stringified CVE records the vulnrichment feeder publishes raised in the identifier accessor and
restarted the indexer at every batch (before that they were indexed under the id "none"), so they are
classified as an unknown format and a classification failure is isolated to its event. An instance
that indexed before this release holds anonedocument incve_list_v5to delete once.
98e9d043, bc85959d, 575a4e19, bf89921e - fix: [search] Meilisearch rejected the whole federated query when one index did not exist, and the
nvdindex never existed (its records are metadata on the CVE), so every search that ticked NVD
returned nothing at all. The nvd source leaves the index mapping, and missing indexes are dropped from
the query with a warning instead of failing everyone else's hits.
968f716b - fix: [notifications] The report scheduler walked Kvrocks on the event loop inside the transaction
opened by its reads, and PostgreSQL's idle-in-transaction timeout killed the connection under it
("KEV catalog lookup failed, hazard KEV policy skipped"); the exposure scheduler died the same way on
its first UPDATE. Reads are committed away before storage work, the walks run on a worker thread, and
the KEV lookups use a connection of their own.last_execution_timeis stored as the naive UTC it
claims instead of the session time zone's local time, so a notification no longer fires early or late
by the server's offset (#639).
d346aaf4, ca32c080 - fix: [kev] A KEV entry failing the model's identifier pattern (
vulnId: "UNKNOWN") answered 500;
POST and PUT now answer 400 with the validator's message.
8029b4d0 - fix: [web] The entry table of a catalog ordered by status date with no tiebreaker, so on a
bulk-synced catalog consecutive pages overlapped and skipped as many entries; both sort orders end on
the uuid. The catalog page now carries the catalog's name and description, a summary strip (entries,
last change, entries listed in the last 30 and 7 days, dominant evidence type, share also carried by
other catalogs) and, per entry, the other catalogs listing it.
7be603c3 - fix: [index]
index_vulnerabilitiesprunes the ids of a source index whose record is gone (deletions
that predate the cleanup on delete), which inflated the entry count and pagination of/recent
across every reindex (#629).
326ef897 - fix: [gna] The local GNA service never wrote
index:{local}:published, so the dashboard's "This
instance" card froze at the last reindex while/recentkept moving; it is written on every save and
cleaned on delete. Oneindex_vulnerabilitiespass backfills the entries saved since the last reindex
(#627, #628).
fc35deea - fix: [euvd] The creation pass read absence from the published state index as "not published" and
advanced its cursor past such CVEs for good, when it also means "not yet indexed". Unindexed candidates
are parked and re-examined every pass, the feeder warns while any remain, and
euvd --backfill-unmintedmints for instances that already ran against stale indexes; the mapping
export tests the rejected index instead of the published one (#597).
e1eb5d27 - fix: [monitoring] The heartbeat and log streams and
/api/system/checkProcessread a single SCAN
batch and dropped the cursor, so every process past the twentieth was missing from the monitoring
page. The cursor is followed, values are read in one MGET or pipeline, the Redis client is bound once
per stream, and the processes page shows a banner while a stream is down instead of stale timestamps
that look live (#622, #623).
bbce52b8 - fix: [feeds] A feed carrying a text node over 10 MB fell back to raw XML for browsers (lxml
Text node too long); it is parsed withhuge_tree.
1d6bab74 - fix: [product] A product's vulnerabilities were found by walking the whole source index and listed
once per matching synonym (30 cards twice and a total overstated by 18% for MISP). The candidates are
read from the{vendor}:{product}:vulnerabilitiessets and scored with ZMSCORE (0.30 s to 0.005 s),
de-duplicated and ordered newest first; an organization matches the union of its products' pairs. Also
fixed: a malformed?sinceserved a 500, cards showed an empty source, and the pagination line
counted "organizations".
6134666f - fix: [fstec] About one record in ten separates its aliases with commas, and the feeder split on
spaces only, linking the BDU record tocve-2025-32414,and leaving 21k bogus link sets behind.
Aliases are split on commas and whitespace, andtools/fix_fstec_alias_links.pyrepairs the stored
links (62,465 links across 23,231 records on one instance). FSTEC dates, stored as dd.mm.yyyy, were
blanked on/recentby the relative-date script.
f279d035, 4332a8fd - fix: [vulnogram] A reserved identifier was inserted lowercase and failed the field's pattern; the
Delete and Reject handlers were bound on the new-record page where the buttons do not exist, aborting
the rest of the page script.
a0c13ad8, 5b391970 - fix: [tests] Session-level settings a test SETs (a time zone) no longer outlive it on a pooled backend,
which made one CI matrix leg fail on every run; the KEV evidence and list assertions are scoped to
their own catalogs; the VLAI retrieval tests patch the originals mypy can see.
c78a3344, c2e2fb5b