DAAM
Alpha

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

FieldDefaultDescription
listen:6432Address and port for the PostgreSQL proxy listener.
upstream.hostlocalhostHostname of the upstream PostgreSQL server.
upstream.port5432Port of the upstream PostgreSQL server.
upstream.dsn_filePath to a file containing a PostgreSQL DSN. Recommended over inline credentials in production.
upstream.dsnInline PostgreSQL DSN. Use dsn_file instead in production to keep secrets out of config files.

Control Plane Fields

FieldDefaultDescription
control_plane.urlURL of the DAAM control plane. Required.
control_plane.auth_token_filePath to a file containing the agent registration token. Recommended for production.
control_plane.auth_tokenInline agent registration token. Use auth_token_file instead in production.
control_plane.credential_path/var/lib/daam-agent/credentialPath where the agent stores its persistent credential after registration.
control_plane.policy_cache_path/var/lib/daam-agent/policies.jsonPath 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.jsonlPath for buffering audit events when the control plane is temporarily unreachable.

Health and Logging Fields

FieldDefaultDescription
health.enabledtrueEnable the HTTP health check endpoint.
health.port8080Port for the health check HTTP server.
health.auth_tokenOptional bearer token required to reach /health/detailed from a non-localhost address. When unset, the detailed endpoint is reachable from localhost only.
logging.levelinfoLog level: debug, info, warn, or error.
logging.queriestrueLog individual SQL queries. On by default; set to false to disable query logging entirely.
logging.redact_queriestrueReplace 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.

StatusHTTP CodeMeaning
healthy200Agent 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.
starting200Agent is still completing startup (bootstrap and first policy fetch)
unhealthy503Agent 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_path whenever 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.
ScenarioBehavior
Control plane reachableAgent uses live policies from the control plane and updates the disk cache
Control plane down, cache existsAgent continues with cached policies. Health status stays healthy with control_plane_connected: false
Control plane down, no cacheAgent rejects all connections (fail-closed). Health status reports unhealthy
Control plane restoredAgent 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.