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.

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

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

Section titled “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.

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.

  • 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).

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

Section titled “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.

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