Skip to content

Development

gladsonsam edited this page Jun 20, 2026 · 5 revisions

Development

Local development guide for Vantyr. The stack is a Rust/Axum/Tokio/SQLx-PostgreSQL server (the only root Cargo workspace member), a Windows/Linux Rust agent with a Tauri React settings UI, and a React 19 + TypeScript + Vite dashboard.

See also: Deployment · Configuration · Environment-template · Usage · Security


Prerequisites

Tool Version For
Rust toolchain stable (rustup) server + agent
Node.js 20 dashboard (frontend/) and agent settings UI (agent/ui-src/)
PostgreSQL 16 server database

For the quickest look at the UI, no Rust or PostgreSQL is needed — run the dashboard in demo mode (below).


Project layout

.
├─ Cargo.toml            # root workspace — only member is `server` (vantyr-server)
├─ docker-compose.yml    # server + postgres for production / integration
├─ .env.example          # full commented config template
├─ server/               # Rust/Axum API + serves the built dashboard
│  ├─ src/               # main.rs (router/middleware), api/, ws_*.rs, db.rs, auth.rs …
│  └─ migrations/        # 55+ SQL migrations, run at startup (embedded)
├─ frontend/             # React 19 + TS + Vite dashboard (built into static files)
└─ agent/                # Rust endpoint agent (EXCLUDED from the root workspace)
   └─ ui-src/            # Tauri React settings UI

The root package.json provides convenience shortcuts: npm run dev (dashboard), npm run demo (dashboard demo mode), npm run agent:dev|build|ui.


Server (Rust)

# from the repo root
cargo run -p vantyr-server      # or: cd server && cargo run
  • Reads config from the environment via ServerConfig::from_env (server/src/config.rs, fail-fast validated). The simplest path is to copy .env.example to .env and export it, or set DATABASE_URL and ADMIN_PASSWORD inline.
  • DATABASE_URL must be postgres://… / postgresql://…. Default is postgres://monitor:monitor@localhost:5432/monitor. Bring up just Postgres with docker compose up -d db if you don't have one locally.
  • Migrations from server/migrations/ run automatically on startup.
  • Listens on LISTEN_ADDR (default 0.0.0.0:9000).

Common commands (run from the repo root):

cargo fmt --all
cargo check -p vantyr-server
cargo clippy -p vantyr-server --all-targets
cargo test  -p vantyr-server

Dashboard (React + Vite)

cd frontend
npm install
npm run dev          # Vite dev server, proxies /api and /ws to localhost:9000

vite.config.ts proxies /api and /ws to http://localhost:9000, so run the server alongside it (no CORS config needed).

Demo mode (no backend)

cd frontend
npm run dev:demo     # http://localhost:5173 — runs entirely on mock data

dev:demo runs Vite with --mode demo on port 5173 and needs no backend or database — the fastest way to see the UI. (npm run demo from the repo root does the same thing.)

Other frontend scripts:

Command Does
npm run build tsc -b && vite build — type-check + production build into dist/
npm run build:demo demo-mode production build
npm run lint / npm run lint:fix ESLint
npm run test Vitest (very few tests exist today)
npm run preview preview the production build

Agent (Rust + Tauri)

The agent lives in agent/ and is excluded from the root Cargo workspace — always build it from its own manifest, not from the repo root. The Tauri settings UI (agent/ui-src/) must be built first because tauri-build expects its dist/ to exist.

Windows (native)

npm --prefix agent/ui-src ci          # once
npm --prefix agent/ui-src run build   # build the settings UI

cargo fmt   --manifest-path agent/Cargo.toml --all
cargo check --manifest-path agent/Cargo.toml
cargo build --manifest-path agent/Cargo.toml   # or: cd agent && cargo build

Many agent modules are #[cfg(target_os = "windows")] (the dual-process Session-0 service + user-session companion model). Ctrl+Shift+F12 opens the settings webview; --no-ui / AGENT_NO_UI runs headless.

Linux

The agent also builds on Linux (headless direct-WS runtime). The desktop build needs X11/XCB, dbus, PipeWire, Wayland, xkbcommon and EGL dev packages — see the agent-linux job in .github/workflows/ci.yml for the exact apt-get install list. Then:

cargo check --manifest-path agent/Cargo.toml

Cross-compiling Windows release MSIs

Release MSIs are produced with cargo xwin + WiX (agent/wix/) in .github/workflows/release-agent-windows.yml. cargo-xwin lets you target x86_64-pc-windows-msvc from Linux without a Windows host. Updater artifacts are signed with minisign (TAURI_SIGNING_PRIVATE_KEY); the MSI itself is not Authenticode-signed, so SmartScreen shows "Unknown Publisher".


