Vengtoo Agent
The Vengtoo Agent runs on your infrastructure. It pulls your tenant's policy bundle from Vengtoo cloud on a configurable interval (default: 30 seconds), evaluates it in-memory, and answers POST /access/v1/evaluation requests locally.
Why run the agent
- Low latency: decisions are evaluated in-memory locally, with no network round-trip to the cloud.
- Data stays local: decision inputs never leave your network.
- Graceful degradation: if the cloud is unreachable, the agent keeps serving from the last cached bundle.
- No code changes: point any SDK at the agent with a single
baseUrlconfig; the evaluation API is identical to cloud.
Architecture
On restart, the agent loads the cached bundle from disk and starts serving immediately, without waiting for the next cloud sync.
Feature scope
The agent always contacts Vengtoo Cloud to pull its policy bundle: that connection is how policies stay current. What changes when you route evaluation calls to the agent (localhost:8181) instead of the cloud endpoint is which features are enforced at decision time.
RBAC, ABAC, direct grants, and policy modifiers are baked into the bundle and evaluated fully in-memory on the agent.
The features marked below have no effect when calling the local agent: they are enforced only when evaluation goes through the cloud endpoint (pdp.vengtoo.com/access/v1/evaluation). Route any decision that depends on delegation, human-in-the-loop approval, rate limiting, or JIT subject creation to the cloud endpoint.
| Feature | Cloud endpoint | Local agent endpoint |
|---|---|---|
| RBAC / ABAC / direct grants | ✓ | ✓ |
| Policy modifiers (time window, IP allowlist) | ✓ | ✓ |
| Agent-to-agent delegation | ✓ | ✗ Delegation records are live DB state |
| Human-in-the-loop approval | ✓ | ✗ Approval state changes on human action |
| Rate limiting | ✓ | ✗ Per-subject counters require shared state |
| JIT subject creation | ✓ | ✗ Writes to the entity DB |
The agent still forwards decision events to the cloud audit log when reachable (VENGTOO_AUDIT_FORWARDING=true, the default). Set it to false to suppress forwarding entirely.
If you need delegation chains or human-in-the-loop approval enforced on the local evaluation path (for example, in an air-gapped deployment), contact us about Enterprise private-PDP options.
Install
Binary
Download the latest release from GitHub Releases for your platform (linux/amd64, linux/arm64, darwin/arm64).
Go install
go install github.com/vengtoo/agent/cmd/agent@latest
Docker
Image: vengtoo/agent on Docker Hub.
docker pull vengtoo/agent:latest
docker run -d \
--name vengtoo-agent \
-e VENGTOO_API_KEY=vgt_... \
-p 8181:8181 \
-v vengtoo-cache:/var/lib/vengtoo/bundles \
-e VENGTOO_CACHE_DIR=/var/lib/vengtoo/bundles \
vengtoo/agent:latest
Docker Compose
services:
vengtoo-agent:
image: vengtoo/agent:latest
environment:
VENGTOO_API_KEY: ${VENGTOO_API_KEY}
VENGTOO_CACHE_DIR: /var/lib/vengtoo/bundles
volumes:
- vengtoo-cache:/var/lib/vengtoo/bundles
ports:
- "8181:8181"
volumes:
vengtoo-cache:
Mount a persistent volume at VENGTOO_CACHE_DIR so the agent can warm-start across container replacements.
Configuration
The agent resolves configuration in this order:
--config <path>: explicit path passed as a CLI flag./vengtoo-agent.yaml: current working directory$HOME/.vengtoo/agent.yaml: home directory fallback
Environment variables override whichever YAML file was loaded.
| Env var | YAML key | Default | Purpose |
|---|---|---|---|
VENGTOO_API_KEY | api_key | — (required) | Bearer token sent to the cloud policy bundle endpoint. |
VENGTOO_CLOUD_URL | cloud_url | https://api.vengtoo.com | Cloud base URL. Override for staging or self-hosted. |
VENGTOO_TENANT_ID | tenant_id | auto-resolved | Usually inferred from the bundle. Set explicitly for multi-tenant bundles. |
VENGTOO_LISTEN_ADDR | listen_addr | 0.0.0.0:8181 | HTTP listen address. |
VENGTOO_POLL_INTERVAL | poll_interval | 30s | Go time.Duration. Bundle re-pull frequency. |
VENGTOO_CACHE_DIR | cache_dir | ~/.vengtoo/bundles | Where bundle.json is persisted. |
VENGTOO_LOG_LEVEL | log_level | info | Log level. |
VENGTOO_DECISION_LOG | decision_log | false | Set to true to emit a JSON log line per decision. |
VENGTOO_AGENT_NAME | agent_name | hostname | Identifies this agent instance in the dashboard. Defaults to os.Hostname(). Set explicitly when running multiple agents in the same tenant. |
VENGTOO_AUDIT_FORWARDING | audit_forwarding | true | Forwards decisions to cloud for the Decision Log. Set to false to disable. |
VENGTOO_CLIENT_SECRET | client_secret | — (optional) | When set, requires Authorization: Bearer <secret> on /access/v1/evaluation. /healthz and /readyz stay open. |
VENGTOO_AGENT_REGION | agent_region | — | Display label for the region this agent runs in. Shown in the dashboard. |
VENGTOO_AGENT_DOMAIN | agent_domain | — | Display label for the domain or environment (e.g. prod, staging). Shown in the dashboard. |
VENGTOO_HEARTBEAT_INTERVAL | heartbeat_interval | 30s | How often the agent sends a liveness ping to cloud. |
VENGTOO_AUDIT_ENDPOINT | audit_endpoint | derived from cloud_url | Override the endpoint where decision events are forwarded. Defaults to <cloud_url>/v1/agent-logs/ingest. |
VENGTOO_AUDIT_BATCH_SIZE | audit_batch_size | 100 | Number of decision events to buffer before flushing to cloud. |
VENGTOO_AUDIT_BATCH_INTERVAL | audit_batch_interval | 5s | Maximum time to wait before flushing a partial batch. |
VENGTOO_AUDIT_BUFFER_SIZE | audit_buffer_size | 10000 | In-memory buffer capacity for decision events. Events are dropped if the buffer fills. |
VENGTOO_AUDIT_RETRY_ATTEMPTS | audit_retry_attempts | 5 | Number of retry attempts for failed audit log flushes. |
VENGTOO_TRUSTED_KEYS_PATH | trusted_keys_path | ~/.vengtoo/trusted_keys.json | Path to a pinned public keys file for bundle signature verification in air-gapped environments. |
All variables are prefixed VENGTOO_.
Example vengtoo-agent.yaml:
api_key: "vgt_..."
cloud_url: "https://api.vengtoo.com"
listen_addr: "0.0.0.0:8181"
poll_interval: "30s"
cache_dir: "/var/lib/vengtoo/bundles"
decision_log: true
Using with SDKs
Point any SDK at the agent:
- Go
- Node.js
- Python
client := vengtoo.NewClient("", vengtoo.WithBaseURL("http://localhost:8181"))
const vengtoo = new Vengtoo({ baseUrl: 'http://localhost:8181' })
client = Vengtoo(base_url="http://localhost:8181")
No API key is needed on calls to the agent: the agent authenticates with cloud on your behalf to fetch bundles.
Health endpoints
| Endpoint | Purpose |
|---|---|
GET /healthz | Diagnostic JSON: bundle revision, last sync time, degraded flag, consecutive failures, sync age. Returns 200 while the process is up, even when stale. |
GET /readyz | 200 once a bundle has loaded (from cache or cloud). 503 during the cold-start window. Use this as your orchestrator readiness probe. |
Example /healthz body:
{
"status": "ok",
"bundle_revision": "a3f8c2d1",
"last_sync": "2026-04-19T14:02:41Z",
"degraded": false,
"consecutive_failures": 0,
"sync_age_seconds": 12.4
}
Metrics
GET /metrics exposes Prometheus-format metrics.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
vengtoo_agent_up | gauge | — | 1 while the process is alive. |
vengtoo_agent_decisions_total | counter | allowed, access_path | One per /access/v1/evaluation call. |
vengtoo_agent_decision_duration_seconds | histogram | allowed | Eval latency. Buckets tuned for sub-ms. |
vengtoo_agent_bundle_last_sync_timestamp_seconds | gauge | — | Unix ts of last successful sync. |
vengtoo_agent_bundle_sync_failures_total | counter | — | Monotonic count of failed bundle polls. |
vengtoo_agent_bundle_cache_hit | counter | source (cache / sync) | How the agent became ready at startup. |
vengtoo_agent_degraded | gauge | — | 1 when serving stale data because cloud sync is failing. |
vengtoo_agent_consecutive_sync_failures | gauge | — | Current sync failure streak. Resets on success. |
Recommended alerts:
vengtoo_agent_up == 0: process is down.vengtoo_agent_degraded == 1for more than 5 minutes: sustained cloud unreachability.time() - vengtoo_agent_bundle_last_sync_timestamp_seconds > 600: no successful sync in 10 minutes.
Decision logs
Set VENGTOO_DECISION_LOG=true to emit one JSON line to stdout per /access/v1/evaluation call:
{
"time": "2026-04-19T14:03:11.482Z",
"level": "INFO",
"msg": "decision",
"subject_id": "a5f77b72-d139-46e7-b3f2-09dfe049bedc",
"resource_id": "14d9ce16-dea0-4de4-b528-ef5949ef9614",
"action": "view",
"decision": true,
"reason": "Access granted via role",
"access_path": "role",
"policy_id": "550e8400-e29b-41d4-a716-446655440000",
"ms": 0.42
}
Tail them live:
docker logs -f vengtoo-agent | grep '"msg":"decision"'
Volume can be high under load, and subject/resource attributes may contain PII: leave this off in production by default. Pipe through Datadog, Loki, CloudWatch, or any structured-log collector as needed.
Graceful degradation
On every startup, the agent does two things in order:
- Load from disk cache: if
bundle.jsonexists inVENGTOO_CACHE_DIR, the agent starts serving decisions immediately./readyzgoes 200 before any network call. - Sync with cloud: pulls a fresh bundle and replaces the in-memory engine on revision bump.
If the cloud is unreachable, the agent continues serving from the cached bundle. The following signal the degraded state:
/healthz→"degraded": true,consecutive_failures > 0,sync_age_secondsgrowing.vengtoo_agent_degraded == 1.- Log line at every fifth failure:
[ERROR] Bundle sync failing for N consecutive attempts, serving from cache (last successful sync: <age>).
When sync recovers, the agent logs Bundle sync recovered after N consecutive failures, cache refreshed.
If the cloud has never been reached and there is no cached bundle on disk, /readyz stays 503 until the first successful sync.
Trust model
By default the agent trusts anything that can reach :8181: deploy it inside a Docker network, Kubernetes NetworkPolicy, or VPC with the same isolation guarantees you apply to any internal service.
For stricter isolation, set VENGTOO_CLIENT_SECRET. When set, the agent requires Authorization: Bearer <secret> on every /access/v1/evaluation call and rejects requests without it. Health and readiness probes (/healthz, /readyz) remain open so orchestrators can probe without the secret.
Troubleshooting
| Symptom | Likely cause | Check |
|---|---|---|
/readyz is 503 after a minute | Initial sync failed and no cache on disk | Is VENGTOO_CACHE_DIR mounted? Can the agent reach VENGTOO_CLOUD_URL? |
[FATAL] Failed to load config: API key is required | VENGTOO_API_KEY unset | Check env vars / mounted config. |
vengtoo_agent_degraded == 1 sustained | Cloud unreachable | Verify outbound connectivity. Logs print last successful sync age at every fifth failure. |
| Every user sees "permission denied" | Wrong tenant bundle, or bundle is stale | /healthz → compare bundle_revision with cloud. |
[ERROR] Evaluation failed per request | Bundle parses but evaluation fails on this input shape | Set VENGTOO_DECISION_LOG=true and capture the failing input. |