Agent
The DAAM agent is a lightweight proxy that sits between developers and your PostgreSQL database. Deploy one agent per database — it enforces access policies, applies column-level data masking, and creates ephemeral database roles for each connection based on the developer's identity.
Overview
Developers connect through the agent as though it were a normal PostgreSQL server, but the agent intercepts every query and applies the policies and masking rules configured in the control plane.
- One agent per database — each agent proxies a single upstream PostgreSQL instance.
- Identity-aware — each developer gets an isolated database session tied to their control plane identity.
- Policy enforcement — table-level grants are evaluated on every query. Queries that reference tables not permitted by the developer's policy are rejected.
- Column-level masking — sensitive columns are masked in query results according to masking rules defined in the policy.
- Encrypted connectivity — developers reach the agent through encrypted tunnels established via the CLI, with automatic relay fallback.
- Offline resilience — agents cache policies to disk and continue enforcing them during control plane outages.
The agent never stores database credentials for developers. Isolated sessions are created on-the-fly when a secure tunnel is established and cleaned up when the connection closes.
Deployment
The agent ships as a single static binary with no runtime dependencies. Deploy it alongside your database using Docker, a direct binary download, or a systemd service.
Docker
Pull and run the agent container image. The agent can be configured entirely from DAAM_* environment variables — no config file required. Pass your agent token (obtained from the console when registering a database), the upstream connection string the agent uses to manage ephemeral roles, and the address it listens on:
docker run -d \
--name daam-agent \
--restart unless-stopped \
-p 6432:6432 \
-p 8080:8080 \
-e DAAM_LISTEN=:6432 \
-e DAAM_UPSTREAM_HOST=your-db-host \
-e DAAM_UPSTREAM_PORT=5432 \
-e DAAM_UPSTREAM_DSN=postgres://daam_admin:PASSWORD@your-db-host:5432/yourdb \
-e DAAM_CONTROL_PLANE_URL=https://daam.dev \
-e DAAM_CONTROL_PLANE_AUTH_TOKEN=your-agent-token \
<registry>/daam-agent:latest For Kubernetes, supply the same variables from a Secret. If you prefer file-based secrets, mount a config file and a 0600 DSN file instead (see the systemd and Configuration Reference sections); environment variables and a config file can be combined, with environment variables overriding file values.
Binary Download
Download the agent binary for your platform and run it with a configuration file:
# Download the binary
curl -sLO https://<release-url>/daam-agent-linux-amd64
chmod +x daam-agent-linux-amd64
mv daam-agent-linux-amd64 /usr/local/bin/daam-agent
# Run with a config file
daam-agent --config /etc/daam/agent.yaml Systemd
For long-running deployments on Linux, create a systemd unit file so the agent starts on boot and restarts on failure:
[Unit]
Description=DAAM Agent
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/daam-agent --config /etc/daam/agent.yaml
Restart=on-failure
RestartSec=5s
User=daam
Group=daam
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target The agent registers itself with the control plane on first startup using the auth token. After registration, it receives a persistent credential and rotates it automatically. Store the auth token securely — it is only needed for the initial connection.
Configuration Reference
The agent reads its configuration from a YAML file specified with the --config flag, from DAAM_* environment variables, or both. The --config flag is optional. Below is an annotated example showing all available fields:
# Address the agent listens on for PostgreSQL connections
listen: ":6432"
# Upstream database connection
upstream:
host: "localhost"
port: 5432
# Use dsn_file in production to avoid secrets in the config
dsn_file: "/run/secrets/upstream-dsn"
# Control plane connection
control_plane:
url: "https://daam.dev"
# Use auth_token_file in production
auth_token_file: "/run/secrets/agent-token"
# Path where the agent stores its persistent credential
credential_path: "/var/lib/daam-agent/credential"
# Path for the on-disk policy cache
policy_cache_path: "/var/lib/daam-agent/policies.json"
# Path for buffered audit events during outages
audit_buffer_path: "/var/lib/daam-agent/audit-buffer.jsonl"
# Secure tunnel configuration
wireguard:
listen_port: 51820
private_key_path: "/var/lib/daam-agent/wg-private.key"
# Public host:port developers reach for a direct connection.
# Required for direct connectivity; without it connections use the relay.
endpoint: "agent.example.com:51820"
# Health check endpoint
health:
enabled: true
port: 8080
# Optional bearer token for remote /health/detailed access.
# When unset, /health/detailed is reachable from localhost only.
# auth_token: "a-long-random-token"
# Logging configuration
logging:
level: "info"
queries: false Environment Variables
For containerized and Kubernetes deployments, a curated set of variables maps onto the deployment-relevant fields. Environment variables take precedence over file values, so you can ship a base config file and override per-environment settings (or skip the file entirely). After merging, the same validation runs regardless of source — a missing required value fails startup with a clear message rather than starting silently.
DAAM_LISTEN # listen address (e.g. :6432)
DAAM_UPSTREAM_HOST # upstream PostgreSQL host
DAAM_UPSTREAM_PORT # upstream port
DAAM_UPSTREAM_TLS # true/false
DAAM_UPSTREAM_DSN # upstream connection string (supports ${VAR})
DAAM_UPSTREAM_SCOPE # comma-separated schema.table allowlist
DAAM_CONTROL_PLANE_URL # control plane URL
DAAM_CONTROL_PLANE_AUTH_TOKEN # agent registration token
DAAM_WIREGUARD_ENDPOINT # public host:port for direct connectivity
DAAM_WIREGUARD_PRIVATE_KEY_PATH # path to the WireGuard private key
DAAM_HEALTH_AUTH_TOKEN # bearer token for remote /health/detailed
DAAM_LOG_LEVEL # debug | info | warn | error Secrets such as DAAM_UPSTREAM_DSN and DAAM_CONTROL_PLANE_AUTH_TOKEN can be delivered from a Kubernetes Secret. Where filesystem secrets are an option, the file-based dsn_file and auth_token_file fields enforce 0600 permissions and are preferred. Setting DAAM_UPSTREAM_SCOPE to an empty string means deny-all (not allow-all); leave it unset to allow all tables.
Core Fields
| Field | Default | Description |
|---|---|---|
listen | :6432 | Address and port for the PostgreSQL proxy listener. |
upstream.host | localhost | Hostname of the upstream PostgreSQL server. |
upstream.port | 5432 | Port of the upstream PostgreSQL server. |
upstream.dsn_file | — | Path to a file containing a PostgreSQL DSN. Recommended over inline credentials in production. |
upstream.dsn | — | Inline PostgreSQL DSN. Use dsn_file instead in production to keep secrets out of config files. |
Control Plane Fields
| Field | Default | Description |
|---|---|---|
control_plane.url | — | URL of the DAAM control plane. Required. |
control_plane.auth_token_file | — | Path to a file containing the agent registration token. Recommended for production. |
control_plane.auth_token | — | Inline agent registration token. Use auth_token_file instead in production. |
control_plane.credential_path | /var/lib/daam-agent/credential | Path where the agent stores its persistent credential after registration. |
control_plane.policy_cache_path | /var/lib/daam-agent/policies.json | Path for the on-disk policy cache. Allows the agent to survive control plane restarts. |
control_plane.audit_buffer_path | /var/lib/daam-agent/audit-buffer.jsonl | Path for buffering audit events when the control plane is temporarily unreachable. |
Health and Logging Fields
| Field | Default | Description |
|---|---|---|
health.enabled | true | Enable the HTTP health check endpoint. |
health.port | 8080 | Port for the health check HTTP server. |
health.auth_token | — | Optional bearer token required to reach /health/detailed from a non-localhost address. When unset, the detailed endpoint is reachable from localhost only. |
logging.level | info | Log level: debug, info, warn, or error. |
logging.queries | true | Log individual SQL queries. On by default; set to false to disable query logging entirely. |
logging.redact_queries | true | Replace literal values in logged queries with placeholders so sensitive data never reaches the logs. On by default; set to false only in development when you need raw query values. |
Health Checks
The agent exposes two HTTP health check endpoints when health.enabled is true. Use these for load balancer probes, Kubernetes liveness/readiness checks, and monitoring.
Basic Health Check
GET /health returns a simple status response. No authentication required.
{
"status": "ok"
} Returns HTTP 200 with {"status":"ok"} when the agent process is running and able to accept connections. Returns HTTP 503 with {"status":"unavailable"} when the agent is shutting down or has encountered a fatal error.
Detailed Health Check
GET /health/detailed returns extended status including upstream database connectivity and control plane connection state. It is reachable from localhost without authentication; to reach it from another host, set health.auth_token and send it as a bearer token. The response includes boolean control_plane_connected and upstream_reachable fields alongside the overall status.
| Status | HTTP Code | Meaning |
|---|---|---|
healthy | 200 | Agent is serving traffic. If the control plane is unreachable the status stays healthy and control_plane_connected is false — the agent runs on cached policies. |
starting | 200 | Agent is still completing startup (bootstrap and first policy fetch) |
unhealthy | 503 | Agent cannot serve traffic (e.g. upstream database unreachable). During shutdown the status is shutting_down. |
Kubernetes Probes
Example Kubernetes liveness and readiness probe configuration for the agent container:
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10 Use the basic /health endpoint for liveness and readiness probes. The detailed endpoint is intended for operational dashboards; it is localhost-only unless you configure health.auth_token, which makes it a poor fit for orchestrator probes that hit the pod over the network.
Schema Introspection
The agent automatically discovers the schema of the upstream database (tables, columns, and data types) and reports it to the control plane. This powers the policy editor in the console — when you create or edit a policy, you can select from actual tables and columns rather than typing names manually.
- Automatic discovery — the agent discovers schemas, tables, and columns from the upstream database.
- On-demand refresh — the control plane can request a schema refresh at any time.
- Reported to control plane — the discovered schema is made available in the console for policy authoring.
- Read-only — the agent never modifies the upstream schema. Introspection uses read-only queries only.
The discovered schema is used only for the policy editor UI and does not affect query enforcement.
Policy Caching
Agents cache policies to disk so they can continue enforcing access controls during control plane outages. This ensures that a temporary loss of connectivity to the control plane does not leave your database unprotected or inaccessible.
- Disk persistence — policies are written to the path specified by
control_plane.policy_cache_pathwhenever the agent receives a policy update. - Integrity verification — the cache file is integrity-verified on startup. Corrupted caches are rejected.
- No expiry — cached policies do not expire. The agent uses them indefinitely until replaced by a fresh update from the control plane.
- Fail-closed on cold start — if the agent cannot verify its policies, it refuses connections. Security is never degraded by an outage.
| Scenario | Behavior |
|---|---|
| Control plane reachable | Agent uses live policies from the control plane and updates the disk cache |
| Control plane down, cache exists | Agent continues with cached policies. Health status stays healthy with control_plane_connected: false |
| Control plane down, no cache | Agent rejects all connections (fail-closed). Health status reports unhealthy |
| Control plane restored | Agent reconnects, receives latest policies, updates cache, drains buffered audit events |
Audit events generated during a control plane outage are buffered locally. When connectivity is restored, buffered events are drained to the control plane automatically.
Graceful Shutdown
The agent shuts down gracefully, draining active connections before exiting. The health endpoint reports unhealthy during shutdown so load balancers stop sending traffic.
Set the Kubernetes terminationGracePeriodSeconds to at least 65 seconds to allow the agent to finish draining connections before the pod is killed.