Skip to content

cywf/vTOC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

296 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vTOC Platform

CI Preview Deploy

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.

Live Demo

🌐 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.

Branch playbooks

  • main branch guide – documentation-first branch that is only updated through the automated production sync pull request.
  • prod branch guide – the staging ground for release candidates and production hotfixes.
  • live branch guide – mirrors what Terraform Cloud deploys to Fly.io.

Release flow overview

  1. Start feature work from a topic branch that targets prod and run the standard CI (ci.yml) and preview deploy checks.
  2. Merge reviewed changes into prod. Terraform Cloud promotes the release to the hardened live branch which triggers the Fly Deploy workflow.
  3. After the deploy step succeeds, the verify-sync job hits https://vtoc.pr-cybr.com/healthz. A passing probe automatically upserts a prod → main synchronization PR so that the documentation branch stays aligned with production.
  4. Maintainers merge the synchronization PR—direct merges to main are 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.

Collaboration hubs

  • 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/healthz and the frontend renders on http://localhost:5173 (or 8081 when using the containerized stack). ChatKit sandboxes, AgentKit playbooks, and station metadata are stored in .env.local / .env.station files that are generated by the setup script.

Live Codebase Mindmap

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-driven development

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.

Environment variables

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.

ChatKit / AgentKit workflow

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:

  1. ChatKit receives operator prompts or telemetry alerts.
  2. The backend normalizes the message and schedules an AgentKit run matching the POSTGRES_STATION_ROLE.
  3. AgentKit invokes domain-specific tasks (for example, Logistics triggers supply chain lookups, Intelligence scores sensor packets, and Operations coordinates mission overlays).
  4. 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.

Deploy (Terraform Cloud on Fly.io)

The repository keeps two long-lived branches with distinct deployment roles:

  • main retains the generic development defaults used by contributors when iterating locally or in preview environments.
  • stage is the continuously exercised promotion branch fed by Codex-driven automation; it mirrors main, runs the autonomous QA loop, and gates releases bound for live.
  • live is managed by Terraform Cloud, which applies the Fly.io workspace in infrastructure/README-infra.md to 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.

Stage branch QA loop

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.

Stage CI run history

No Stage CI runs have been recorded yet.

Deployment modes

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.

Containers and orchestration

  • 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 the live branch

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 up

When 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-local

setup_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.json

Where 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.

Frontend

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 mode

Backend

The 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 head

Telemetry scraper and AgentKit bridge

agents/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.

Documentation map

Tooling highlights

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.

About

Virtual Tactical Operations Center (vTOC)

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages