vTOC pairs a FastAPI backend, a map-first Vite frontend, and automation agents to deliver a virtual tactical operations center. This README now serves as the global navigation hub—follow the links below to get to the right branch guide, documentation, or collaboration surface quickly.
🌐 https://pr-cybr.github.io/vTOC/demo – Static Next.js demo dashboard showcasing four station command interfaces with mock telemetry, IMEI alerts, and timeline feeds.
mainbranch guide – documentation-first branch that is only updated through the automated production sync pull request.prodbranch guide – the staging ground for release candidates and production hotfixes.livebranch guide – mirrors what Terraform Cloud deploys to Fly.io.
- Start feature work from a topic branch that targets
prodand run the standard CI (ci.yml) and preview deploy checks. - Merge reviewed changes into
prod. Terraform Cloud promotes the release to the hardenedlivebranch which triggers theFly Deployworkflow. - After the deploy step succeeds, the
verify-syncjob hitshttps://vtoc.pr-cybr.com/healthz. A passing probe automatically upserts aprod → mainsynchronization PR so that the documentation branch stays aligned with production. - Maintainers merge the synchronization PR—direct merges to
mainare blocked, enforcing the “only merge from prod” policy.
If anything fails along the way, investigate the workflow run, fix the branch that failed (usually prod or live), and let the
automation retry. The manual Configure Main Branch Protection workflow keeps the
protection rules reproducible by running scripts/automation/protect-main-branch.sh
with the GitHub CLI.
New stations should complete the hardware-focused checklist in docs/SETUP.md before powering on
equipment. Component playbooks for the ADS-B receiver, GPS module, and H4M tablets live in docs/HARDWARE.md
with deep dives in docs/ADSB.md, docs/GPS.md, and docs/H4M.md. Low-power
stations built on Raspberry Pi 5 hardware can follow the dedicated
docs/deployment/raspberry-pi.md profile to keep the board within mission-ready resource
limits.
- Discussions – async planning, release notes, and incident follow-ups.
- Projects – roadmap boards and sprint tracking.
- Wiki – reserved for curated runbooks and operations guides (coming soon).
During development the backend exposes
http://localhost:8080/healthzand the frontend renders onhttp://localhost:5173(or8081when using the containerized stack). ChatKit sandboxes, AgentKit playbooks, and station metadata are stored in.env.local/.env.stationfiles that are generated by the setup script.
Auto-generated on each push: repo-map.html (via GitHub Pages and CI artifact).
When Pages is enabled, it will be served at: https://pr-cybr.github.io/vTOC/repo-map.html
Spec Kit threads the new discovery workflow through a set of /speckit.* slash commands that are available in ChatKit
channels and Codex conversations. Each command writes structured artifacts into specs/<feature>/ so backend and frontend
teams can iterate from a shared source of truth.
| Command | When to run it | Outputs | Workstream alignment |
|---|---|---|---|
/speckit.init <feature-name> |
Immediately after a backlog item is triaged. Initializes folders and seed templates. | specs/<feature>/README.md, specs/<feature>/context.yaml |
Creates a neutral workspace so both web and API owners capture constraints before diving into solutions. |
/speckit.spec <feature-name> |
When product discovery starts. Prompts for user stories, acceptance criteria, and UX notes. | specs/<feature>/spec.md |
Frontend teams use the UX/behavior checklist, backend teams rely on data/API expectations in the same file. |
/speckit.plan <feature-name> |
After the spec is approved. Breaks work into milestones and outlines integration points. | specs/<feature>/plan.md, specs/<feature>/qa-checklist.md |
Backend sections include service contracts and migration steps; frontend sections track component states and analytics hooks. |
/speckit.tasks <feature-name> |
Once the plan is ready to staff. Generates task stubs and backlog mutations. | specs/<feature>/tasks/*.md, updates to backlog/backlog.yaml |
Backend cards inherit API/schema subtasks, frontend cards inherit UI/state subtasks, and a shared “integration” card stitches both streams together. |
/speckit.sync <feature-name> |
Whenever artifacts change materially. Pushes updates to Codex and GitHub Projects. | Refreshes spec metadata, updates linked project items | Keeps delivery dashboards and deployment automation aligned across teams. |
Generated files are colocated with their feature folder so reviewers can diff discovery artifacts alongside code. Plans and QA
checklists are read by CI automation to decide which pipelines (backend pytest, frontend vitest, infrastructure smoke tests)
must pass before a pull request is merged. When /speckit.sync runs it also updates release-train notes under
docs/workflows/ and rehydrates Codex prompts so the assistant can answer follow-up questions with the latest spec context.
| Variable | Scope | Description |
|---|---|---|
DATABASE_URL |
Backend | SQLAlchemy connection string for the station database. Populated by setup for each station role. |
SUPABASE_URL |
Backend, Frontend | Base URL for the Supabase project that now hosts the managed Postgres cluster and auth API. |
SUPABASE_ANON_KEY |
Frontend | Public Supabase key used by the web client for session bootstrap and row-level security policies. |
SUPABASE_SERVICE_ROLE_KEY |
Backend, Agents | Supabase service-role key used for privileged database access and webhook verification. |
POSTGRES_STATION_ROLE |
All services | Role identifier (ops, intel, logistics) used to bind ChatKit channels to the correct AgentKit playbooks. |
POSTGRES_POOL_SIZE |
Backend | Optional override for multi-station connection pooling. |
CHATKIT_API_KEY |
Backend, Agents | API token for ChatKit orchestration. |
CHATKIT_ORG_ID |
Backend, Agents | Organization identifier required to join shared channels. |
AGENTKIT_CLIENT_ID / AGENTKIT_CLIENT_SECRET |
Agents | OAuth credentials for AgentKit workflow execution. |
STATION_CALLSIGN |
Frontend, Agents | Friendly identifier rendered in the UI header and propagated to telemetry events. |
TELEMETRY_BROADCAST_URL |
Agents | Optional WebSocket endpoint for streaming telemetry into ChatKit threads. |
See docs/QUICKSTART.md for role-specific bootstrap instructions and
docs/DEPLOYMENT.md for production notes. Secret sourcing and rotation live in
docs/secret-management.md.
Each station is bootstrapped with a ChatKit channel named after the STATION_CALLSIGN. The backend exposes /api/v1/chatkit
webhooks that translate channel events into AgentKit runs. AgentKit playbooks dispatch telemetry connectors and update the
mission log while respecting station roles:
- ChatKit receives operator prompts or telemetry alerts.
- The backend normalizes the message and schedules an AgentKit run matching the
POSTGRES_STATION_ROLE. - AgentKit invokes domain-specific tasks (for example, Logistics triggers supply chain lookups, Intelligence scores sensor packets, and Operations coordinates mission overlays).
- Results are written back to ChatKit and persisted through the telemetry subsystem.
An end-to-end overview is available in docs/ARCHITECTURE.md together with updated diagrams in
docs/DIAGRAMS.md.
The repository keeps two long-lived branches with distinct deployment roles:
mainretains the generic development defaults used by contributors when iterating locally or in preview environments.stageis the continuously exercised promotion branch fed by Codex-driven automation; it mirrorsmain, runs the autonomous QA loop, and gates releases bound forlive.liveis managed by Terraform Cloud, which applies the Fly.io workspace ininfrastructure/README-infra.mdto promote approved container images and environment configuration.
The Terraform Cloud workflow handles Fly.io secrets, release rollbacks, and keeps the live branch fast-forwarded to the exact commit running in production. Operators can inspect the job history, trigger re-runs, or apply manual overrides directly from Terraform Cloud using the linked infrastructure guide.
Codex automation promotes green builds from main into stage, ensuring every change exercises the same workflows used for production without touching the live infrastructure state. The Stage CI pipeline fans out to the reusable test suite and container build workflows, tagging published images with stage when the branch is updated. A follow-on Stage CI Report job captures the latest run metadata through the GitHub REST API and persists the summary below so downstream operators can confirm the autonomous loop is healthy.
Branch protection on stage requires the Stage CI check to succeed before Codex (or a human operator) can land changes, keeping the promotion gate aligned with the automated QA signal.
No Stage CI runs have been recorded yet.
| Mode | Command | Description |
|---|---|---|
| Local dev | python -m scripts.bootstrap_cli setup local (or make setup-local) |
Installs dependencies, writes .env.local, and provisions ChatKit/AgentKit sandbox credentials. |
| Generated Compose | python -m scripts.bootstrap_cli setup container --apply (or make setup-container) |
Produces docker-compose.generated.yml, interpolates station secrets, and starts the stack with role-specific services. |
| Infrastructure | python -m scripts.bootstrap_cli setup cloud (or make setup-cloud) |
Generates Terraform/Ansible scaffolding in infra/ with multi-station Postgres plans. |
| Cleanup | python -m scripts.bootstrap_cli compose down (or make compose-down) |
Tears down services started from the generated compose file and removes role-specific temp volumes. |
Supply configuration through --config path.json or --config-json '{...}'. The schema now includes stationRoles[],
chatkit/agentkit sections, and the optional configBundle override used by the container generator – see
scripts/inputs.schema.json for details.
Codex CLI is detected automatically: if the codex binary exists, the setup scripts use codex interpolate for variable
expansion; otherwise they fall back to portable Bash implementations. Commits that land on main now trigger the
Main discussion summary workflow which renders a release note style update in
GitHub Discussions using Codex. Configure the CODEX_API_KEY and DOCS_DISCUSSION_CATEGORY_ID secrets/variables in the
repository so the automation can authenticate and target the correct discussion category.
docker-compose.yml— developer stack (Postgres, FastAPI backend, Vite frontend via nginx, telemetry scraper)docker-stack.yml— Docker Swarm with Traefik routing (vtoc.local/api.vtoc.local) and optional integrations (ZeroTier, Tailscale, MediaMTX, TAK Server)fly.toml— Fly.io deployment descriptor for the backend container deployed from thelivebranch
Images are built and pushed to GHCR via GitHub Actions through the Stage CI workflow (tagged as stage) and the release-focused Publish Containers workflow:
ghcr.io/<repo>/frontend(Vite static bundle served by nginx)ghcr.io/<repo>/backend(FastAPI + Uvicorn on port 8080)ghcr.io/<repo>/scraper(RSS/HTML telemetry agent)
docker-compose.yml references these images directly so docker compose up pulls from GHCR by default. Override the registry or
tag without editing the file by exporting environment variables, for example:
VTOC_IMAGE_TAG=main docker compose up
# or pin a preview build
VTOC_IMAGE_REPO=ghcr.io/myfork/vtoc VTOC_IMAGE_TAG=pr-123 docker compose upWhen running docker compose up directly, make sure the ChatKit, AgentKit, and Supabase credentials expected by the backend and frontend are present in your shell (or a .env file). The compose file reads the following variables:
- Backend:
AGENTKIT_API_BASE_URL,AGENTKIT_API_KEY,AGENTKIT_ORG_ID,AGENTKIT_TIMEOUT_SECONDS,CHATKIT_WEBHOOK_SECRET,CHATKIT_ALLOWED_TOOLS,CHATKIT_API_KEY,CHATKIT_ORG_ID,SUPABASE_URL,SUPABASE_PROJECT_REF,SUPABASE_SERVICE_ROLE_KEY,SUPABASE_JWT_SECRET,SUPABASE_ANON_KEY - Frontend:
VITE_CHATKIT_WIDGET_URL,VITE_CHATKIT_API_KEY,VITE_CHATKIT_TELEMETRY_CHANNEL,VITE_AGENTKIT_ORG_ID,VITE_AGENTKIT_DEFAULT_STATION_CONTEXT,VITE_AGENTKIT_API_BASE_PATH,VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY
Populate these from your Terraform outputs, config bundle override, or the bootstrap helper (python -m scripts.bootstrap_cli setup container --config ...).
To build locally, call the container setup helper with --build-local so the generated compose file includes build: blocks:
./scripts/setup_container.sh --build-localsetup_container.sh now prefers a configBundle override supplied through VTOC_CONFIG_JSON/--config before touching Terraform. When no override exists the script attempts to read the config_bundle output and falls back to scripts/defaults/config_bundle.local.json if Terraform is unavailable. The fallback mirrors the development defaults so --build-local continues to work without additional secrets.
Teams that need to inject real credentials without Terraform can copy the fallback file, replace placeholder values, and pass it through the forwarded config:
python -m scripts.bootstrap_cli setup container --config path/to/override.jsonWhere override.json contains a configBundle object (see scripts/examples/container.json).
You can also select a published image tag during generation with --image-tag <tag> (equivalent to VTOC_IMAGE_TAG).
A Fly.io dispatch workflow (fly-deploy.yml) deploys the backend using the prebuilt image when live receives new commits or
when tags matching v* are pushed. Operators promoting builds manually can use scripts/fly_deploy.sh
to export the required GitHub Container Registry credentials and run flyctl deploy --remote-only with the pinned backend
image.
The Vite/React frontend consumes the backend via frontend/src/services/api.ts, renders station overlays, and includes a
ChatKit mission console sidebar. Environment configuration lives in frontend/.env.example and is
propagated through the setup scripts. Station role badges and callsigns are hydrated from the generated .env.station file.
Key scripts:
pnpm --dir frontend dev # local dev server on 5173
pnpm --dir frontend build # generate production bundle
pnpm --dir frontend test # run vitest suite in CI modeThe FastAPI backend (backend/app) provides:
/healthz— health probe/api/v1/telemetry/*— CRUD for telemetry sources and events/api/v1/chatkit/webhook— Receives ChatKit events and launches AgentKit runs scoped to the station role/api/v1/stations— Registers and reports station metadata/api/v1/stations/{station_slug}/timeline— Returns a merged, paginated timeline of telemetry events and AgentKit audits
Database connectivity is supplied through DATABASE_URL. SQLAlchemy models and Alembic migrations live under backend/app and
alembic/. Run migrations with:
alembic upgrade headagents/scraper reads feeds from config.yaml and posts normalized telemetry to the backend. AgentKit playbooks can invoke the
same connectors via the shared configuration module, enabling ChatKit-driven automations. Run locally with
python -m scripts.bootstrap_cli scraper run (or make scraper-run) or containerize via the provided Dockerfile.
- Quick start – developer onboarding and local setup.
- Architecture & Diagrams – system overview and reference visuals.
- Hardware setup & Station checklist – on-premise station guidance.
- Deployment guide & infrastructure notes – Fly.io and Terraform Cloud automation details.
- Service READMEs – per-service instructions across ADS-B, GPS, H4M bridge, and more.
scripts/bootstrap_cli– orchestrates local, containerized, and cloud bootstrap flows.scripts/fly_deploy.sh– manual helper that mirrors the automated Fly.io deployment.scripts/automation– automation entry points, including backlog management and branch protection.backend/&frontend/– primary application codebases (FastAPI + Vite/React).
Have an improvement in mind? Open a discussion first, then drive a PR through the prod branch so the release automation can
close the loop.