Skip to main content

Configure a Privacy Agent's local policy

Status: in development (#560). The policy file and the local audit log ship with the Privacy Agent v2, which is not released yet.

Goal​

You'll control, from the agent host, what Measured Cloud may ask your Privacy Agent to do: whether custom SQL rules may run, which data sources and job kinds it serves, how many sample values may leave, and whether database sessions are read-only. Measured Cloud cannot override this policy. It shows the policy in effect, and every job it refused, in Settings → Privacy Agents.

You'll also find the agent's local audit log, a record of every job Measured Cloud asked it to run.

Prereqs​

  • A Privacy Agent you can configure: you can edit its environment and put a file on its host or in its container.
  • A read-only database user for the agent. The policy is a second line of defence, not a replacement for least privilege.

Steps​

  1. Write policy.yaml. Every setting is optional. Leave one out to keep its default:

    # Custom SQL rules can infer data one count at a time. Default: true.
    allow_custom_sql: false

    # Most sample or failing values per result that may leave the agent.
    # Default: no local cap. 0 = none.
    max_sample_values: 10

    # Data sources this agent may touch, by id or by name. Default: all.
    # An empty list allows none.
    allowed_datasources:
    - 3f6c1a52-8e0b-4c1e-9d43-2a7b5f0e9c11
    - warehouse

    # Job kinds this agent may run. Default: all.
    # agent.self_test (Run diagnostics) is always allowed.
    allowed_job_kinds:
    - datasource.test_connection
    - datasource.discover
    - asset.introspect
    - asset.profile
    - asset.access_scan
    - validation.run

    # Open database sessions read-only where the driver supports it. Default: true.
    read_only: true

    Values are typed. allow_custom_sql: yes is a string, not a boolean, so the file is invalid. Write true or false.

  2. Point the agent at it. Mount the file read-only and set DQ_AGENT_POLICY_FILE:

    docker run -d --name dq-privacy-agent \
    -v /etc/dq-agent/policy.yaml:/etc/dq-agent/policy.yaml:ro \
    -e DQ_AGENT_POLICY_FILE=/etc/dq-agent/policy.yaml \
    ...

    Without DQ_AGENT_POLICY_FILE there's no policy file, and the defaults above apply.

  3. Check it in Measured Cloud. Open Settings → Privacy Agents and select the agent. Local policy lists each setting (for example, "Custom SQL: Blocked") and the first characters of the file's SHA-256 hash. It updates with the agent's next heartbeat, within about 30 seconds.

  4. Change it at any time. Edit the file. The agent rereads it before the next job, so you don't need to restart it.

What happens to a refused job​

The agent checks every job against the policy before it runs anything. A refused job fails at once with a stable code, blocked_by_agent_policy, and a plain reason:

SettingReason shown in Measured Cloud
allow_custom_sql: falseBlocked by this agent's local policy: custom SQL is disabled
allowed_datasourcesBlocked by this agent's local policy: data source … is not allowed
allowed_job_kindsBlocked by this agent's local policy: job kind … is not allowed
Invalid or missing fileBlocked by this agent's local policy: invalid local policy (…)

Under Recently blocked by local policy, the agent's detail view lists the jobs it refused.

A job kind that can carry custom SQL (a validation with custom SQL rules) is blocked as a whole when allow_custom_sql is false. Validations with built-in rules only still run.

If the policy file is invalid​

If DQ_AGENT_POLICY_FILE is set but the file is missing, unreadable or invalid, the agent fails closed: it refuses every job, including Run diagnostics, until you fix the file. It never falls back to the permissive defaults. The agent shows as Degraded in Settings. The detail view says the policy file is invalid and why, for example "allow_custom_sql: Input should be a valid boolean". The agent's log says the same.

These are invalid:

  • an unknown setting, such as a typo like allow_custom_sq1;
  • a value of the wrong type;
  • an unknown job kind in allowed_job_kinds;
  • the same setting written twice;
  • a file over 64 KiB.

Names in allowed_datasources​

An id always matches the job's data source. A name matches only through the agent's own record of its data sources, never through a name Measured Cloud sends with the job. If the agent can't resolve a name, it doesn't match, and the job is refused. Use ids when in doubt.

Read-only sessions​

With read_only: true, the agent opens sessions read-only on Postgres, Redshift and MySQL (SET SESSION … READ ONLY). Snowflake, BigQuery, Databricks, SQL Server and other drivers have no session switch, so there the setting is best effort. Give the agent's database user read-only grants.

The local audit log​

The agent appends one JSON line per job to its audit log. By default the log is audit/audit.jsonl under the agent's state directory. Set DQ_AGENT_AUDIT_LOG to put it elsewhere.

{"ts":"2026-10-25T10:15:02.114000Z","job_id":"…","kind":"validation.run","attempt":1,"datasource_id":"…","query_hashes":["9f86d081…"],"outcome":"blocked","duration_ms":3,"error_code":"blocked_by_agent_policy","reason":"custom SQL is disabled","policy_sha256":"4e07408…"}

Each line records the time, the job, its kind and data source, and the outcome (succeeded, failed, blocked or cancelled). It also has the duration, an error code and the hash of the policy in effect. Custom SQL appears only as its SHA-256 hash, never as text. The log never holds credentials, payloads, results or row data. To check whether a query ran, hash it: printf '%s' "$SQL" | sha256sum.

The log rotates by size. Past DQ_AGENT_AUDIT_LOG_MAX_BYTES (10 MiB), it rolls to audit.jsonl.1 and keeps DQ_AGENT_AUDIT_LOG_BACKUPS (5) old files. See the configuration reference.

The agent's local status page also shows the policy in effect and the newest audit log entries.