Jobs & Progress

Understand the scan job lifecycle, real-time progress streaming, and how to interpret workflow stages.

Job Lifecycle

How a scan moves from upload to results.

Every scan job follows a state machine:

queued → running → done | failed
  • queued — Job created after file upload. Waiting for a worker to pick it up.
  • running — Worker is actively scanning. Progress events stream in real time.
  • done — Scan completed. Full report available for download.
  • failed — An error occurred. Check the last progress event for details.

Progress Streaming

Real-time visibility into scan stages.

The scanner writes NDJSON progress events to a file. The Go worker tails this file and inserts rows into the scan_events table. PostgreSQL NOTIFY triggers server-sent events (SSE) that push updates to your browser.

Scanner NDJSON → Worker tails → scan_events → pg_notify → SSE → Browser

Progress Stages

What each stage prefix means.

Stage PrefixMeaning
scan.startScan initialization
container.extract / container.layersExtracting container layers
container.packagesDetecting OS packages (dpkg, apk, rpm)
container.packages.appDetecting application packages (npm, pip, gem, etc.)
archive.extractExtracting ZIP archive (APK, JAR, wheel, etc.)
archive.typeClassified archive type
iso.detect / iso.repodataISO image analysis
osv.* / container.osv.* / archive.osv.*OSV vulnerability query
nvd.* / container.enrich.nvd / archive.enrich.nvdNVD enrichment
redhat.*Red Hat CSAF enrichment
binary.*Binary analysis (ELF/PE/Mach-O)
dmg.extractDMG disk image extraction
scan.done / scan.summaryScan completed
*.err / *.errorError in that stage
*.timingDuration of that stage (ms)

Stale Job Detection

What happens when a scan gets stuck.

Workers send periodic heartbeats while processing a job. If no heartbeat arrives within 30 minutes, the job is automatically marked as failed with a "stale job timeout" message.

To retry a failed scan, upload the file again. The original job and its events are preserved for debugging.