# Architecture ## Components and trust boundaries The Rust agent is a stateless, pull-oriented HTTP API. It stores no metrics or log history and does no background polling. `/proc`, `/sys`, journald, and fixed read-only utilities are queried only when the control plane asks. The Go backend owns users, sessions, servers, metric/log samples, retention, and audit. React is served behind nginx and only calls the backend. Grafana also talks only to the backend. The backend implements the constrained read portions of the Prometheus and Loki HTTP APIs, backed by PostgreSQL. There is intentionally no Grafana-to-agent or Loki-to-agent path and no agent token is ever provisioned into Grafana. Agent transport is expected to be WireGuard plus a 256-bit application token, or TLS plus that token. The listen address is loopback by default. Tokens live in files outside configuration and are never returned by an API. ## Agent API v1 All `/v1` routes require `Authorization: Bearer …`; `/healthz` only returns liveness. JSON bodies are capped at 64 KiB. Read traffic is capped at 10 rps, writes at 2 rps, burst 20, with 32 concurrent requests and five-second timeouts. | Method | Route | Purpose | |---|---|---| | GET | `/v1/system/status` | CPU, load, memory, swap, filesystems, uptime | | GET | `/v1/network/interfaces` | interfaces, addresses and counters | | GET | `/v1/network/routes` | kernel routes | | GET | `/v1/network/listeners` | listening TCP/UDP sockets | | GET | `/v1/network/connections` | current TCP/UDP sockets | | GET | `/v1/firewall/rules` | backend and read-only ruleset | | GET | `/v1/fail2ban/status` | installation and jail summary | | GET | `/v1/fail2ban/jails/:jail` | validated jail detail | | POST | `/v1/fail2ban/ban` | validated IP and jail ban | | POST | `/v1/fail2ban/unban` | validated IP and jail unban | | GET | `/v1/wireguard/status` | interface and peer dump | | GET | `/v1/services` | allow-listed services only | | POST | `/v1/services/:name/restart` | allow-listed restart only | | GET | `/v1/logs` | cursor-paginated journald rows from allow-listed units | No generic command route exists. Child processes receive a fixed executable and fixed subcommand plus individually validated argv values. ## Data model - `users(id, username, password_hash, role, disabled, timestamps)` - `sessions(id_hash, user_id, csrf_hash, expires_at, source_ip)` - `servers(id, name, hostname, agent_endpoint, credential_ref, tags, status, last_seen, timestamps)` - `metric_samples(server_id, sampled_at, cpu, memory, disk, rx, tx, connections, bans)` with 30-day retention - `agent_log_entries(server_id, journal_cursor, event_at, unit, priority, message, boot_id, pid)` with configurable seven-day retention - `audit_events(actor, server, action, target, before, after, result, source_ip, created_at)`; secrets are excluded Credentials are indirect references to Docker secrets or environment variables, not database values. Database migrations are append-only and run on startup. ## Observability compatibility APIs `/integrations/prometheus` accepts direct selectors for the fixed `vps_control_*` metric set. `/integrations/loki` accepts bounded stream selectors over `server_id`, `server_name`, `unit`, and `priority`. Full PromQL, arbitrary LogQL pipelines, writes, remote-write, and Loki push APIs are deliberately not implemented. Queries are bearer-authenticated with an independent secret, rate-limited, range-limited to 31 days, and result-limited. The data path is: ```text Grafana ──Prometheus/Loki read APIs──> backend ──semantic polling──> VPS agent │ PostgreSQL ``` ## Privilege design The agent has no ambient capabilities and runs as `vps-agent`. Linux usually permits the read-only endpoints without elevation. Fail2Ban mutations and service restarts need administrator-provided sudoers or polkit rules restricted to each approved verb/name. The shipped service does not install such policy automatically. Managed firewall mutation is out of v1 because giving the main daemon `CAP_NET_ADMIN` violates least privilege; its future helper protocol must provide prepare/apply/health/confirm/commit and persistent timed rollback.