CLI and flags
One binary, diffhold, runs the server and the operator commands.
Commands
| Command | What it does |
|---|---|
diffhold serve | Runs the ExtMcp server, admission webhook, approvals API and AuthZEN endpoint. |
diffhold policy check FILE... | Validates AgentPolicy files with the same rules the server uses. |
diffhold test FILE... | Runs policy test suites: for each tool call, the expected allow, deny or approval, and optionally a reason substring. |
diffhold approvals list | Lists pending calls with their diffs (admin token). |
diffhold approvals approve CALL_ID | Approves a pending call with your own token. |
diffhold grants revoke-all | Deletes every grant Secret, which revokes every outstanding per-call token. The chart runs it on uninstall. |
diffhold audit verify FILE [--public-key KEY]... | Checks a hash-chained audit log and reports the first broken record. With the public key from GET /audit/public-key, also checks every signed checkpoint and reports records after the last one. Repeat --public-key for a log that spans a key change. |
diffhold permissions check SA... | Reports what an agent ServiceAccount can do that could switch off Diffhold. Exits 1 on a high-severity finding, so it can gate CI. |
diffhold version | Prints the version. |
Policy tests
policies: [agentpolicy.yaml] cases: - name: scale over limit agent: sre-bot tool: resources_scale arguments: {apiVersion: apps/v1, kind: Deployment, name: web, namespace: team-a, scale: 50} expect: deny reason: replicas <= 5
serve flags
Gateway and identity
| Flag | Default | Meaning |
|---|---|---|
--grpc-addr | 127.0.0.1:9000 | ExtMcp gRPC listen address. |
--grpc-tls-cert, --grpc-tls-key | Server certificate. Required with --grpc-client-ca on any non-loopback address. | |
--grpc-client-ca | CA that signs the gateway's client certificate. | |
--grpc-client-names | Allowed client certificate names (CN, DNS or URI SAN). Empty allows any certificate from the CA. | |
--insecure-grpc | false | Plaintext on a non-loopback address. Testing only. |
--insecure-agent-header | Take the agent identity from this header. Testing only. |
Policies and credentials
| Flag | Default | Meaning |
|---|---|---|
--policies | Load policies from a file instead of the cluster. | |
--policy-namespace | Diffhold's namespace | Where to watch AgentPolicy objects. * watches all namespaces, so anyone who can create one anywhere can bind an agent. |
--grant-namespaces | agents | Namespaces of agent ServiceAccounts. Grant Secrets live here, and the backstop applies only to them. |
--grant-store | kubernetes | kubernetes (shared by replicas) or memory (one replica). |
--grant-key-secret | diffhold-grant-key | Secret holding the grant signing key; created if missing. |
--audit-key-secret | diffhold-audit-key | Secret holding the audit checkpoint signing key, separate from the grant key; created if missing (kubernetes grant store). |
--audit-key-file | <audit-log>.key | Audit checkpoint signing key with the memory grant store; created if missing. With --audit-log - and no file, the key is new on every start. |
--dry-run | true | Dry-run and diff every write. With it off, apply calls are denied. |
Approvals, admin and HA
| Flag | Default | Meaning |
|---|---|---|
--admin-addr | 127.0.0.1:9001 | Approvals API, AuthZEN and health. |
--admin-tls-cert, --admin-tls-key | HTTPS for the admin listener. Required on a non-loopback address unless --insecure-admin. | |
--admin-token-file | Bearer token for listing approvals, permission findings and AuthZEN. | |
--max-pending-per-agent | 20 | Open approval requests one agent may have at once. |
--permission-check | warn | off, warn (log and GET /permissions) or enforce (also deny agents with high-severity findings, and agents not yet checked). |
--audit-checkpoint-interval | 30s | How often each replica signs its audit chain head. |
--approver-audience | diffhold | Audience approver tokens must carry. |
--approval-ttl | 1h | How long a request stays open, unless the rule sets timeout. |
--approved-ttl | 15m | How long an approval can be used. |
--approval-namespace | pod namespace | Where approvals, used-grant markers and the Lease live. |
--leader-elect | true | Only the Lease holder revokes grants, expires approvals and writes status. |
--audit-log | audit.jsonl | Audit log path, or - for stdout. |
--webhook-addr, --tls-cert, --tls-key | :9443 | Admission webhook listener and certificate. |
Set OTEL_EXPORTER_OTLP_ENDPOINT to export a trace span per decision.
Helm values
| Value | Default | Meaning |
|---|---|---|
agentNamespaces | [agents] | Same as --grant-namespaces. |
policyNamespace | "" | Empty means the release namespace. |
replicaCount | 2 | With anti-affinity and a PodDisruptionBudget. |
grpc.tls.mode | auto | auto generates the CA, server and gateway client certificates; certManager has cert-manager issue and renew them; existingSecret; or insecure. The admin listener uses the same server certificate, and is plain HTTP only in insecure mode. |
grpc.tls.clientNames | [agentgateway] | Allowed gateway certificate names. |
webhook.tls.mode | auto | Self-signed CA kept across upgrades (10-year certificate, not rotated), certManager (renewed, caBundle injected), or existingSecret with a caBundle. Diffhold reloads renewed certificates within 10 s, without a restart. |
certManager.issuerRef | {} | Issuer for certManager mode, for example {name: corp-ca, kind: ClusterIssuer}. Empty: the chart creates a self-signed CA. |
certManager.duration / renewBefore | 2160h / 720h | Certificate lifetime and how early it is renewed. |
permissionCheck | warn | Same as --permission-check. |
approvals.maxPendingPerAgent | 20 | Same as --max-pending-per-agent. |
networkPolicy.enabled | false | Limit the gRPC port to the gateway's pods (networkPolicy.grpcFrom). |
revokeOnUninstall | true | Revoke every grant before the webhook is removed. |
approvals.pendingTTL, approvals.approvedTTL | 1h, 15m | Approval lifetimes. |
otel.endpoint | OTLP endpoint for traces. |