Graph overlay and tile cache schema (draft)

Status: DRAFT / NOT IMPLEMENTED (forward design). As of this revision no code in src/Graph (or anywhere in src/) produces, persists, or consumes a cached tile document of the shape described below. A repo-wide search for the distinguishing markers of this schema (graph-tile::, bbox, minX, verdictAt, statementId) returns no hits in src/. The only overlay-export artifact that exists today is GraphOverlayExporter (src/Graph/StellaOps.Graph.Indexer/Analytics/GraphOverlayExporter.cs), which writes unrelated overlays/clusters.ndjson, overlays/centrality.ndjson, and an overlays/manifest.json — not this tile cache. Treat this file as a target design, not a description of current behavior.

Overview

This document describes a proposed cached/materialized tile representation for gateway/UI components to store graph tiles alongside overlay data. It is a forward design and is not yet wired into any service.

This cache schema is distinct from the streaming NDJSON tile protocol that the Graph API actually implements today:

Relationship to the implemented streaming contract

The live Graph API (src/Graph/StellaOps.Graph.Api) does not emit the overlay arrays shown here. Instead, POST /graph/query and POST /graph/paths stream NDJSON tile envelopes, and overlays — when includeOverlays is requested — are attached per node, keyed by overlay kind, inside the node tile rather than collected into top-level arrays. The implemented contracts are:

So the per-node streaming overlay shape ({ kind, version, data: { … } }) is different from the cache overlay-record shape ({ nodeId, badge, policyId, verdictAt }) defined in the Schema section below. If this cache is ever implemented, that mapping must be reconciled.

Cached tile shape

A cached tile document is a single JSON object with:

Note on bbox. Graph tiles are not geospatial; the geographic-looking coordinate names (minX/minY/maxX/maxY with longitude/latitude-shaped sample values) are illustrative of an abstract 2-D layout extent, not real map coordinates. No layout/extent producer for this field exists in code yet.

Schema (draft)

{
  "version": "0.2",
  "tenantId": "tenant-default",
  "tile": {
    "id": "graph-tile::<scope>::<hash>::z8/x12/y5",
    "bbox": { "minX": -122.41, "minY": 37.77, "maxX": -122.38, "maxY": 37.79 },
    "zoom": 8,
    "etag": "c0ffee-etag"
  },
  "nodes": [
    {
      "id": "asset:...",
      "kind": "asset|component|vuln",
      "label": "optional display label",
      "severity": "critical|high|medium|low|info",
      "reachability": "reachable|unreachable|unknown",
      "attributes": {}
    }
  ],
  "edges": [
    {
      // NOTE: lower-case `type` values below are this draft's convention. The
      // implemented graph stores edge kinds upper-case (CONTAINS, DEPENDS_ON —
      // see GraphInspectorTransformer / SbomIngestTransformer), and the
      // streaming EdgeTile.Kind defaults to "depends_on". "evidence" is not a
      // recognized edge kind in current code.
      "id": "edge-1",
      "source": "nodeId",
      "target": "nodeId",
      "type": "depends_on|contains|evidence",
      "weight": 0.0,
      "attributes": {}
    }
  ],
  "overlays": {
    "policy": [
      { "nodeId": "nodeId", "badge": "pass|warn|fail|waived", "policyId": "policy://...", "verdictAt": "2025-01-02T03:04:05Z" }
    ],
    "vex": [
      { "nodeId": "nodeId", "state": "not_affected|fixed|under_investigation|affected", "statementId": "vex:...", "lastUpdated": "2025-01-02T03:04:05Z" }
    ],
    "aoc": [
      { "nodeId": "nodeId", "status": "pass|fail|warn", "lastVerified": "2025-01-02T03:04:05Z" }
    ]
  },
  "telemetry": { "generationMs": 0, "cache": "hit|miss", "samples": 0 }
}

Overlay-kind / field reconciliation (draft vs. implemented). The overlay kinds in this cache (policy, vex, aoc) match the kinds emitted today by InMemoryOverlayService. Note the following divergences against current code, to be resolved if this cache is implemented:

  • The implemented VEX overlay uses a field named status (values affected, under_investigation, not_affected) — this draft renames it to state and adds a fixed value that the service does not currently emit.
  • The implemented policy overlay carries a decision field (pass/warn/ fail) — this draft renames it to badge and adds a waived value.
  • The streaming gateway draft (graph-gateway-spec-draft.yaml) enumerates overlay kinds as policy, vex, advisory (not aoc); the live overlay service emits aoc rather than advisory. The two enumerations are not yet consistent.

Determinism rules

Constraints (draft)