Server Configuration
The server is configured via server.yaml. This page documents every section and option.
To see a fully annotated reference with all options, defaults, and descriptions:
vigocli server server.yaml full # full reference
vigocli server server.yaml list # list all sections
vigocli server server.yaml list smtp # show one section
Configuration File Location
The server looks for server.yaml in the current working directory. Override with:
VIGO_CONFIG_FILE=/path/to/server.yaml vigosrv
Sections
secrets
Determines how secret: references in config values are resolved.
secrets:
backend: local # local (default) | isopass
key_file: "/srv/vigo/master.key" # 32-byte hex master key (local only)
secrets_dir: "/srv/vigo/secrets" # encrypted files directory (local only)
| Option | Default | Description |
|---|---|---|
backend |
local |
Secrets backend: local (AES-256-GCM encrypted files) or isopass (external API) |
key_file |
auto-generated | Path to 32-byte hex master key file (local backend, mode 0600) |
secrets_dir |
/srv/vigo/secrets |
Directory for encrypted secret files (local backend) |
url |
— | Isopass API base URL (isopass backend only) |
token_file |
— | File containing bearer token (isopass backend, required, mode 0600) |
tls_skip_verify |
false |
Skip TLS verification for isopass (not recommended) |
The local backend auto-generates the master key on first start if key_file doesn't exist.
server
Listener addresses and TLS configuration.
server:
grpc_listen: ":1530"
api_listen: ":8443"
hostname: "myserver.example.com" # server's enrolled envoy hostname
tls:
cert_file: "tls/cert"
key_file: "tls/key"
ca_file: "tls/ca"
ca_key_file: "tls/ca_key"
tls_sans:
- "192.168.1.2"
- "vigo.example.com"
| Option | Default | Description |
|---|---|---|
grpc_listen |
:1530 |
gRPC listen address (agent communication, mTLS) |
api_listen |
:8443 |
REST API + web UI + metrics listen address |
| hostname | — | Server's enrolled envoy hostname. Used by the compliance page to show the server's own standards compliance separately from the fleet aggregate. Required when running in Docker (where os.Hostname() returns the container ID). |
| tls.cert_file | — | Server TLS certificate path |
| tls.key_file | — | Server TLS private key path |
| tls.ca_file | — | CA certificate for mTLS verification |
| tls.ca_key_file | — | CA private key — enables bootstrap CSR signing |
| tls_sans | [] | Extra Subject Alternative Names (IPs or DNS names) for auto-generated certs |
TLS certificates are auto-generated on first start when using the local secrets backend. The auto-generated cert includes localhost, 127.0.0.1, the machine hostname, and all non-loopback interface IPs as SANs. When running in Docker, the container won't see the host's LAN IP — use tls_sans to add it so remote agents can verify the server certificate.
database
database:
dsn: "secret:vigo/db/dsn"
driver: sqlite
retention: "90d"
| Option | Default | Description |
|---|---|---|
dsn |
— | SQLite file path or Postgres connection string (supports secret: prefix) |
driver |
sqlite |
Database driver: sqlite or postgres |
retention |
— | Auto-prune runs/tasks/workflows older than this (e.g., 90d, 2160h). Empty = no pruning |
checkin
checkin:
interval: "5m"
jitter_percent: 20
bundle_max_age: "24h"
observe_only: false
| Option | Default | Description |
|---|---|---|
interval |
5m |
Agent check-in frequency. Stale threshold = 2x this value |
jitter_percent |
20 |
Randomize check-in timing by 0–N% to avoid thundering herd |
bundle_max_age |
24h |
How long signed policy bundles remain valid on agents. 0 = forever |
observe_only |
false |
Global observe-only mode. All envoys report drift only — no changes applied. ORed with per-entry observe_only in node configs |
bootstrap
bootstrap:
cert_validity: "8760h"
trusted_enrollment:
- pattern: "*"
cidrs: ["192.168.0.0/16", "10.0.0.0/8"]
| Option | Default | Description |
|---|---|---|
cert_validity |
8760h (1 year) |
Signed agent certificate lifetime |
trusted_enrollment |
— | List of patterns for token-free enrollment |
trusted_enrollment[].pattern |
— | Hostname glob pattern |
trusted_enrollment[].cidrs |
— | Source CIDR allowlist |
auth
auth:
method: basic
| Option | Default | Description |
|---|---|---|
method |
basic |
Authentication method: basic, oidc, isowebauth |
session_idle_timeout |
15m |
Session idle timeout (HIPAA compliant default) |
oidc.issuer |
— | OIDC provider URL (required for oidc) |
oidc.client_id |
— | OIDC client ID (required for oidc) |
oidc.client_secret |
— | OIDC client secret (supports secret: prefix) |
oidc.redirect_url |
— | OAuth callback URL (e.g., http://vigo:8443/auth/callback) |
oidc.scopes |
[openid, profile, email] |
OIDC scopes to request |
isowebauth.namespace |
vigo |
Namespace for SSH signature verification |
Basic auth stores user accounts in the database and bcrypt password hashes in the secrets provider at vigo/web/auth/<username>. An admin account is created automatically on first startup — activate it with vigocli secrets set web vigo/web/auth/admin.
See Authentication for setup details.
watcher
watcher:
enabled: true
poll_interval: "5m"
| Option | Default | Description |
|---|---|---|
enabled |
true |
Enable secret rotation polling |
poll_interval |
5m |
How often to check for rotated secrets |
Only active when the secrets backend supports versioning (isopass). Disabled on spoke servers.
spanner
spanner:
role: standalone
| Option | Default | Description |
|---|---|---|
role |
standalone |
Server role: standalone, hub, or spoke |
spoke_id |
— | Unique spoke identifier (required for spoke mode) |
hub_addr |
— | Hub gRPC address (spoke→hub status reporting) |
hub_fallback_addrs |
— | Spoke-only: fallback hub addresses for failover |
spokes |
— | Hub-only: list of spoke definitions |
spokes[].id |
— | Unique spoke identifier |
spokes[].addr |
— | Spoke gRPC address (host:port) |
spokes[].patterns |
— | Hostname globs routed to this spoke |
auto_failover.enabled |
false |
Hub-only: auto-drain unhealthy spokes |
auto_failover.threshold |
3 |
Consecutive failures before drain |
auto_failover.interval |
30s |
Health check interval |
Spanner requires a paid license — the server will refuse to start in hub or spoke mode on the built-in free license. See Spanner for details.
tuning
All fields are optional with sensible defaults.
tuning:
signature_window: "5m"
last_seen_flush: "10s"
traits_prune_interval: "5m"
metrics_interval: "30s"
traits_scanner_ttl: "30s"
run_store: "database"
run_store_capacity: 20
max_concurrent_streams: 5000
webhook:
timeout: "10s"
max_retries: 3
max_concurrent: 20
retry_delay: "5s"
| Option | Default | Description |
|---|---|---|
signature_window |
5m |
Max age of signed agent requests |
last_seen_flush |
10s |
FleetIndex dirty-set flush interval to SQLite |
traits_prune_interval |
5m |
Stale trait cleanup frequency |
metrics_interval |
30s |
Prometheus metrics collection interval |
traits_scanner_ttl |
30s |
Custom trait collector cache TTL |
run_store |
database |
Run history backend: database or memory |
run_store_capacity |
20 |
Per-envoy run depth in memory mode |
max_concurrent_streams |
5000 |
gRPC bidirectional stream limit |
See Tuning for detailed guidance.
smtp
smtp:
host: "smtp.example.com"
port: 587
from: "vigo@example.com"
recipients: ["admin@example.com"]
| Option | Default | Description |
|---|---|---|
host |
— | SMTP relay hostname. Empty = notifications disabled |
port |
587 |
SMTP port |
from |
— | Sender email address |
username |
— | SMTP auth username |
password |
— | SMTP auth password (supports secret: prefix) |
tls |
true |
Use STARTTLS |
recipients |
— | Default recipient list |
events |
all enabled | Per-event overrides (see below) |
digest.interval |
— | Batch window for digest summaries (e.g., 1h) |
Available events: envoy.enrolled, run.success, run.failure, drift.detected, secret.rotated, config.reload.failure, envoy.stale, convergence.threshold.
See Email Notifications for details.
ai
ai:
enabled: true
provider: "ollama"
model: "qwen2.5:7b"
| Option | Default | Description |
|---|---|---|
enabled |
false |
Enable AI assistant |
provider |
ollama |
LLM provider: ollama, claude, openai, openai-compat |
model |
provider-specific | Model name (ollama: qwen2.5:7b, claude: claude-sonnet-4-20250514, openai: gpt-4o) |
max_tokens |
4096 |
Max tokens per response |
base_url |
auto for ollama | API base URL (required for openai-compat) |
api_key |
— | API key for paid providers (supports secret: prefix) |
rate_limit |
0 (unlimited) |
Requests per minute |
max_tool_rounds |
10 |
Max tool-use iterations per request |
context_mode |
auto for ollama, tools otherwise |
tools, prefetch, or auto |
docs_mode |
full |
Inject docs into AI system prompt: full, condensed, off |
See AI Assistant for details.
backup
backup:
url: "s3://my-bucket/vigo"
access_key_id: "secret:vigo/backup/aws_access_key"
secret_access_key: "secret:vigo/backup/aws_secret_key"
| Option | Default | Description |
|---|---|---|
url |
— | Replica URL: s3://, gcs://, sftp://, file:///. Empty = disabled |
access_key_id |
— | S3/GCS access key (supports secret: prefix) |
secret_access_key |
— | S3/GCS secret key (supports secret: prefix) |
region |
provider default | AWS region |
endpoint |
— | S3-compatible endpoint (MinIO, Backblaze) |
force_path_style |
false |
Path-style S3 URLs (required for MinIO) |
retention |
720h (30 days) |
Snapshot retention period |
sync_interval |
1s |
WAL sync frequency |
Only applies when database.driver is sqlite. See Backup for details.
rate_limit
rate_limit:
enabled: true
checkin_per_envoy: 6
checkin_global: 500
| Option | Default | Description |
|---|---|---|
enabled |
false |
Enable rate limiting on check-in RPCs |
checkin_per_envoy |
6 |
Max check-ins per envoy per minute |
checkin_global |
500 |
Max total check-ins per second |
See Rate Limiting for details.
maintenance
maintenance:
freeze_until: "2026-03-14T06:00:00Z"
| Option | Default | Description |
|---|---|---|
freeze_until |
— | RFC3339 timestamp; config publishes blocked while active |
See Maintenance Windows for details.
publish
Controls change approval workflow for vigocli config publish.
publish:
approval_required: true
approval_timeout: "24h"
compliance_threshold: 90
rollback_window: "5m"
| Option | Default | Description |
|---|---|---|
approval_required |
false |
When true, config publish creates a pending change requiring a second admin to approve |
approval_timeout |
"24h" |
Pending changes expire after this duration |
compliance_threshold |
0 (disabled) |
Auto-rollback if fleet convergence drops below this percentage after reload (1-100) |
rollback_window |
"5m" |
Duration to monitor convergence after reload |
retraction.enabled |
false |
Auto-generate .retract modules when modules are removed during publish. Requires ai.enabled: true. See Module Retraction |
When approval_required is enabled, vigocli config publish no longer applies changes immediately. Instead it creates a pending change record that must be approved by a different admin via vigocli config approve or the REST API. Pending changes that are not approved within approval_timeout expire automatically.
If compliance_threshold is set, the server monitors fleet convergence for rollback_window after a config reload. If convergence drops below the threshold percentage, the server automatically rolls back to the previous config snapshot.
stream_edit
Guardrails for stream_edit pipelines in file resources. Scripts piped through stream_edit run as root on managed nodes — these settings control what scripts are allowed.
stream_edit:
enabled: true # false = strip all stream_edit globally
allowed_paths: # directories where scripts must reside
- "/srv/vigo/scripts"
default_timeout: "10s" # per-script timeout
| Option | Default | Description |
|---|---|---|
enabled |
true |
When false, all stream_edit attributes are stripped from policy bundles before delivery to agents |
allowed_paths |
["/srv/vigo/scripts"] |
Script paths must be under one of these directories. Paths outside are stripped with a warning |
default_timeout |
"10s" |
Per-script execution timeout. Scripts exceeding this are killed and the resource fails |
In addition to server-side path validation, agents enforce:
- Scripts must be owned by root (uid 0)
- Scripts must not be world-writable
- Scripts must exist and be regular files
task
Controls task dispatch behavior.
task:
require_definition: true
| Option | Default | Description |
|---|---|---|
require_definition |
false |
When true, only named task definitions (from the stockpile/tasks/ directory) can be dispatched. Ad-hoc commands are rejected with 403. |
When enabled, POST /api/v1/task requires a taskName field referencing a predefined task definition. Requests with only a command field (ad-hoc execution) are rejected. This prevents arbitrary command execution while still allowing controlled operations through pre-approved task definitions.
branding
Custom branding for the web UI. When set, overrides the default Vigo logo and displays your organization's name in the sidebar and footer.
branding:
org_name: "ACME Corp" # shown in sidebar and footer
logo_path: "/srv/vigo/branding/logo.svg" # sidebar logo (SVG or PNG)
favicon_path: "/srv/vigo/branding/icon.svg" # browser tab icon
| Field | Default | Description |
|---|---|---|
org_name |
(none) | Organization name shown below the sidebar logo and in the page footer |
logo_path |
embedded Vigo logo | Path to custom sidebar logo (SVG or PNG) |
favicon_path |
embedded Vigo icon | Path to custom browser tab icon |
Place branding assets in /srv/vigo/branding/ (or any path accessible to the server process). When a path is not set, the embedded Vigo default is used.
Environment Variables
Only one environment variable is supported. All other configuration belongs in server.yaml.
| Variable | Default | Description |
|---|---|---|
VIGO_CONFIG_FILE |
server.yaml |
Path to server config file (bootstraps everything else) |
The server version is injected at build time via -ldflags="-X main.version=1.2.3" and defaults to dev.
paths
Directory paths for server subsystems.
paths:
migrations: "migrations"
custom_traits: "custom-traits"
docs: "docs"
tasks: "stockpile/tasks"
workflows: "workflows"
agent_dist: "dist/agent"
license_dir: "/srv/vigo/license"
snapshots: "/srv/vigo/snapshots"
| Option | Default | Description |
|---|---|---|
snapshots |
/srv/vigo/snapshots |
Directory where the vigo-server-backup module writes dated snapshot directories. The web UI reads this directory to display snapshot history at /backups. Must match the module's snapshot_dir var. |
License
Controls how nodes are counted for license enforcement.
license:
mode: "local" # default — count nodes on this server only
In multi-site deployments using spanner, set mode: aggregate on the hub to count nodes across all spokes under a single license. Spokes skip enforcement entirely in this mode.
# Hub server.yaml
license:
mode: "aggregate"
# Spoke server.yaml — no license section needed (hub enforces)
grc
Push compliance evidence to external GRC platforms (Vanta, Drata, ServiceNow GRC) on a configurable schedule.
grc:
integrations:
- name: vanta
enabled: true
endpoint: "https://api.vanta.com/v1/evidence"
api_key: "secret:vigo/grc/vanta-api-key"
push_interval: "6h"
frameworks:
hipaa: "hipaa-2013"
soc2: "soc2-2017"
pci-dss: "pci-dss-4.0"
| Field | Default | Description |
|---|---|---|
name |
(required) | Integration name (for logging) |
enabled |
false |
Enable this integration |
endpoint |
(required) | API URL to POST evidence to |
api_key |
(required) | Bearer token for authentication (supports secret: prefix) |
push_interval |
6h |
How often to push evidence (minimum 1 minute) |
frameworks |
(required) | Map of Vigo standard keys to GRC platform framework IDs |
The pusher sends a JSON payload with fleet size, per-standard coverage percentages, covered/missing control counts, and envoy count. Evidence is pushed on startup and at each interval.
Security Alert Events
In addition to operational events (drift.detected, envoy.stale, etc.), the following security-specific events can be configured in smtp.events and webhook --events:
| Event | Trigger |
|---|---|
security.rootkit |
chkrootkit detects infection or rkhunter detects warnings |
security.malware |
ClamAV infected count increases from 0 |
security.integrity |
AIDE clean changes from true to false |
security.cve_critical |
Trivy critical CVE count increases from 0 |
security.hardening_drop |
Lynis hardening score drops by more than 10 points |
These events fire automatically during agent trait reporting — no additional configuration beyond enabling them in smtp.events or webhook subscriptions.
smtp:
events:
security.rootkit:
recipients: ["soc@example.com"]
security.cve_critical: {} # uses default recipients
swarm
Peer-to-peer content distribution. When enabled, agents download large files from each other instead of all pulling from the server.
swarm:
enabled: true
seed_dir: "/var/lib/vigo/swarm/seed"
port: 1531
max_bandwidth_percent: 50
| Option | Default | Description |
|---|---|---|
enabled |
false |
Enable swarm P2P distribution |
seed_dir |
/var/lib/vigo/swarm/seed |
Directory where vigocli swarm distribute writes seed files |
port |
1531 |
Agent peer server listen port (mTLS HTTPS) |
max_bandwidth_percent |
50 |
Maximum network bandwidth agents will use for swarm transfers (percentage of interface capacity) |
Agents listen on the configured port for peer chunk requests using the same mTLS certificates as gRPC communication. Multicast discovery uses UDP on 224.0.0.42 at the same port.