Skip to content

Events and Findings

Events and findings are the two outputs of a scan. They serve different purposes: events are a running log of what happened, findings are durable security issues for human review.

Events

An event is an append-only record emitted during a scan. Events are never edited or deleted — they're a log. They exist to:

  1. Drive the live scan feed in the dashboard (polling-based)
  2. Provide a full debug trace for operators after the scan finishes

Events have a kind field that describes what happened:

Kind Description
scan_started The scan engine started running
scan_progress Progress update (e.g., "auditing component 3 of 8")
scan_log A debug log line from v16 or Codex
finding_updated A security issue was found or updated
scan_completed The scan finished successfully
scan_failed The scan encountered an unrecoverable error
scan_cancelled A user or operator stopped the scan

Events have a JSON payload that varies by kind. For finding_updated, the payload contains the finding details. For scan_progress, it contains a message and progress percentage.

Key files: app/events/models.py, app/events/service.py

Findings

A finding is a structured, durable record of a specific security issue. Unlike events (which are logs), findings are meant for triage — users review them, mark them confirmed or dismissed, and track them over time.

Each finding has:

Field Description
severity critical, high, medium, low, or info
title Short description of the issue
file_path Path to the affected file in the repository
status open, confirmed, or dismissed
evidence The scan engine's supporting reasoning
scan_id The scan that produced this finding
repository_id The repository it belongs to

Key files: app/projects/models.py, app/projects/service.py

How findings are created

Findings don't come from a separate API call — they're automatically extracted from finding_updated events:

v16 scan engine
    ↓ emits finding_updated event
app/projects/v16_adapter.py
    ↓ maps to Vega event format, calls back into ProjectService
ProjectService.upsert_finding()
    ↓ creates or updates the finding record
PostgresStore (or JSON file)
    ↓ persisted
GET /v1/repositories/:id/findings
    ↓ frontend reads findings

Upsert semantics — if the scan engine reports the same logical finding twice (e.g., with updated confidence), the backend updates the existing record rather than creating a duplicate. The deduplication key is based on the finding's file path and title within a scan.

Where findings appear in the UI

Page What it shows
Repository findings page All findings for one repository across all scans
Scan findings view Findings from one specific scan
Project findings page All findings across all repositories in a project
Findings inbox Cross-project view of all open findings
Finding detail page Full context for one finding including evidence

Debugging

Events appear in logs but no findings show up: 1. Confirm v16 emitted finding_updated events — check v16-events.jsonl in the scan artifacts. 2. Check app/projects/v16_adapter.py — does it handle the finding_updated event kind from the version of v16 being run? 3. If using Postgres, confirm migration 004_findings_columns.sql has been applied. 4. Check the findings page filters — severity and status filters might be hiding results.

Findings exist but the UI shows nothing: 1. Open the findings page and clear all filters. 2. Check the API response directly: GET /v1/repositories/:id/findings 3. Confirm the frontend is using the correct project/repository ID.