Skip to content

Supply-chain graph: index image flow with Kùzu (embedded graph store) #145

Description

@toddysm

Summary

Introduce a supply-chain graph for the CSSC Framework reference implementation that indexes the flow of container images through the supply chain — acquisition, base-image lineage, dependencies (SBOM), vulnerabilities, builds, promotion, and deployments — and supports multi-hop provenance/reachability queries across multiple images.

Decision: Use Kùzu — an embedded, serverless property-graph database with Cypher — as the graph store.

Motivation

We already produce rich supply-chain signals that form a natural DAG:

  • OCI image annotations (org.opencontainers.image.*, com.toddysm.*, base name/digest, build run URL)
  • SPDX SBOM referrers (OCI 1.1)
  • SLSA provenance referrers (OCI 1.1)
  • GHCR package listings (quarantine/*, golden/*, apps/*)
  • Promotion-pending / CI-failure tracking issues

Relational joins are awkward for the questions we actually want to ask (variable-length, multi-hop). A graph store lets us answer, e.g.:

  • Full provenance chain of a given image digest
  • Which deployments run an image that transitively depends on a package with CVE-X (blast radius)
  • What upstream image a golden image was mirrored/promoted from

Proposed graph model (grounded in existing artifacts)

Nodes: Image (per digest), Tag, BaseImage, Package/Component, Vulnerability, Build, Deployment, Issue, Source.

Edges:

  • Image -[MIRRORED_FROM]-> UpstreamImage
  • Image -[BUILT_FROM]-> BaseImage (base pinned by digest)
  • Image -[CONTAINS]-> Package (from SBOM)
  • Package -[AFFECTED_BY]-> Vulnerability
  • Build -[PRODUCED]-> Image, Build -[FROM_SOURCE]-> Source
  • Image -[PROMOTED_FROM]-> QuarantineImage (+ related Issue)
  • Deployment -[RUNS]-> Image
  • Tag -[POINTS_TO]-> Image

Alternatives considered

Requirements that drove the decision (agreed with maintainer): build it ourselves / in-repo / Python-native, embedded (no server, single on-disk store, ships inside a service), and programmatic/API querying is sufficient (the dashboard renders its own views; interactive graph-browser visualization is not a first-class requirement).

Broader landscape (rejected before narrowing to embedded)

Option Model Why not chosen
GUAC (Graph for Understanding Artifact Composition) Purpose-built supply-chain graph; ingests SBOM/SLSA/in-toto/OCI; GraphQL Highest domain alignment, but requires running a server + DB and is "adopt a tool" rather than "build it ourselves"; less illustrative as a reference implementation
Neo4j / Memgraph Server property graph, Cypher Best interactive visualization tooling, but requires a running server; visualization is not a first-class requirement here
PostgreSQL + Apache AGE Relational + openCypher in one DB Viable single-store option, but pulls in a Postgres server; heavier than needed for an embedded demo
RDF triple store (Jena / GraphDB / Oxigraph) RDF / SPARQL Most standards-pure for provenance, but steepest learning curve; overkill for this reference implementation

Embedded candidates (the shortlist)

Option Why not first choice
SQLite + recursive CTEs Zero new deps, but hand-written recursion; variable-length path queries get painful as the model grows
DuckDB Excellent columnar analytics + recursive CTEs, but not a graph model — property-graph traversals are verbose
NetworkX Pure-Python with rich algorithms, but in-memory only — no durable single-file store, so it's a compute layer, not a source of truth

Decision: Kùzu

  • Embedded, serverless — persists to a single on-disk database, no server process ("SQLite for graphs"); matches the no-server / ships-inside-a-service constraint.
  • Python-nativepip install kuzu; drops into the existing apps/python-app/ FastAPI structure.
  • Cypher — declarative multi-hop path queries for the provenance/reachability questions above.
  • Columnar / fast for analytical multi-image traversals.

Optional hybrid: keep Kùzu as the source of truth and hydrate small subgraphs into NetworkX for algorithms Cypher doesn't express well (centrality, blast-radius scoring). Start with Kùzu alone.

Proposed placement in the repo

  • apps/python-app/libs/cssc_common/ — graph model (node/rel schemas) + ingesters reading OCI annotations, SPDX SBOM referrers, SLSA provenance referrers, GHCR packages, and tracking issues.
  • apps/python-app/services/graph-service/ — FastAPI service owning the Kùzu DB, exposing query endpoints (provenance chain, blast radius, deployment inventory); dashboard-web renders views on top.
  • Deployment — Kùzu DB directory on a mounted volume (PVC in kind), or rebuilt on ingest for a stateless demo; Helm subchart consistent with existing services.

Next steps

  • Draft design doc (docs/architecture/cross-cutting/supply-chain-graph.md) — spans acquire/build/catalog/deploy/run.
  • Finalize node/edge schema + properties.
  • Define ingestion sources → graph mapping.
  • Define service shape + query API.
  • Define persistence/deployment model in kind.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions