Skip to content

ENISA Reporting — Overview

FLEET includes an ENISA Single Reporting Platform (SRP) module that lets a defender prepare and submit the staged notifications required by CRA Article 14 — actively-exploited vulnerabilities and severe incidents — and disseminate them to ENISA when a submission channel is available.

It is designed as an alternative pathway for collecting and disseminating this data: capture it now, in a queryable, versioned, audit-grade store, and hand it off to ENISA’s platform when its API exists (manual/form export until then).

  • Versioned OpenAPI 3.1 + Swagger UI — see REST API & Swagger.
  • GraphQL query surface over the same data — see GraphQL.
  • Obligation matrix — exactly which fields are required at each stage — see Obligation Matrix.
  • MCP tools so an AI assistant can drive the whole flow — see MCP Tools.

The feature lives in a self-contained workspace crate, crates/enisa-srp, that takes a PostgreSQL pool and exposes an axum router, an OpenAPI document, and a GraphQL schema. FLEET mounts it under /api/v1/enisa.

┌─────────────────────────────┐
│ Obligation matrix (versioned)│ source of truth
│ X / C / O / I / A per field │
└───────────────┬──────────────┘
│ drives validation
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────────┐ ┌─────────────────┐
│ PostgreSQL│◀────────────▶│ REST + OpenAPI│ │ GraphQL │
│ (relational│ immutable │ + Swagger UI │ │ (query surface) │
│ revisions)│ revisions │ (utoipa) │ │ (async-graphql) │
└───────────┘ └───────────────┘ └─────────────────┘
┌──────────────────────────┐
│ ENISA export adapter │ file / API stub → live ENISA API later
└──────────────────────────┘

The module is “versioned” on three independent axes:

  1. API version — everything is served under /api/v1/enisa.
  2. Report lifecycle — the same case is re-submitted as it matures (24h → 72h → final); each submission is an immutable revision.
  3. Audit / chain-of-custody — revisions are append-only. Database triggers reject UPDATE/DELETE, and each revision links to its predecessor, so you can prove what was known when. This matters because the reporter’s own systems may be compromised.

Each revision is a complete snapshot. Fields you don’t resubmit at a later stage are carried forward from the previous revision (the matrix C rule), so a 72h or final report doesn’t need to repeat everything from the 24h warning.

Storage is relational; GraphQL is a projection

Section titled “Storage is relational; GraphQL is a projection”

The store is plain PostgreSQL. GraphQL is a query projection over it (e.g. notification { revisions { … } }) — there is no separate graph database, which keeps FLEET’s single-database deployment intact.

Submission to ENISA is abstracted behind a pluggable EnisaExporter. FLEET delivers the form-ready document through its existing notification channels — email (via Resend) or a webhook — when one is configured (see Notifications & Report Delivery); otherwise it falls back to a file exporter / API stub. ENISA’s Single Reporting Platform has no public submission API yet, so that direct hop is the one piece still stubbed — when it ships, only the adapter changes; FLEET stays the stable interface. Every export, whichever adapter handles it, is recorded as an append-only receipt.

The OpenAPI document’s info.version tracks the obligation-matrix schema version (currently cra-2026.1). When ENISA revises the form, the matrix and this version are bumped together, so the contract and the rules never drift.