Graph Gateway API (draft)

This document describes the Graph Gateway (StellaOps.Graph.Api) HTTP surface and points to the authoritative contracts. It is reconciled against the implementation in src/Graph/StellaOps.Graph.Api (notably Program.cs, the Endpoints/* and Contracts/* files, and Security/GraphPolicies.cs).

Audience: API client integrators building impact-analysis, lineage, or export workflows against the Graph Gateway, and UI authors consuming its streaming tile protocol.

Authoritative contracts

What the Graph Gateway is for

Graph Gateway provides deterministic, tenant-scoped graph queries used for:

Endpoints

All routes are minimal-API endpoints registered in src/Graph/StellaOps.Graph.Api/Program.cs and the Endpoints/*.cs files. Streaming routes return application/x-ndjson; the remaining routes return application/json.

Streaming graph routes

Method & pathScope policyStreaming?Notes
POST /graph/searchgraph:read or graph:queryNDJSONNode + cursor tiles.
POST /graph/querygraph:queryNDJSONNode/edge/stats/cursor tiles; may return 501 (see below).
POST /graph/pathsgraph:queryNDJSONPath tiles (pathHop set); may return 501.
POST /graph/diffgraph:queryNDJSONSnapshot diff tiles (added/removed/changed).

JSON graph routes

Method & pathScope policyNotes
POST /graph/lineagegraph:read or graph:queryReturns a lineage object (not NDJSON).
POST /graph/exportgraph:exportReturns a completed export manifest (200, status:"completed"). On live hosts this currently returns 501 (GRAPH_FEATURE_UNAVAILABLE); see “Backend availability”.
GET /graph/export/{jobId}graph:exportDownloads the export file; sets X-Content-SHA256. 404 GRAPH_EXPORT_NOT_FOUND for unknown/cross-tenant jobs.
POST /graph/edges/metadatagraph:read or graph:queryBatch edge-metadata lookup.
GET /graph/edges/{edgeId}/metadatagraph:read or graph:querySingle edge; 404 EDGE_NOT_FOUND.
GET /graph/edges/path/{sourceNodeId}/{targetNodeId}graph:queryEdges along a path with metadata.
GET /graph/edges/by-reason/{reason}graph:read or graph:queryreason must parse to an EdgeReason; 400 INVALID_REASON otherwise. Supports limit (default 100) and cursor.
GET /graph/edges/by-evidencegraph:read or graph:queryQuery params evidenceType, evidenceRef.
GET /healthz(anonymous){ "status": "ok" }.

The edge-metadata family (/graph/edges/...) returns 501 GRAPH_FEATURE_UNAVAILABLE on live hosts; see “Backend availability”.

Compatibility routes (CompatibilityEndpoints.cs)

Tenant-scoped read/query routes used by the UI/legacy clients (all enforce .RequireTenant()):

Asset registry routes (AssetRegistryEndpoints.cs, base /graph/assets)

Cartographer-compatible job routes (CartographerEndpoints.cs)

These match the Scheduler Worker’s HTTP client contracts and are not scope-gated by GraphPolicies (they rely on tenant/identity from the request body and the audit pipeline):

Job ids are deterministic: an Idempotency-Key header collapses retries; otherwise the id derives from the canonical request body so byte-equal POSTs collapse to the same job.

Tenancy and authentication

NDJSON tile streaming

The streaming routes (/graph/search, /graph/query, /graph/paths, /graph/diff) emit application/x-ndjson, where each line is a serialized TileEnvelope (Contracts/SearchContracts.cs).

Envelope fields:

There is no diagnostic tile type in the implementation. Errors surfaced mid-stream use type: "error" carrying an ErrorResponse payload. The draft OpenAPI’s diagnostic enum value and DiagnosticTile schema are not implemented.

Request shapes and limits

Validators in Contracts/SearchContracts.cs enforce (selected rules):

Validation failures return 400 with error: "GRAPH_VALIDATION_FAILED".

Cursor and resume

Search/query responses include a cursor tile carrying an opaque token and a resumeUrl when more results remain. Requests can pass a cursor field to resume from a prior stream.

Budgets and rate limits

Overlays

node and edge tiles may carry overlays when requested (includeOverlays: true). Overlays are keyed by overlay kind and use the OverlayPayload shape { kind, version, data }.

On live (non-Testing) hosts the overlay service is UnsupportedGraphOverlayService, which throws GRAPH_FEATURE_UNAVAILABLE (501) rather than synthesizing policy/VEX/AOC overlays. See “Backend availability”.

For the cache/materialized representation used by gateway/UI tile caches, see docs/api/graph/overlay-schema.md.

Backend availability

The service guards configuration on startup: outside the Testing environment a Graph Postgres connection string (Postgres:Graph:ConnectionString or ConnectionStrings:Default) is required, or startup fails.

Several features are backed by Unsupported* services on live hosts and therefore return 501 with error: "GRAPH_FEATURE_UNAVAILABLE" (and a details.feature discriminator) until durable backends are wired:

search, query, paths, and diff are served by in-memory implementations over the Postgres runtime repository and remain available.

Errors and determinism

Error responses use the ErrorResponse shape { error, message, details?, requestId? }. Codes emitted by the service:

Determinism requirements:

Observability

Metrics are emitted via the StellaOps.Graph.Api meter (Services/GraphMetrics.cs):

Per-request audit events (tenant, route, method, actor, scopes, status, duration) are logged and forwarded to the unified audit pipeline.

Known spec drift (draft OpenAPI vs. implementation)

The following items in docs/api/graph-gateway-spec-draft.yaml do not match the shipped service: