35C4n0r 99510a1f75
feat(coder/modules/boundary): add agent-firewall module (#840)
## Description

Extracts boundary installation and wrapper logic into a standalone
`coder/agent-firewall` module, decoupling it from `agentapi`.

### Why

Boundary is currently embedded inside `agentapi` (`scripts/boundary.sh`)
and duplicated in `claude-code`. This couples network isolation to the
AI/Tasks stack, but boundary is a general-purpose primitive — users
running a plain agent with no agentapi or tasks should be able to use it
too.

### What this adds

`registry/coder/modules/agent-firewall/` — a new first-class module
that:

* Installs boundary via one of three strategies:
  1. `coder boundary` subcommand (default, zero-install)
  2. Direct binary from release (`use_agent_firewall_directly = true`)
  3. Compiled from source (`compile_agent_firewall_from_source = true`)
* Ships a comprehensive [default allowlist
config](registry/coder/modules/agent-firewall/config.yaml.tftpl)
(Anthropic, OpenAI, VCS, package managers, cloud platforms, etc.)
* Auto-fills the Coder deployment domain via
`data.coder_workspace.me.access_url`
* Supports inline config (`agent_firewall_config`) or external file
(`agent_firewall_config_path`), mutually exclusive with cross-variable
validation
* Creates a wrapper script at
`$HOME/.coder-modules/coder/agent-firewall/scripts/agent-firewall-wrapper.sh`
* Strips `CAP_NET_ADMIN` from the coder binary (copies to
`coder-no-caps`) to allow execution inside network namespaces without
`sys_admin`
* Supports `pre_install_script` / `post_install_script` hooks
* Exposes `agent_firewall_wrapper_path`, `agent_firewall_config_path`,
and `scripts` outputs for script coordination
* No env vars exported — everything is output-only

### Usage

```tf
module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id
}
```

Works standalone with any agent — no agentapi dependency required.

### Testing

* 8 Terraform plan tests (`agent-firewall.tftest.hcl`): default outputs,
compile from source, use directly, custom hooks, custom module
directory, inline config, external config path, mutual exclusion
validation
* TypeScript integration tests (`main.test.ts`): state verification,
coder subcommand happy path, inline config, config path skip, custom
hooks, env var absence, wrapper execution, idempotent installation

## Type of Change

- [X] New module

## Module Information

**Path:** `registry/coder/modules/agent-firewall` <br>**New version:**
`v0.0.1` <br>**Breaking change:** No

## Related Issues

Closes coder/registry#844

🤖 Generated by Coder Agents

---------

Co-authored-by: Jay Kumar <jay.kumar@coder.com>
2026-05-10 06:23:37 +00:00
..

display_name description icon verified tags
Agent Firewall Configures agent-firewall for network isolation in Coder workspaces ../../../../.icons/coder.svg true
agent-firewall
ai
agents
firewall
boundary

Agent Firewall

Installs agent-firewall for network isolation in Coder workspaces.

This module:

  • Installs agent-firewall (via coder subcommand, direct installation, or compilation from source)
  • Creates a wrapper script at $HOME/.coder-modules/coder/agent-firewall/scripts/agent-firewall-wrapper.sh
  • Writes a default agent-firewall config to $HOME/.coder-modules/coder/agent-firewall/config/config.yaml (customizable)
  • Provides the wrapper path, config path, and script names via outputs
  • Uses coder-utils and output scripts for synchronization. https://registry.coder.com/modules/coder/coder-utils?tab=outputs
module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id
}

Examples

Use the agent_firewall_wrapper_path output to access the wrapper path and agent_firewall_config_path to access config path in Terraform and pass it to scripts that should run commands in network isolation.

With Claude Code

Use agent-firewall alongside the claude-code module to run Claude in a network-isolated environment.

As an automated task

module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id
}

resource "coder_script" "claude_with_agent_firewall" {
  agent_id     = coder_agent.main.id
  display_name = "Claude (Agent Firewall)"
  run_on_start = true
  script       = <<-EOT
    #!/bin/bash
    set -e
    coder exp sync want claude-agent-firewall \
      ${join(" ", module.agent-firewall.scripts)} \
      ${join(" ", module.claude-code.scripts)}
    coder exp sync start claude-agent-firewall
  "${module.agent-firewall.agent_firewall_wrapper_path}" --config="${module.agent-firewall.agent_firewall_config_path}" -- claude -p "Fix issue #840 from coder/coder"
  EOT
}

As a Coder app

module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id
}

resource "coder_app" "claude_with_agent_firewall" {
  agent_id     = coder_agent.main.id
  display_name = "Claude Code"
  slug         = "claude-code"
  command      = <<-EOT
    #!/bin/bash
    set -e
    exec tmux new-session -A -s claude-code \
      '"${module.agent-firewall.agent_firewall_wrapper_path}" --config="${module.agent-firewall.agent_firewall_config_path}" -- claude'
  EOT
}

Configuration

The module ships with a comprehensive default config based on the Coder dogfood allowlist. It covers Anthropic services, OpenAI services, version control, package managers, container registries, cloud platforms, and common development tools.

The Coder deployment domain is automatically added to the allowlist using data.coder_workspace.me.access_url.

By default the config is written to $HOME/.coder-modules/coder/agent-firewall/config/config.yaml. You can access the resolved path via the agent_firewall_config_path output. Override it in two ways:

Inline config

Pass the full YAML content directly:

module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id

  agent_firewall_config = <<-YAML
    allowlist:
      - domain=your-deployment.coder.com
      - domain=api.anthropic.com
      - domain=api.openai.com
    log_dir: /tmp/agent_firewall_logs
    proxy_port: 8087
    log_level: warn
  YAML
}

External config file

Point to an existing config file in the workspace. The module will not write any config and the agent_firewall_config_path output will point to your path. The file must exist on disk before agent-firewall starts.

module "agent-firewall" {
  source   = "registry.coder.com/coder/agent-firewall/coder"
  version  = "0.0.1"
  agent_id = coder_agent.main.id

  agent_firewall_config_path = "/workspace/my-agent-firewall-config.yaml"
}

Note: agent_firewall_config and agent_firewall_config_path are mutually exclusive, setting both produces a validation error.

See the Agent Firewall docs for the full config reference.

References