CI

.github/workflows/ci.yml runs on pull requests and pushes to main/dev:

Job Runner Steps
server (rust) ubuntu cargo fmt --all --check, cargo clippy -p vantyr-server, cargo check -p vantyr-server, cargo test -p vantyr-server
frontend (react) ubuntu npm ci, npm run lint*, npm run build (type-check + build)
agent (windows) windows build agent/ui-src, cargo fmt*, cargo check --manifest-path agent/Cargo.toml
agent (linux) ubuntu install desktop build deps, cargo fmt*, cargo check --manifest-path agent/Cargo.toml

* fmt / clippy / lint steps are continue-on-error: true (advisory) until the tree is clean. The check/test/build steps are hard gates.

Release workflows (tag v* only): release-server-docker.yml (builds/pushes the GHCR server image), release-agent-windows.yml and release-agent-linux.yml (agent installers).


HTTP API and WebSocket overview

Routes are assembled in server/src/main.rs; the authoritative dashboard route list is server/src/api/mod.rs. There is no /api/v1 prefix and no OpenAPI spec — action endpoints are mostly POST .../verb.

Unauthenticated / special-case HTTP

Path Purpose
GET /healthz Liveness (always allowed, even with ENFORCE_HTTPS=true).
GET /readyz Readiness (includes DB check).
GET /metrics Prometheus metrics when METRICS_ENABLED=true.
POST /api/login · POST /api/logout Dashboard session; login returns { csrf_token } and sets the HttpOnly session cookie.
GET /api/auth/status · /api/auth/config Session + OIDC discovery.
GET /api/auth/oidc/login · /api/auth/oidc/callback OIDC SSO.
POST /api/agent/enroll Exchange an enrollment code for a per-device agent token (per-IP rate-limited via GovernorLayer).
GET /api/integration/agents/live Optional JSON feed when INTEGRATION_API_TOKEN is set (Authorization: Bearer …).

Authenticated REST (/api/*)

Nested under /api behind a session cookie, with a per-session CSRF token required on POST/PUT/PATCH/DELETE (header X-CSRF-Token: <csrf_token>). Major groups: agents & telemetry (/api/agents, …/info|windows|keys|urls|activity|screen|mjpeg|logs|analytics), rules & policy (/api/alert-rules, /api/agent-groups, /api/app-block-rules, /api/internet-block-rules, /api/scheduled-scripts), users & audit (/api/users, /api/audit), and settings (/api/settings/… — retention, enrollment tokens, storage, capabilities, URL categorization, 2FA).

WebSocket channels

Path Purpose
GET /ws/agent Endpoint agent connection — bearer-auth'd inside the handler. Carries telemetry up (keys, window/URL focus, AFK, software, MJPEG frames) and commands down (capture, policy, run-script, power).
GET /ws/view Dashboard viewer stream (requires a session when auth is enforced). Only operator/admin users may send agent-control commands; viewer users receive telemetry only.

A third WebSocket, GET /ws/terminal, exists in the auth-gated nest for the interactive remote-terminal feature.


Troubleshooting

Symptom Check
HTTP 426 / "HTTPS required" Set ENFORCE_HTTPS=false for direct HTTP, or fix the proxy so it sends X-Forwarded-Proto: https (and trust it via TRUSTED_PROXY_CIDRS).
Login fails on plain HTTP COOKIE_SECURE=true breaks cookies when the browser isn't using HTTPS.
/api calls 404 from the dev server Run vantyr-server on :9000 so Vite's proxy (/api, /ws) has a target — or use npm run dev:demo, which needs no backend.
"ADMIN_PASSWORD not configured" / can't log in Set ADMIN_PASSWORD (or the legacy UI_PASSWORD) for first-user bootstrap.
Database errors on startup Verify DATABASE_URL, that Postgres is running, and credentials/firewall. docker compose up -d db gives you a local one.
Migration checksum errors Don't edit applied migrations. Follow the message from the server (rebuild from the same files / fix _sqlx_migrations).
Agent won't cargo check from the root The agent is excluded from the workspace — use --manifest-path agent/Cargo.toml, and build agent/ui-src first.
Tauri build fails "frontend dist not found" Run npm --prefix agent/ui-src run build before cargo build in agent/.
Dashboard tokens/colors render black index.css must be imported in main.tsx; the app is effectively dark-only (no light token block).

License

MIT — see the LICENSE file in the repository.

Home

Install and configure

Day to day

Integrations

Developers and security

Clone this wiki locally