From a30ef222d5538b388d11267afa401c0bdab707c6 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 11:00:36 -0500 Subject: [PATCH 01/10] init simple integration --- registry/coder/modules/claude-code/main.tf | 17 ++++++++++++ .../coder/modules/claude-code/main.tftest.hcl | 26 +++++++++++++++++++ .../modules/claude-code/scripts/start.sh | 18 ++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 4836347b7..4d2ad66cf 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -192,6 +192,18 @@ variable "claude_md_path" { default = "$HOME/.claude/CLAUDE.md" } +variable "enable_boundary" { + type = bool + description = "Whether to enable coder boundary for network filtering" + default = false +} + +variable "boundary_log_dir" { + type = string + description = "Directory for boundary logs" + default = "/tmp/boundary_logs" +} + resource "coder_env" "claude_code_md_path" { count = var.claude_md_path == "" ? 0 : 1 @@ -231,6 +243,8 @@ locals { start_script = file("${path.module}/scripts/start.sh") module_dir_name = ".claude-module" remove_last_session_id_script_b64 = base64encode(file("${path.module}/scripts/remove-last-session-id.sh")) + # Extract hostname from access_url for boundary --allow flag + coder_host = replace(replace(data.coder_workspace.me.access_url, "https://", ""), "http://", "") } module "agentapi" { @@ -270,6 +284,9 @@ module "agentapi" { ARG_PERMISSION_MODE='${var.permission_mode}' \ ARG_WORKDIR='${local.workdir}' \ ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ + ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \ + ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \ + ARG_CODER_HOST='${local.coder_host}' \ /tmp/start.sh EOT diff --git a/registry/coder/modules/claude-code/main.tftest.hcl b/registry/coder/modules/claude-code/main.tftest.hcl index c48923cf3..55eedd5e6 100644 --- a/registry/coder/modules/claude-code/main.tftest.hcl +++ b/registry/coder/modules/claude-code/main.tftest.hcl @@ -187,3 +187,29 @@ run "test_claude_code_permission_mode_validation" { error_message = "Permission mode should be one of the valid options" } } + +run "test_claude_code_with_boundary" { + command = plan + + variables { + agent_id = "test-agent-boundary" + workdir = "/home/coder/boundary-test" + enable_boundary = true + boundary_log_dir = "/tmp/test-boundary-logs" + } + + assert { + condition = var.enable_boundary == true + error_message = "Boundary should be enabled" + } + + assert { + condition = var.boundary_log_dir == "/tmp/test-boundary-logs" + error_message = "Boundary log dir should be set correctly" + } + + assert { + condition = local.coder_host != "" + error_message = "Coder host should be extracted from access URL" + } +} diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index b5fca7a5a..ccc22d90a 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -15,6 +15,9 @@ ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-} ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-} ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d) +ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false} +ARG_BOUNDARY_LOG_DIR=${ARG_BOUNDARY_LOG_DIR:-"/tmp/boundary_logs"} +ARG_CODER_HOST=${ARG_CODER_HOST:-} echo "--------------------------------" @@ -25,6 +28,9 @@ printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIO printf "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE" printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT" printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR" +printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY" +printf "ARG_BOUNDARY_LOG_DIR: %s\n" "$ARG_BOUNDARY_LOG_DIR" +printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST" echo "--------------------------------" @@ -74,7 +80,17 @@ function start_agentapi() { fi fi printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")" - agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}" + + if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then + mkdir -p "$ARG_BOUNDARY_LOG_DIR" + printf "Starting with coder boundary enabled\n" + agentapi server --type claude --term-width 67 --term-height 1190 -- \ + coder boundary --log-dir "$ARG_BOUNDARY_LOG_DIR" \ + --allow "*.anthropic.com" --allow "$ARG_CODER_HOST" -- \ + claude "${ARGS[@]}" + else + agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}" + fi } validate_claude_installation From 00337b3bdfe59c27f28bc20afd73837d08395f91 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 11:19:21 -0500 Subject: [PATCH 02/10] default unprivileged --- registry/coder/modules/claude-code/main.tf | 7 +++++++ registry/coder/modules/claude-code/scripts/start.sh | 13 +++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 4d2ad66cf..6034cd5cd 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -204,6 +204,12 @@ variable "boundary_log_dir" { default = "/tmp/boundary_logs" } +variable "boundary_unprivileged" { + type = bool + description = "Whether to use --unprivileged flag with coder boundary (recommended for security)" + default = true +} + resource "coder_env" "claude_code_md_path" { count = var.claude_md_path == "" ? 0 : 1 @@ -286,6 +292,7 @@ module "agentapi" { ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \ ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \ ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \ + ARG_BOUNDARY_UNPRIVILEGED='${var.boundary_unprivileged}' \ ARG_CODER_HOST='${local.coder_host}' \ /tmp/start.sh EOT diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index ccc22d90a..00d536f7c 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -17,6 +17,7 @@ ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d) ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false} ARG_BOUNDARY_LOG_DIR=${ARG_BOUNDARY_LOG_DIR:-"/tmp/boundary_logs"} +ARG_BOUNDARY_UNPRIVILEGED=${ARG_BOUNDARY_UNPRIVILEGED:-true} ARG_CODER_HOST=${ARG_CODER_HOST:-} echo "--------------------------------" @@ -30,6 +31,7 @@ printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT" printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR" printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY" printf "ARG_BOUNDARY_LOG_DIR: %s\n" "$ARG_BOUNDARY_LOG_DIR" +printf "ARG_BOUNDARY_UNPRIVILEGED: %s\n" "$ARG_BOUNDARY_UNPRIVILEGED" printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST" echo "--------------------------------" @@ -84,9 +86,16 @@ function start_agentapi() { if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then mkdir -p "$ARG_BOUNDARY_LOG_DIR" printf "Starting with coder boundary enabled\n" + + # Build boundary args with conditional --unprivileged flag + BOUNDARY_ARGS=(--log-dir "$ARG_BOUNDARY_LOG_DIR") + if [ "${ARG_BOUNDARY_UNPRIVILEGED:-true}" = "true" ]; then + BOUNDARY_ARGS+=(--unprivileged) + fi + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "$ARG_CODER_HOST") + agentapi server --type claude --term-width 67 --term-height 1190 -- \ - coder boundary --log-dir "$ARG_BOUNDARY_LOG_DIR" \ - --allow "*.anthropic.com" --allow "$ARG_CODER_HOST" -- \ + coder boundary "${BOUNDARY_ARGS[@]}" -- \ claude "${ARGS[@]}" else agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}" From 4a1bf33b28dce8b63b5b0a6649474d8dc6fad6d1 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 14:36:30 -0500 Subject: [PATCH 03/10] additional urls and agentapi required urls --- registry/coder/modules/claude-code/main.tf | 7 +++++++ registry/coder/modules/claude-code/scripts/start.sh | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 6034cd5cd..9d3c4a8b7 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -210,6 +210,12 @@ variable "boundary_unprivileged" { default = true } +variable "boundary_additional_allowed_urls" { + type = list(string) + description = "Additional URLs to allow through boundary (in addition to default allowed URLs)" + default = [] +} + resource "coder_env" "claude_code_md_path" { count = var.claude_md_path == "" ? 0 : 1 @@ -293,6 +299,7 @@ module "agentapi" { ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \ ARG_BOUNDARY_LOG_DIR='${var.boundary_log_dir}' \ ARG_BOUNDARY_UNPRIVILEGED='${var.boundary_unprivileged}' \ + ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS='${join(" ", var.boundary_additional_allowed_urls)}' \ ARG_CODER_HOST='${local.coder_host}' \ /tmp/start.sh EOT diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 00d536f7c..5dd959d4e 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -92,7 +92,16 @@ function start_agentapi() { if [ "${ARG_BOUNDARY_UNPRIVILEGED:-true}" = "true" ]; then BOUNDARY_ARGS+=(--unprivileged) fi - BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "$ARG_CODER_HOST") + # Add default allowed URLs + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "$ARG_CODER_HOST") + + # Add any additional allowed URLs from the variable + if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then + IFS=' ' read -ra ADDITIONAL_URLS <<< "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" + for url in "${ADDITIONAL_URLS[@]}"; do + BOUNDARY_ARGS+=(--allow "$url") + done + fi agentapi server --type claude --term-width 67 --term-height 1190 -- \ coder boundary "${BOUNDARY_ARGS[@]}" -- \ From 2a3e76d31c49d76d2f5acbca65ea724d41d3415a Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 15:06:53 -0500 Subject: [PATCH 04/10] it's behind exp omfg --- registry/coder/modules/claude-code/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 5dd959d4e..9f1fd0a25 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -104,7 +104,7 @@ function start_agentapi() { fi agentapi server --type claude --term-width 67 --term-height 1190 -- \ - coder boundary "${BOUNDARY_ARGS[@]}" -- \ + coder exp boundary "${BOUNDARY_ARGS[@]}" -- \ claude "${ARGS[@]}" else agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}" From 9796e05fdc0d34259bd220a36a68c34c6ea2a75d Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 15:21:35 -0500 Subject: [PATCH 05/10] allow localhost for healthz and other reporting --- registry/coder/modules/claude-code/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 9f1fd0a25..7f31ea488 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -93,7 +93,7 @@ function start_agentapi() { BOUNDARY_ARGS+=(--unprivileged) fi # Add default allowed URLs - BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "$ARG_CODER_HOST") + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost" --allow "$ARG_CODER_HOST") # Add any additional allowed URLs from the variable if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then From 01c436a751871c3aa41f0bc986f4f900b329aaf3 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 15:34:31 -0500 Subject: [PATCH 06/10] specifically allow healthz --- registry/coder/modules/claude-code/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 7f31ea488..2488dda4c 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -93,7 +93,7 @@ function start_agentapi() { BOUNDARY_ARGS+=(--unprivileged) fi # Add default allowed URLs - BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost" --allow "$ARG_CODER_HOST") + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost:8080/healthz" --allow "$ARG_CODER_HOST") # Add any additional allowed URLs from the variable if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then From 7acb90b2b8cb8eb39c9a1417424e7dad24f35ae7 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 15:47:57 -0500 Subject: [PATCH 07/10] fmt --- registry/coder/modules/claude-code/scripts/start.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index 2488dda4c..dd846cba3 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -86,7 +86,7 @@ function start_agentapi() { if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then mkdir -p "$ARG_BOUNDARY_LOG_DIR" printf "Starting with coder boundary enabled\n" - + # Build boundary args with conditional --unprivileged flag BOUNDARY_ARGS=(--log-dir "$ARG_BOUNDARY_LOG_DIR") if [ "${ARG_BOUNDARY_UNPRIVILEGED:-true}" = "true" ]; then @@ -94,7 +94,7 @@ function start_agentapi() { fi # Add default allowed URLs BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost:8080/healthz" --allow "$ARG_CODER_HOST") - + # Add any additional allowed URLs from the variable if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then IFS=' ' read -ra ADDITIONAL_URLS <<< "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" @@ -102,7 +102,7 @@ function start_agentapi() { BOUNDARY_ARGS+=(--allow "$url") done fi - + agentapi server --type claude --term-width 67 --term-height 1190 -- \ coder exp boundary "${BOUNDARY_ARGS[@]}" -- \ claude "${ARGS[@]}" From 2cf20a4bda3226e2665954cf2556352723c6d231 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 16:18:20 -0500 Subject: [PATCH 08/10] try just localhost:8080 for the allow rule --- registry/coder/modules/claude-code/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index dd846cba3..f5644744b 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -93,7 +93,7 @@ function start_agentapi() { BOUNDARY_ARGS+=(--unprivileged) fi # Add default allowed URLs - BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost:8080/healthz" --allow "$ARG_CODER_HOST") + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost:8080" --allow "$ARG_CODER_HOST") # Add any additional allowed URLs from the variable if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then From 919c1bbbc5a69f8e4e5408c3fa06b87f039a1c7e Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 16:48:26 -0500 Subject: [PATCH 09/10] try changing claudes mcp port --- registry/coder/modules/claude-code/main.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index 9d3c4a8b7..fd3df5ec1 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -246,6 +246,12 @@ resource "coder_env" "claude_api_key" { value = var.claude_api_key } +resource "coder_env" "mcp_server_port" { + agent_id = var.agent_id + name = "MCP_SERVER_PORT" + value = "8081" +} + locals { # we have to trim the slash because otherwise coder exp mcp will # set up an invalid claude config From 7ba975a2d890dbc56ba54c066c28f6f2d774cbee Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 6 Oct 2025 17:12:08 -0500 Subject: [PATCH 10/10] remove the localhost allowance, going to fix on the boundary side of things --- registry/coder/modules/claude-code/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index f5644744b..195730ccf 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -93,7 +93,7 @@ function start_agentapi() { BOUNDARY_ARGS+=(--unprivileged) fi # Add default allowed URLs - BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "localhost:8080" --allow "$ARG_CODER_HOST") + BOUNDARY_ARGS+=(--allow "*.anthropic.com" --allow "registry.npmjs.org" --allow "*.sentry.io" --allow "claude.ai" --allow "$ARG_CODER_HOST") # Add any additional allowed URLs from the variable if [ -n "$ARG_BOUNDARY_ADDITIONAL_ALLOWED_URLS" ]; then