Hummingbird Data Flow
Overview of all data sources, what is stored where, and how data moves between systems.
Data sources
| Source | What it provides |
|---|---|
| Jira REST API | CVE tracker ticket fields (status, timestamps, labels, custom fields) |
Jira whiteboard (customfield_10841) |
Per-ticket timestamp cache written back by the analysis tool |
Red Hat Pulp (packages.redhat.com) |
SRPM/RPM publish timestamps for Hummingbird repos |
| Hummingbird container catalog API | Image rebuild history (when a package first appeared in a rebuilt image) |
Red Hat CSAF VEX feed (security.access.redhat.com) |
Advisory fix/not-affected status per CVE |
OSIDB (osidb.prodsec.redhat.com) |
Subpackage-level affectedness; flaw created_dt and hummingbird-1 affect created_dt |
| NVD / CVE list | CVE publish dates, product/version data |
| GitHub / GitLab | Upstream fix commit / PR / release dates |
| Fedora updates | Fedora update availability timestamps |
| Konflux SNS/SQS events | Pipeline run, snapshot, release, MR, push events |
PostgreSQL databases
There are two Postgres databases.
1. hummingbird-status — Konflux pipeline events
Fed by SNS/SQS events via the hummingbird-status ingestor.
| Table | Primary key | What is stored |
|---|---|---|
gitlab_pushes |
(sha, repo, ref) |
Git push events — sha, branch, commit list |
components |
name |
Konflux component definitions and git context |
pipelineruns |
name |
Build/test PLR outcomes, status, start/completion times |
snapshots |
name |
Multi-component snapshots, source PLR, sha |
releases |
name |
Release outcomes, images, LLM analysis text |
gitlab_merge_requests |
(project, iid) |
Current MR state, merge commit sha |
gitlab_mr_versions |
(project, iid, sha) |
Per-commit history of each MR head |
The legacy tables above remain in public. The independent pipeline-timing
service consumes a second SQS copy of each SNS envelope and writes only to its
Alembic-managed pipeline_timing schema in the same database. The
pipeline-timing Pod’s init container creates that schema and runs migrations
before its worker container starts. The single-replica Deployment uses
Recreate for upgrades. Scaling it to zero leaves the queue untouched without
affecting legacy ingestion.
| Timeline table | Primary key | What is stored |
|---|---|---|
events |
SNS message ID | Source, kind, timestamp, exact S3 bucket and key |
merge_requests, commits, merge_request_commits |
GitLab project ID and source identities | MR lifecycle and head/merge commits |
gitlab_pipelines, gitlab_jobs |
GitLab project ID and pipeline/job ID | Execution status and timing per attempt |
pipeline_runs, task_runs, snapshots, releases |
Cluster and Kubernetes UID | Konflux execution, bridge lineage, and Release completion |
These tables store selected fields and event references, not raw event JSON. The
archive retains the raw SNS envelope. Canonical HTTPS repository URL plus SHA links
MR commits, GitLab pipelines, and Konflux PipelineRuns; project and pipeline IDs
link GitLab jobs. Kubernetes owner UIDs link TaskRuns to PipelineRuns, while the
Snapshot label and source run name connect build to test, and a Release names its
Snapshot and managed PipelineRun. Distinct run UIDs and job IDs preserve retries.
Consumers calculate elapsed intervals from the stored timestamps. The Dashboard
continues to read only the public tables.
2. hummingbird-dashboard — CVE lifecycle + dashboard overlays
| Table | Primary key / unique | What is stored |
|---|---|---|
cve_ticket_events |
(ticket_key, event_type) |
One row per lifecycle milestone per ticket. occurred_at = timestamp; metadata = JSONB (see below). The source of all R-Time computations. |
cve_analysis_log |
id |
Each analysis tool run: timestamp, ticket count, log output |
cve_ticket_claims |
ticket_key |
Claim/lock for deduplicating concurrent analysis runs |
cve_ticket_blame |
(ticket_key, category) unique |
Human-assigned R-Time blame categories (hummingbird, prodsec, konflux, testing, pulp, other); a ticket may have multiple |
dashboard_settings |
key |
Runtime flags: auto-rerun enabled, analysis enabled, etc. |
auto_rerun_log |
id |
History of automatic retrigger attempts |
blocked_error_patterns |
id |
Regex patterns that suppress auto-rerun |
blocked_snapshots |
snapshot_name |
Manually blocked snapshots |
analysis_log / analysis_costs |
id |
LLM failure analysis runs and token costs |
blocked_push_builds / push_build_rerun_log |
id |
Push build blocking and retry history |
package_lifecycle |
(package, event_type) |
Package-scoped lifecycle milestones (e.g. rpm_first_published). Distinct from cve_ticket_events — one row per package/milestone, not per ticket. |
cve_ticket_events lifecycle milestone event_type values
These are the canonical names after the HUM-5918 migration:
event_type |
Meaning |
|---|---|
cve_published |
CVE published date (NVD / CVE list) |
osidb_flaw_created |
OSIDB flaw created_dt (when ProdSec ingested the CVE) |
osidb_affect_created |
hummingbird-1 affect created_dt for this ticket’s pscomponent (when a HUM tracker could be filed) |
cve_embargo_lifted |
OSIDB flaw unembargo_dt, only when it postdates created_dt (i.e. the flaw was genuinely embargoed; omitted otherwise) |
hum_ticket_created |
HUM Jira ticket creation date |
hum_ticket_closed |
HUM Jira ticket resolution date |
upstream_fix_merged |
Upstream fix commit / release merged |
fedora_update_available |
Fedora update containing the fix became available |
rpm_fix_published_to_pulp |
Fix RPM published to Hummingbird Pulp repo |
image_rebuilt_on_quay |
Hummingbird container image rebuilt with the fix |
vex_resolved |
Red Hat CSAF VEX advisory confirmed the resolution (HUM-5843) |
rpm_fix_published_to_pulp and image_rebuilt_on_quay are mutable — they
are updated when a newer delivery event supersedes the previous one. All other
event types are write-once: once set, occurred_at is never overwritten.
cve_ticket_events.metadata JSONB fields
Each row carries a metadata blob that reflects the ticket’s state at the time
of the most recent analysis run:
| Field | Source | Description |
|---|---|---|
computed_resolution |
analysis tool | Hummingbird’s computed fix status |
jira_status |
Jira status field |
Current Jira issue status |
labels |
Jira labels field |
All labels on the ticket |
fixed_in_build |
Jira customfield_10578 |
SRPM set manually in the “Fixed in Build” field |
detected_fixed_build |
Pulp / SRPM detection | SRPM filename auto-detected from Pulp repodata |
vex_status |
Red Hat VEX feed | fixed / known_not_affected / … |
vex_match_state |
reconciliation logic | matched / pending / mismatch |
vex_resolved |
VEX reconciliation | Timestamp when VEX first agreed with Jira resolution |
catalog_image_source |
collector catalog map | true when the package is a catalog image SBOM source (R-Time delivery is image publish); false means RPM publish. Missing on old rows: dashboard still requires image |
Jira whiteboard (customfield_10841)
The CVE analysis tool uses the Jira whiteboard field as a per-ticket timestamp cache. This is being phased out in favour of Postgres as the durable store (HUM-5917), but is still the source for a subset of fields.
The whiteboard holds compact JSON. The relevant sub-key is cve_cycle:
cve_cycle key |
Meaning | Mutable? |
|---|---|---|
cve_published |
CVE publish date | No — write-once |
jira_created |
HUM ticket creation date | No — write-once |
jira_closed |
HUM ticket close date | No — disabled (255-char limit) |
upstream_fix |
Upstream fix timestamp | No — write-once |
fedora_fix |
Fedora update timestamp | No — write-once |
hb_rpm_fix |
RPM published to Pulp | Yes — re-evaluated each run |
hb_image_fix |
Image rebuilt on Quay | Yes — re-evaluated each run |
The whiteboard is read and written only by the CVE analysis tool. The
dashboard never reads it directly; its authoritative source is always
cve_ticket_events.
Jira fields read by the CVE analysis tool
On each run the analysis tool fetches every open HUM CVE tracker ticket and reads the following fields from the Jira REST API:
| Jira field / custom field | Purpose |
|---|---|
summary |
Ticket title / package detection |
status, resolution, resolutiondate |
Ticket lifecycle state |
created |
Jira ticket creation date → hum_ticket_created |
description, comment |
CVE ID extraction, fix evidence |
labels |
cve-next-release, fix-in-progress, etc. |
security |
Embargo level |
assignee |
Ticket owner |
customfield_10578 (Fixed in Build) |
Manually set SRPM identifying the fix build |
customfield_10841 (Whiteboard) |
Cached cve_cycle timestamps (read + write) |
customfield_10667 (CVE ID) |
Structured CVE identifiers |
customfield_10860 (Embargo Status) |
Embargo flag |
customfield_10020 (Sprint) |
Sprint membership |
customfield_10014 (Epic Link) |
Parent epic |
Data flow
┌─────────────────────────────────────────────────────────────────┐
│ External data sources │
│ │
│ Jira REST API ─────────────────────────────────────┐ │
│ NVD / CVE list ────────────────────────────────────┤ │
│ GitHub / GitLab (upstream fix commits) ────────────┤ │
│ Fedora updates ────────────────────────────────────┤ │
│ Red Hat Pulp (RPM publish times) ─────────────────►│ │
│ Hummingbird catalog API (image rebuild times) ─────┤ │
│ Red Hat CSAF VEX feed ─────────────────────────────┤ │
│ OSIDB (subpackage affectedness) ───────────────────┘ │
└──────────────────────────────┬──────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ CVE analysis tool (cron, hummingbird-cve-analysis) │
│ │
│ • Reads Jira ticket fields + whiteboard cache │
│ • Resolves fix SRPM from Pulp repodata │
│ • Resolves image rebuild timestamp from catalog API │
│ • Checks upstream PR/release dates (GitHub/GitLab) │
│ • Fetches CVE publish date (NVD/CVE list) │
│ • Reconciles VEX status (CSAF feed) │
│ • Checks subpackage affectedness (OSIDB) │
│ │
│ Writes back to: │
│ ├── Jira whiteboard (cve_cycle timestamp cache) │
│ ├── Jira labels / Fixed-in-Build field │
│ ├── Jira comments │
│ └── dashboard DB → cve_ticket_events (one row per milestone) │
│ cve_analysis_log (run record) │
└──────────────────────────────┬───────────────────────────────────┘
│
┌───────────────────┴───────────────────┐
│ │
▼ ▼
┌──────────────────────────┐ ┌───────────────────────────────┐
│ Jira whiteboard │ │ dashboard DB │
│ (per-ticket JSON cache) │ │ cve_ticket_events table │
│ cve_cycle timestamps │ │ one row per (ticket, │
│ ← being phased out │ │ event_type) milestone │
│ (HUM-5917) │ └───────────────┬───────────────┘
└──────────────────────────┘ │
▼
┌──────────────────────────────────┐
│ hummingbird-dashboard API │
│ │
│ Pivots cve_ticket_events rows │
│ into per-ticket dicts, computes:│
│ • entry-level R-Time fields │
│ (HUM-5920) │
│ • duration legs inside stages │
│ (HUM-5921) │
│ │
│ Serves JSON + Jinja templates │
└──────────────────────────────────┘
flowchart LR
EVENTS[GitLab pushes, pipelines, jobs, MRs<br/>Kubernetes PipelineRuns, TaskRuns, Snapshots, Releases]
SNS[(SNS topic)]
ARCHIVER[S3 archiver]
S3[(Deterministic archive)]
STATUS[hummingbird-status ingestor]
STATUS_DB[(hummingbird-status DB)]
DASHBOARD[Dashboard API]
TIMELINE[Pipeline timing queue and worker]
TIMELINE_DB[(pipeline_timing schema)]
EVENTS --> SNS
SNS --> ARCHIVER --> S3
SNS --> STATUS --> STATUS_DB --> DASHBOARD
SNS --> TIMELINE --> TIMELINE_DB
Kubernetes TaskRun transport
The Kubernetes event forwarder applies optional resource labelSelector and
fieldSelector values to both its initial LIST and subsequent Watch requests.
Field-selector support depends on the Kubernetes resource and API server; TaskRun
collection should use a confirmed stable RPM application label instead of a
status field selector.
The forwarder publishes the logical kubeconfig context.cluster value, rather
than the API server URL, with each Kubernetes event. For TaskRuns, it removes
only metadata.managedFields before gzip/base64 encoding; identity, owner
references, labels, annotations, task specification, conditions, results, pod
name, and timing remain in the published and archived payload.
API field names
The dashboard API exposes these computed fields per R-Time entry:
Entry-level fields
| API field | Meaning |
|---|---|
cve_published_at |
Start of R-Time: CVE published, falling back to HUM created when missing (Include CVE-HUM); the later of HUM created and CVE published (Exclude, so an embargoed ticket also starts at disclosure) |
fix_delivered_at |
Done timestamp: latest of delivery and Jira close (Exclude VEX), plus VEX when Include VEX is on; unset until the required gates exist. Delivery is image publish for catalog image sources, RPM publish otherwise |
cve_to_delivery_hours |
Completed R-Time only (cve_published_at → fix_delivered_at); None while still accumulating |
cve_to_hum_created_hours |
Filing lag after NVD: max(HUM created − NVD, 0); 0.0 when HUM is first |
advisory_to_vex_hours |
Completed ADV-VEX only (hum_ticket_closed → vex_resolved); None until VEX exists |
display_advisory_to_vex_hours |
ADV-VEX hours, or elapsed-to-now when the VEX feed has not updated yet |
fix_before_cve_published |
True when the delivery timestamp predates notification (informational) |
hum_ticket_open |
True until the selected done gates exist |
deferred_to_next_release |
True for cve-next-release tickets |
display_duration_hours |
R-Time hours, or elapsed-to-now when the ticket is not yet done |
Duration legs (stages dict)
| API field | Interval |
|---|---|
cve_published_to_osidb_flaw |
CVE publish → OSIDB flaw created |
osidb_flaw_to_osidb_affect |
OSIDB flaw created → hummingbird-1 affect created |
osidb_affect_to_hum_created |
hummingbird-1 affect created → HUM ticket opened |
cve_published_to_hum_created |
CVE publish → HUM ticket opened |
cve_published_to_upstream_fix |
CVE publish → upstream fix merged |
hum_created_to_upstream_fix |
HUM ticket → upstream fix merged |
upstream_fix_to_fedora_update |
Upstream fix → Fedora update available |
fedora_update_to_rpm_fix_published |
Fedora update → fix RPM in Pulp |
upstream_fix_to_rpm_fix_published |
Upstream fix → fix RPM in Pulp |
hum_created_to_rpm_fix_published |
HUM ticket → fix RPM in Pulp |
rpm_fix_published_to_image_rebuilt |
Fix RPM in Pulp → image rebuilt on Quay |
image_rebuilt_to_hum_closed |
Image rebuild → HUM ticket closed |
hum_closed_to_vex_resolved |
HUM ticket closed (advisory MR merge) → VEX feed update |
Aggregate stats
| API field | Meaning |
|---|---|
avg_hum_created_to_rpm_fix_hours |
Mean HUM ticket → RPM fix (skips missing legs) |
avg_rpm_fix_to_image_rebuilt_hours |
Mean RPM publish → image rebuild |
avg_advisory_to_vex_hours |
Mean Done-Errata close → VEX feed (completed ADV-VEX only) |
Package lifecycle data
rpm_first_published is the earliest timestamp at which any SRPM for a given
package appeared in the Hummingbird Pulp repo. It is package-scoped — one
row per package — as opposed to cve_ticket_events which is ticket-scoped.
Collection
scripts/collect_rpm_first_published --prod --rpms-repo /path/to/rpms
Flow:
- Loads the package list from
rpms_repo.load_package_map_from_metadata(). - For each package: calls
pulp.fetch_earliest_srpm_time(), which browsespackages.redhat.com/.../source/Packages/{letter}/, parses the HTML listing already used by the analysis tool, and returns the earliest upload timestamp across all SRPMs for that package. - POSTs all results in one request to
/api/cve-import(same endpoint used forcve_ticket_eventssync). The dashboard upserts withCOALESCE(LEAST(existing, incoming), existing, incoming)— re-runs only updateoccurred_atif the incoming timestamp is earlier, and NULLs are never stored over a real value.
Surfaced in dump_lifecycle
scripts/dump_lifecycle --prod HUM-1234
After fetching ticket milestones from cve_ticket_events, the script resolves
the package name from those rows and makes a second call to
/api/cve-export?section=package_lifecycle to fetch rpm_first_published for
that package. It is shown at the bottom of the text output and under
rpm_first_published in the JSON output.