Vectasec · Get started
Deployment
How to run the Vectasec engine, worker and console against Supabase, where the Collector fits, and the status of the self-hosted preview.
On this page
A Vectasec deployment has three processes: the engine API, one or more queue workers and the console. All three share one Postgres control plane on Supabase. This page covers how to run them, what the database needs, and what to set before anything is reachable from outside your machine.
Topologies#
- Hosted control plane (default). A Supabase project provides Postgres, Auth, Vault,
pgmq,pg_cronand Realtime, and you run the engine API, workers and console beside it. Workers scan cloud-mode connections directly, and the engine runs connection tests the same way, so both must be able to reach each cloud-mode target. - Collector. A process inside your network that runs the same connectors and detectors, connects outbound only and keeps credentials local. It pushes findings, their observations and resource metadata. Credentials and raw resource configuration stay in your network. The console and API cannot mint enrollment tokens yet. See Run a collector in your network.
- Self-hosted stack. The self-hosted preview bundle (Docker Compose and a Helm chart). This is a preview, described at the end of this page.
Run the services#
Run the Python processes from the engine directory of the release. Each one loads the nearest .env at or above its working directory. Variables already set in the environment take precedence.
Engine API#
Start the engine API with its bundled entry point from the engine directory of the release (it reads .env, ENGINE_HOST and ENGINE_PORT); don't start it with bare uvicorn, which skips the .env load. The engine binds 127.0.0.1:8000 by default (ENGINE_HOST, ENGINE_PORT). Set ENGINE_HOST=0.0.0.0 only inside a container or a private network, because the engine holds a database connection that bypasses row-level security. Set SUPABASE_URL so the engine can verify user JWTs.
The console calls the engine from its server side, never from the browser. Only the console and, if you run them, collectors need to reach it.
Worker#
uv run vectasec worker
The worker drains the scan queue (a pgmq queue), claiming up to 5 messages per read with a 600-second visibility timeout. You can run several workers, because a claimed message stays hidden from the others until it is archived or the timeout passes. If a worker dies mid-scan, the message reappears and the scan reruns. Findings dedupe on a fingerprint, so a rerun is safe.
A failed scan is retried until its message has been read 5 times, then archived as poison and sent to your scan-failed alert rules. Rejected credentials and configuration errors are archived after the first attempt, because no retry can fix them. Each loop also marks scans that have been running for more than an hour as error.
--oncedrains what is queued, then exits.--reap-onlymarks stuck scans, then exits without touching the queue.
Console#
From the console directory of the release:
pnpm install
pnpm build
pnpm start
The console is a Next.js 16 app. It reads NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, VECTASEC_ENGINE_URL (default http://127.0.0.1:8000) and VECTASEC_ENGINE_TOKEN, plus RESEND_API_KEY and EMAIL_FROM if it sends mail. Put them in the console's .env.local, because Next.js reads env files from the app directory, not the repository root. The NEXT_PUBLIC_* values are inlined into the browser bundle at build time, so rebuild when they change.
Scheduler#
Scheduled scans need no extra process. A scheduled pg_cron job runs every minute. It queues active connections whose scan_interval_minutes has elapsed and that have no scan already queued or running. You set the interval in the console or with PATCH /connections/{id}: 5 to 10080 minutes, or null to unschedule. A cloud-mode connection gets a scan record and a queue message for a worker. A collector-mode connection gets only the scan record, which its collector claims.
Database#
Apply every database migration shipped with the release, in order. There are 16 today, written to be idempotent, so you can reapply the full set.
The migrations create pgcrypto, pgmq and pg_cron. supabase_vault must already be installed, as it is on Supabase, because the engine stores connection credentials in Vault. The scheduler migration registers its job only when the cron schema exists. Without it, no scheduled scans run.
- The engine and workers connect with
DATABASE_URLas a role that bypasses RLS, and every engine query filters by tenant itself. Treat this URL as the most sensitive value in the deployment. - The console reads as
authenticated, which has SELECT-only policies scoped to the user's tenants. Tenant tables use FORCE row-level security.
Health checks#
curl -s http://127.0.0.1:8000/health
/health always returns HTTP 200 with {status, db, detectors, connectors}. db holds the real database state, and status reads degraded when it is false. Use the endpoint for liveness, and alert on db: false, not on the status code.
uv run vectasec doctor also checks the extensions and the scan queue: messages waiting, scans queued and scans running. A growing queue with nothing running means no worker is draining it. doctor exits non-zero when it finds a problem.
Production checklist#
Set these before anyone but you can reach the engine or the console.
Then:
- Keep the engine off the public internet, because it holds a privileged database connection. If collectors connect from other networks, expose only the
/collector/*routes to them, over TLS. - Run
vectasec doctor --verify-ledgeron a schedule. It recomputes the evidence hash chain, prints the first 20 breaks with a count of any others, and exits non-zero if it finds one. It reads every hashed observation, so run it off-peak on a large estate.
Self-hosted stack (preview)#
The self-hosted preview bundle contains:
- A Docker Compose file with Supabase Postgres, GoTrue, PostgREST, Realtime, the engine, the console and Caddy. Caddy terminates TLS, serves everything from one origin and routes
/api/*to the engine. A one-shotmigrateservice applies the migrations on everyup. - Dockerfiles for the engine and the console.
- A Helm chart for the engine, the console and an optional worker. It does not deploy Postgres or the Supabase services, and it does not apply migrations.
The compose file predates the engine's verified-token auth. Before you try it:
- Apply the production checklist above.
- Add
VECTASEC_ENGINE_TOKENto theengineandwebservices, and setVECTASEC_ENGINE_URL=http://engine:8000onweb. The chart sets neither, so on Kubernetes add both to the engine and web Deployments, withVECTASEC_ENGINE_URLpointing at the engine Service. - Set
VECTASEC_COLLECTOR_TOKEN_SECRETon the engine. - Narrow
VECTASEC_ENGINE_ALLOWED_CIDRS, the source ranges Caddy allows on/api/*, to your operator subnet. On Kubernetes, add a source-range restriction to the/apiingress path, because the chart sets none. The chart also does not strip the/apiprefix, and the engine serves its routes at the root, so add your ingress controller's rewrite. - Add a queue worker. The compose
workerprofile and the chart's worker run the collector image, notvectasec worker. The engine image includes thevectasecCLI, so a service built from it, with the engine's environment, can runvectasec worker.
Also keep in mind:
- Engine user-JWT verification accepts only ES256 or RS256 keys from the Auth JWKS endpoint. The bundled GoTrue signs HS256, so rely on the engine-token path, which the console already uses.
NEXT_PUBLIC_*values are baked in at build time, so build the console image per hostname.- For an air-gapped runtime, leave
ANTHROPIC_API_KEY,OPENAI_API_KEYandGEMINI_API_KEYblank so triage writes its deterministic summary, and setVECTASEC_OSV_DISABLED=1andVECTASEC_NVD_DISABLED=1wherever scans run: the engine and every worker. The compose file does not pass those two switches through to the engine, so add them to its environment. Without them, a scan that finds components to check records a not-assessable coverage-gap finding for each feed it cannot reach. Building the images needs network access once.
Next steps#
- Security model: authentication paths, roles and the guarantees Postgres enforces
- Run a collector in your network: scan private systems without sending credentials out
- Configuration: environment variables for the engine, worker, collector and console