ReachGraph
Unified store for reachability subgraphs with edge-level explainability.
Audience: Policy authors and reachability consumers (Console, CLI, ExportCenter) who need to query and prove “exactly why a dependency is reachable,” and engineers populating the store from Scanner and Signals.
Purpose
The ReachGraph module provides a unified store for reachability subgraphs, enabling fast, deterministic, audit-ready answers to “exactly why a dependency is reachable.” It consolidates data from Scanner, Signals, and Attestor into content-addressed artifacts with edge-level explainability.
Quick Links
- Architecture - Technical design and implementation details
- Guides - Usage and query guides
- Schemas - ReachGraph schema definitions
Status
| Attribute | Value |
|---|---|
| Maturity | Production |
| Last Reviewed | 2026-05-29 |
| Maintainer | Scanner Guild, Signals Guild |
Key Features
- Unified Schema:
reachgraph.min@v1minimal subgraph format with edge explainability (ReachGraphMinimal) - Content-Addressed Store: All artifacts identified by a BLAKE3-256 digest (
blake3:<64-hex>) - Slice Query API: Fast queries by package PURL, CVE, entrypoint, or file
- Deterministic Replay: Verify that same inputs produce same graph (audited in
reachgraph.replay_log) - DSSE Signing: Offline-verifiable proofs (signing hooks exist in
StellaOps.ReachGraph.Signing; the signer is not yet wired into the WebService DI — graphs are stored unsigned today)
Dependencies
Upstream (this module depends on)
- Scanner - CallGraph data source; populates the store via
POST /v1/reachgraphs - Signals - runtime reachability facts, queried over HTTP (
GET /signals/facts/{digest}) for the unified/v1/reachability/*API - Attestor - DSSE signing model (hooks present; signer not wired in the WebService today)
Downstream (modules that depend on this)
- Policy Engine - Reachability-based policy evaluation
- Web Console - Reachability visualization
- CLI - Reachability queries
- ExportCenter - Reachability data exports
API Endpoints
Store API (ReachGraphController):
POST /v1/reachgraphs- Upsert reachgraph (idempotent by digest)GET /v1/reachgraphs/{digest}- Retrieve reachgraph by digestGET /v1/reachgraphs/{digest}/slice- Query slice of reachgraphGET /v1/reachgraphs/by-artifact/{artifactDigest}- List subgraphs for an artifactPOST /v1/reachgraphs/replay- Verify deterministic replayDELETE /v1/reachgraphs/{digest}- Delete a reachgraph (graph:adminonly)
Unified reachability query API (ReachabilityController):
POST /v1/reachability/{static|runtime|hybrid|batch}
CVE-symbol mapping API (CveMappingController):
GET /v1/cve-mappings/{cveId},GET /v1/cve-mappings/by-package,GET /v1/cve-mappings/by-symbol,POST /v1/cve-mappings,POST /v1/cve-mappings/analyze-patch,POST /v1/cve-mappings/{cveId}/enrich,GET /v1/cve-mappings/stats
All controllers are versioned under v1/... and the gateway proxies them under /api/v1/.... The service also exposes anonymous GET /healthz (liveness) and GET /readyz (readiness) probes plus a build-info endpoint.
See architecture.md for full endpoint, schema, and persistence detail.
Authentication & Authorization
- The service registers Authority resource-server authentication (
AddStellaOpsResourceServerAuthentication, configuration sectionAuthority:ResourceServer). Outside theTestingenvironment a fallback authorization policy requires an authenticated user on every endpoint that does not opt out with[AllowAnonymous](only/healthz,/readyz, OpenAPI, and build-info are anonymous). - The only scope-gated action today is
DELETE /v1/reachgraphs/{digest}, which requiresgraph:admin(StellaOpsScopes.GraphAdmin, enforced byReachGraphDestructiveActionAuthorizer). Missing scope returns403 insufficient_scope; unauthenticated returns401. - Read and write endpoints currently require only an authenticated principal — they are not scope-gated. The canonical
graph:readandgraph:writescopes (defined inStellaOpsScopes) are not yet enforced on these routes; access is constrained by per-tenant rate limiting instead. - Tenant scope is resolved from the canonical
X-StellaOps-TenantIdheader (legacyX-Tenant-IDfallback) or thetenantJWT claim. PostgreSQL row-level security isolates each tenant’s subgraphs, slices, and replay logs viaapp.tenant_id. - Rate limiting is per-tenant fixed-window:
reachgraph-readallows 100 req/min,reachgraph-writeallows 20 req/min; exhausted limits return429.
Persistence
- PostgreSQL schema
reachgraph(auto-migrated on startup viaAddStartupMigrationsfrom embedded001_reachgraph_store.sql):reachgraph.subgraphs— content-addressed gzip blob keyed bydigest, withartifact_digest,tenant_id,scope/provenanceJSONB, and node/edge counts.reachgraph.slice_cache— precomputed slices (query_typeinpackage/cve/entrypoint/file) with TTL expiry.reachgraph.replay_log— audit log of deterministic replay verifications.
- Valkey/Redis (
ConnectionStrings:Redis) provides the hot-path graph and slice cache (ReachGraphValkeyCache).
Related Documentation
- ReachGraph Architecture — full endpoint, schema, and persistence detail.
- API reference — generated endpoint reference.
- Scanner Architecture — CallGraph data source.
- Signals Architecture — runtime reachability facts.
- Policy Engine — primary downstream consumer of reachability verdicts.
