Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

AI Governance

Cowboy constrains what an agent can do through several independent mechanisms. They overlap deliberately: a command rejected by one layer is not relied upon to be caught by another.

LayerWhereWhat it does
Approvalsbridge / Redishold outbound messages for a human reaction
Egress allowlistsecrets proxygate state-mutating HTTP, inject credentials
Sheepdogseccomp sandboxenforce file/network/exec rules at the syscall level

This page summarizes how they are configured. There is no runtime rule engine or FilterAction-style API in the harness — governance is the sum of the mechanisms below. Command, path, and syscall enforcement is done by sheepdog at the kernel boundary (see below), not by a separate string-matching filter layer.

Approvals (human in the loop)

For outbound messages that should not be sent autonomously, the bridge approval protocol holds a message until a human reacts to a notification. Configure it per bridge:

services.cowboy.bridges.discord.approval = {
  required = true;
  notify = "discord";
  notify_channel = "<channel-id>";
  timeout = 3600;
};

Approval state is tracked in Redis hashes and resolved from human reactions. See Approvals & Outbox for the full protocol.

Egress allowlist (secrets proxy)

When the secrets proxy is enabled, agent HTTP traffic is forced through it. The proxy injects real API credentials (the agent only ever holds placeholders) and enforces an egress policy: read methods (GET, HEAD, OPTIONS, TRACE) are always allowed, but state-mutating methods (POST, PUT, PATCH, DELETE) are allowed only to domains on an allowlist. Everything else returns 403.

services.cowboy.secretsProxy = {
  enable = true;
  domainMappings = {
    "api.anthropic.com" = {
      secretPath = "/run/agenix/anthropic-key";
      headerName = "x-api-key";
    };
  };
  # Extra write-allowed domains. Domains in domainMappings are implicitly
  # write-allowed.
  allowedWriteDomains = [ "github.com" "api.github.com" "*.githubusercontent.com" ];
};

The allowlist enforcement lives in the mitmproxy addon (proxy/addon.py); write methods, the allowed-domain check, and wildcard matching are implemented there. See Security Model.

Sheepdog (seccomp sandbox)

Sheepdog enforces file, network, and exec rules at the syscall level rather than by string matching. Rules are verb-granular — Bash, Read, Edit, Create, Delete, and Connect — and resolve to allow or deny, with optional runtime-granted exceptions (lazy permissions) taking precedence over baked-in denies.

services.cowboy.sheepdog = {
  enable = true;
  lazyPerms = true;   # allow runtime permission grants
};

services.cowboy.agents.<name>.sheepdog = {
  deny  = [ "Connect(0.0.0.0/0)" ];
  allow = [ "Read(/home/*/workspace/**)" "Edit(/home/*/workspace/**)" ];
  blockedSyscalls = [ /* ... */ ];
  readonlyPaths = [ /* ... */ ];
  maskedPaths   = [ /* ... */ ];
};

Sheepdog is Linux-only. See crates/sheepdog/src/policy.rs and modules/options/sheepdog.nix.

See also