VELA demo runbook
How to run a compelling, reliable VELA demo end to end — the 90-second story, exact commands, the live-LLM “wow”, both frontends, the ESA-ADB detection benchmark, and what to do when something misbehaves on stage.
Golden rule for a live demo: the deterministic air-gap path is the reliable demo — it always produces the same cited investigation with no network, no keys, no surprises. The live-LLM path is the wow — real, but it depends on a provider being reachable. Rehearse both; lead with whichever your audience needs and keep the deterministic path one keystroke away as the fallback.
Everything below is advisory-only by construction: VELA reports, cites, and drafts a recovery procedure for a human to execute. It never commands the spacecraft. All spacecraft data is the synthetic VELA-REF-1 reference spacecraft plus seeded replay scenarios — never a real mission.
0. One-time setup
uv venv --python 3.12 && uv pip install -e ".[dev]" # Python 3.12 in .venv; installs httpx toonpm install # root: husky hooks (once)Use make (it hardcodes PY = .venv/bin/python) or activate the venv first
(. .venv/bin/activate). The optional extras you might add for a fuller demo:
.[api]— the HTTP service (python -m vela.api), needed for the operator console..[cloud]— the Anthropic reasoning path (langchain-anthropic). The provider path (Gemini/Sarvam/OpenAI-compatible) needs onlyhttpx, which the.[dev]install already brings..[backends]— real Neo4j + Qdrant stores (not required for the demo; in-memory is the default).
Confirm you are ready:
python -m vela.cli scenarios # lists the 10 scenariosmake demo # the reliable deterministic investigation, held for approval1. The 90-second story
The narrative arc, mapped to what appears on screen. Run one investigation and talk over it:
python -m vela.cli investigate --scenario CELL_DEGRADATION --approve --replay-
An anomaly arrives. Battery voltage collapses under transmit load with rising cell temperature. VELA forms a triggering
AnomalyEvent— the same event a detector or an operator annotation would raise. -
A cited root cause + a drafted procedure. The graph runs a fixed, auditable path (
supervisor → triage → investigator → validator → drafter → approval) and prints a root-cause narrative and a step-by-step recovery procedure — with every claim cited to the spacecraft’s own knowledge: FMECA failure modes, ops-manual sections, telemetry windows. This is the differentiator: not “an LLM thinks it’s the battery,” but a claim you can click through to the FMECA entry and the manual section that support it. -
The grounding gates catch fabrication. VELA prints, deterministically, lines like:
Citation integrity: PASS (N citation(s) checked)Releasable to operator: yesTelemetry-value grounding [advisory]: PASS (N numeric assertion(s) checked)Citation entailment [advisory, lexical]: PASS (N doc span(s) checked)The rule is symmetric and load-bearing: a grounded step with no citation is a violation (it is forced to
UNGROUNDED), and anUNGROUNDEDstep that carries a citation is also a violation. A single ungrounded step flips “Releasable to operator” tono (ungrounded steps blocked)— the fabrication never reaches the operator. A hallucinated reference is a sev-1 and makesinvestigateexit non-zero, failing any pipeline it runs in. Narrate this as the safety mechanism: VELA would rather say “I can’t support that” than hand an operator a confident, unsupported claim. -
A human decides. Without
--approve, the drafter pauses at a real approval gate and printsHuman approval: HELD — re-run with --approve to release the draft. Releasing resumes the exact checkpointed graph — a genuine resume, not a re-run. This is the advisory-only boundary made concrete: the one action a human takes is releasing a draft, never commanding. -
Audit and replay.
--replayprints the forensic checkpoint sequence (Forensic replay — N checkpoints: … -> … -> …) and the full agent trace. Every investigation is one auditable record.
Differentiators to name out loud: citations to the spacecraft’s own FMECA and manuals; an
UNGROUNDED step being blocked from release; the telecommand-response suppression (§2.2);
the air-gap posture (deterministic, zero egress); and never commands — advisory only,
enforced by a committed guardrail, not good intentions.
Want the artifacts on disk to show afterwards?
python -m vela.cli investigate --scenario CELL_DEGRADATION --approve --out ./out# writes ./out/<investigation-id>.md and ./out/<investigation-id>.audit.json2. CLI demo — the three best scenarios
Default mode is airgap (deterministic). Add --mode cloud for the deployment posture; add the
live-LLM env from §3 to reason with a real model. Full scenario list: python -m vela.cli scenarios.
2.1 CELL_DEGRADATION — the full cited flow (EPS, warning)
The headline demo: a real anomaly (battery capacity fade under transmit load) that produces a cited narrative and a drafted recovery procedure held for approval.
# Deterministic (reliable): held for approval, then releasedpython -m vela.cli investigate --scenario CELL_DEGRADATIONpython -m vela.cli investigate --scenario CELL_DEGRADATION --approve --replay
# Cloud deployment posture (still deterministic unless you set the §3 env)python -m vela.cli investigate --scenario CELL_DEGRADATION --mode cloud --approve --replay2.2 SUPPRESSED_MOMENTUM_DUMP — the telecommand filter kills a false alarm (AOCS)
Wide reaction-wheel speed swings look alarming — but they immediately follow a desaturation
telecommand, so they are the expected response, not a fault. VELA’s telecommand-response filter
suppresses the deviation instead of raising a spurious investigation. This is the “check
get_command_history before crying hardware failure” discipline, automated.
# The investigation is suppressed at the supervisor (no false procedure drafted):python -m vela.cli investigate --scenario SUPPRESSED_MOMENTUM_DUMP
# See the same suppression at the detector level (note the SUPPRESSED tag):python -m vela.cli detect --scenario SUPPRESSED_MOMENTUM_DUMP --method ecodpython -m vela.cli detect --scenario SUPPRESSED_MOMENTUM_DUMP --method graphContrast it live with a scenario that is not command-explained (so you show the filter is discriminating, not just silencing everything):
python -m vela.cli detect --scenario WHEEL_FRICTION --method graph2.3 BATTERY_OVERTEMP — a critical severity (EPS)
Cell temperature climbs past its limit while bus voltage stays nominal — a thermal-runaway precursor, the highest severity in the deck. Good for showing VELA escalates appropriately.
python -m vela.cli investigate --scenario BATTERY_OVERTEMP --approve --replaypython -m vela.cli investigate --scenario BATTERY_OVERTEMP --mode cloud --approve --replayThe three detection trigger paths (optional depth)
Every scenario can be triggered three ways, all converging on the same AnomalyEvent:
python -m vela.cli investigate --scenario CELL_DEGRADATION --detector annotation # defaultpython -m vela.cli investigate --scenario MEMORY_SEU_STORM --detector graph # KG-aggregatedpython -m vela.cli detect --scenario WHEEL_FRICTION --method ecod # per-channelgraph aggregates per-channel shift scores through the knowledge graph, catching distributed
subsystem faults that per-channel ECOD misses — the training-free stand-in for the learned detector.
3. Live-LLM demo — the “wow”
Reason with a real model. This is a deployment posture, not a different pipeline: the same
vela.cli investigate runs, only the reasoning backend changes, and it is selected entirely by
environment. airgap mode refuses any non-deterministic backend (no egress path), so the live
path requires cloud mode (--mode cloud).
Fail-closed is a feature to narrate. If the model errors, times out, returns malformed output, or
the key/token is missing, the reasoning layer raises internally and the drafter degrades the verdict
to INSUFFICIENT rather than emitting a confident, ungrounded answer. On top of that, the same
release-blocking grounding gates (telemetry-value + citation-entailment) run on the model’s output, so
a fabricated value or an unsupported citation is forced UNGROUNDED regardless of which provider
produced it. The live model gets to be persuasive; it does not get to be unaccountable.
3.1 Gemini via Vertex AI (ADC bearer token)
The api-key env holds a short-lived gcloud auth print-access-token bearer; the project is your GCP
project. No Google SDK required — the provider path calls Vertex generateContent over httpx.
export VELA_REASONING=providerexport VELA_REASONING_TRANSPORT=vertexexport VELA_REASONING_PROJECT=<your-gcp-project>export VELA_REASONING_MODEL=gemini-3.6-flashexport VELA_REASONING_API_KEY_ENV=GOOGLE_TOKEN # NAME of the env var holding the tokenexport GOOGLE_TOKEN="$(gcloud auth print-access-token)"export VELA_REASONING_PROVIDER=gemini # optional: the label shown in the report
python -m vela.cli investigate --scenario CELL_DEGRADATION --mode cloud --approve --replay# or simply: make demo-cloudADC tokens expire (~1 hour). If a run fails auth, re-run gcloud auth print-access-token and re-export
GOOGLE_TOKEN.
3.2 Sarvam (or any OpenAI-compatible endpoint)
The openai transport (the default) posts to <endpoint>/chat/completions. This covers Sarvam,
OpenAI, Gemini’s OpenAI-compatible endpoint, or a local vLLM/Ollama serving open weights (a
sovereign / air-gap-adjacent posture — no public egress).
export VELA_REASONING=providerexport VELA_REASONING_TRANSPORT=openaiexport VELA_REASONING_ENDPOINT=https://api.sarvam.ai/v1 # OpenAI-compatible base URLexport VELA_REASONING_MODEL=<provider-model-id>export VELA_REASONING_API_KEY_ENV=SARVAM_API_KEY # NAME of the env var holding the keyexport SARVAM_API_KEY=<your-key>export VELA_REASONING_PROVIDER=sarvam # optional: report label
python -m vela.cli investigate --scenario BATTERY_OVERTEMP --mode cloud --approve --replay# or: make demo-cloud DEMO_SCENARIO=BATTERY_OVERTEMP3.3 Anthropic (Claude)
export VELA_MODE=cloud VELA_REASONING=anthropicexport ANTHROPIC_API_KEY=<your-key> # needs the .[cloud] extra: uv pip install -e ".[cloud]"python -m vela.cli investigate --scenario CELL_DEGRADATION --mode cloud --approve --replay# or: make demo-cloud3.4 make demo-cloud
make demo-cloud composes the same investigate command, parameterised by the env above. With no
provider env set it prints a clear message and exits — it never silently falls back to deterministic:
make demo-cloud # prints setup help + exits if no provider envVELA_REASONING=provider … make demo-cloud # runs the live path (see §3.1 / §3.2)make demo-cloud DEMO_SCENARIO=BATTERY_OVERTEMP # pick the scenarioThe report footer prints Reasoning: <label> · mode: cloud, so the audience can see a real model
(gemini / sarvam / anthropic) drove the reasoning — while the citation and grounding verdicts
above it prove it stayed accountable.
4. The operator console (apps/console/)
VELA has one UI: the production, advisory-only React console over the investigation service. It
renders exactly what the service’s cited InvestigationReport contains — the verdict, the
citation-integrity + release status, the narrative, the drafted procedure with each step’s citations
(any UNGROUNDED step flagged and blocked), the assumed channel→sensor bindings, the approval gate,
and the audit/replay view with a backend-readiness pill. Its one action is releasing a held draft —
never commanding.
# Terminal A — the API (needs the .[api] extra):uv pip install -e ".[api]"python -m vela.api # serves http://127.0.0.1:8000 (interactive docs at /docs)
# Terminal B — the console:cd apps/console && npm ci && npm run dev # http://localhost:5173The console opens in offline demo mode (in-memory fixtures — great when there is no backend or the
wifi is hostile). Switch the source selector to live service to drive the running API. For the
release step against the live API, note the approval resumes an in-process checkpoint — keep the same
python -m vela.api process running between launch and approval.
An earlier investor-facing demo (
apps/cockpit-poc/, React/Gemini) has been retired; its worthwhile surfaces — the reasoning-pipeline DAG, the severity verdict hero, the scenario card list — were ported into the console and rebound to the real contracts. The console is now the only UI.
5. ESA-ADB detection benchmark (honest framing)
VELA can score its unsupervised detectors against the real ESA-ADB anomaly labels. This makes detection real and measurable; it does not produce a cited investigation over ESA-ADB — that needs a per-mission knowledge graph, and VELA-REF-1 is a synthetic reference spacecraft.
The dataset is a separate operator download (large; git-ignored; never fetched at runtime):
make data # downloads + checksum-verifies ESA-ADB# or point at a disk with headroom: make data ESA_DATA_DIR=/mnt/big/dataThen run the real-label detection benchmark. At true ESA-ADB scale (~15M points/channel) you must
resample; --targets-only scores just the channels the manifest marks Target=YES:
VELA_ESA_DATA_DIR=data python -m vela.eval detect --mission ESA-Mission1 --resample 1h --targets-only# writes results/bench/detection-ESA-Mission1.json (precision / recall / F0.5, event-wise)The honest caveats — state these; do not overclaim:
- Resolution-limited by design. Resampling to a coarser cadence (e.g.
1h) is required for the pure-Python detectors; anomalies span days so detectability is preserved, but this is not a native-resolution result. Do not claim to beat the ESA-ADB paper — this is a working, honest baseline on downsampled data, not a state-of-the-art submission. - Detection only. The benchmark reports detection metrics it actually computes (F0.5 weights precision because operators drown in false alarms). A cited investigation over ESA-ADB is out of scope. See ADR 0017.
- Attribution required. ESA-ADB is CC BY 3.0 IGO (Zenodo DOI 10.5281/zenodo.12528696). Keep the attribution wherever the data or derived metrics appear.
Separately, make bench runs the pipeline benchmark over the synthetic deck in both modes and
verifies cloud/airgap parity → results/bench/<sha>.json. That is a determinism/parity check, not an
ESA-ADB detection score — don’t conflate the two.
6. Pre-demo checklist
-
uv pip install -e ".[dev]"done;.venvactive or you drive everything throughmake. -
python -m vela.cli scenarioslists 10 scenarios. -
make demoruns clean end to end (this is your reliable fallback — rehearse it). - The three scenarios you’ll show (§2) each run and look right on this machine.
- If doing the live-LLM path: all
VELA_REASONING_*env set; the key/token env is set and fresh (ADC tokens expire ~1h); the endpoint/project is reachable from the demo network. Do a dry run ofmake demo-cloudminutes before, not on stage. - If doing the console:
.[api]installed,python -m vela.apirunning,npm cidone inapps/console, source selector on live service, and the API left running between launch and approval. - Terminal font large;
--replayhandy to show the audit trail.
7. Failure recovery on stage
- Live model errors / times out / auth fails. This is safe — the drafter fails closed to
INSUFFICIENTrather than fabricating. You can narrate that as the safety story, then immediately fall back to the reliable path: drop--mode cloud/ unsetVELA_REASONING, or just runmake demo(deterministic air-gap). Same graph, same cited report, zero dependencies. airgap mode requires reasoning='deterministic'. You leftVELA_REASONINGset while running an air-gap command. Unset it (unset VELA_REASONING) or run the live path with--mode cloud.- Vertex token expired. Re-run
export GOOGLE_TOKEN="$(gcloud auth print-access-token)". httpx/ provider import error. The provider path needshttpx—uv pip install -e ".[dev]"(or addhttpx). The Anthropic path needs.[cloud].- API port already in use. Rebind:
VELA_API_PORT=8010 python -m vela.api(and point the console there).VELA_API_HOSTis available too. - Console shows nothing / backend down. The console’s offline demo mode uses fixtures and needs no backend — leave the source selector on demo to keep presenting while you fix the API.
- Approval “resume” does nothing on the live API. Checkpoints are process-local; don’t restart
python -m vela.apibetween launching an investigation and approving it. - When in doubt, go deterministic.
make demois the demo that always works.
Command reference
# Scenarios & investigationspython -m vela.cli scenariospython -m vela.cli investigate --scenario <KEY> [--mode cloud|airgap] [--detector annotation|ecod|graph] [--approve] [--approver NAME] [--replay] [--out DIR]python -m vela.cli detect --scenario <KEY> [--method ecod|graph] [--mode cloud|airgap]
# Make convenience targetsmake demo # deterministic investigation (reliable)make demo-cloud # live-LLM investigation (needs §3 env)make demo-cloud DEMO_SCENARIO=BATTERY_OVERTEMPmake bench # pipeline benchmark, both modes, parity checkmake data # download the real ESA-ADB dataset (operator step)
# Service + frontendpython -m vela.api # http://127.0.0.1:8000 (docs at /docs)cd apps/console && npm ci && npm run dev # operator console, http://localhost:5173
# Real ESA-ADB detection benchmarkVELA_ESA_DATA_DIR=data python -m vela.eval detect --mission ESA-Mission1 --resample 1h --targets-onlyThe ten scenarios: NOMINAL, CELL_DEGRADATION, STRING_LOSS, WHEEL_FRICTION, HEATER_STUCK_ON,
MEMORY_SEU_STORM, GAIN_DEGRADATION, GYRO_BIAS_DRIFT, BATTERY_OVERTEMP,
SUPPRESSED_MOMENTUM_DUMP.
