Skip to content

Full-stack architecture

VELA is a ground-segment, advisory system. This page is the map of the whole stack; the design — architecture doc goes deeper on the reasoning graph, and data & ingest covers the telemetry pipeline.

The stack at a glance

Frontend — apps/console (React + Vite)Service — vela/api (FastAPI)Reasoning — vela/agents (LangGraph)Audit boundary — vela/mcpData — vela/kg + vela/adaptersOperator consoleCasefile IA · RBAC · Release gateInvestigationService+ HTTP endpointsDeterministic graphsupervisor → triage → investigator → validator → drafter → approvalMCPClient protocolknowledge · telemetry · procedureGraphStore · DocSearchin-memory · Neo4j · QdrantTelemetrySourcereplay · file · yamcs · openc3 · mqtt · aws_gs · ccsds HTTP prepare_investigation only path to data

One composition root, two entry points

vela/orchestration.py::prepare_investigation turns a scenario into a PreparedInvestigation — a triggering AnomalyEvent, a wired in-process MCP client, and a reasoning model. Both the CLI (vela/cli.py) and the HTTP service (vela/api/) go through it, so they drive an identical graph and produce identical reports. Store wiring happens here, deliberately outside the agent layer.

The reasoning graph is fixed and deterministic

vela/agents/graph.py is an auditable blueprint — not an agent choosing its own path:

supervisor ─┬─(suppressed)──────────────────────────────► END
└─(investigate)─► triage ─► investigator ─► validator ─► drafter ─┬─(procedure)─► approval ─► END
└─(none)──────────────────► END
  • State is a TypedDict; audit_log is a reducer. Each node returns only its own AgentTraceStep (annotated with operator.add) — never a read-modify-write of the accumulated list.
  • The approval gate is a real interrupt(). The graph compiles with a checkpointer keyed by thread_id == investigation_id. Resuming with Command(resume=…) continues the run. On resume the interrupted node re-executes from the top, so the only code before interrupt() in approval_node is deterministic and idempotent.

The MCP boundary is the audit boundary

vela/mcp/client.py defines the MCPClient Protocol — the complete data surface available to reasoning, spanning three servers:

Server Tools
knowledge channels_to_components, get_failure_modes, search_docs, get_doc_section, resolve_citation
telemetry get_window, get_command_history, compare_to_baseline
procedure get_procedure_templates, validate_procedure, format_report

InProcessMCPClient calls the tool functions directly (the real path today); serving them as real MCP endpoints over stdio/HTTP is a documented future seam. Every result is stamped with as_of for deterministic provenance.

Guardrail #4 — enforced by import-linter

vela/agents/ may import vela.mcp.client and nothing else data-shaped — no vela.kg, vela.adapters, or store/tool modules. To give agents a new capability, add a tool to an MCP server and expose it on the MCPClient protocol; never widen the import.

Stores sit behind protocols

  • GraphStore (vela/kg/store.py) → InMemoryGraphStore | Neo4jGraphStore
  • DocSearch (vela/kg/doc_search.py) → GraphKeywordDocSearch | QdrantDocSearch | HybridDocSearch

Chosen via store_backend / doc_search_backend in Settings, overridable by env. Nothing above the MCP layer changes when you swap them (ADR 0007).

Contracts are the single source of truth

Pydantic v2 models in vela/contracts.py (VelaModel, extra="forbid") define every shared shape. apps/console/src/api/types.ts mirrors them by hand — a contract change updates both in the same change. See ADR 0002.

Reasoning: deterministic by default, real Claude available

get_model returns DeterministicModel by default — required in airgap, used by the golden/CI suite for reproducibility. AnthropicModel (vela/agents/models/anthropic.py) is implemented and live-verified, key-gated and config-selected; its release-blocking grounding gates fail closed to INSUFFICIENT. Prompts live in versioned .md files under vela/agents/prompts/, never inline f-strings.

Frontend

One frontend: apps/console/ — the operator / production surface, contract-aligned, RBAC-gated, wired to the live API, tested (vitest + Playwright). See Frontend console.