Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion registry/coder-labs/modules/copilot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ display_name: Copilot CLI
description: GitHub Copilot CLI agent for AI-powered terminal assistance
icon: ../../../../.icons/github.svg
verified: false
tags: [agent, copilot, ai, github, tasks]
tags: [agent, copilot, ai, github, tasks, aibridge]
---

# Copilot
Expand Down Expand Up @@ -164,6 +164,39 @@ module "copilot" {
}
```

### Usage with AI Bridge Proxy

[AI Bridge Proxy](https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy) routes Copilot traffic through [AI Bridge](https://coder.com/docs/ai-coder/ai-bridge) for centralized LLM management and governance.
The proxy environment variables are scoped to the Copilot process only and do not affect other workspace traffic.

```tf
module "aibridge-proxy" {
source = "registry.coder.com/coder/aibridge-proxy/coder"
version = "1.0.0"
agent_id = coder_agent.main.id
proxy_url = "https://aiproxy.example.com"
}

module "copilot" {
source = "registry.coder.com/coder-labs/copilot/coder"
version = "0.4.0"
agent_id = coder_agent.main.id
workdir = "/home/coder/projects"
enable_aibridge_proxy = true
aibridge_proxy_auth_url = module.aibridge-proxy.proxy_auth_url
aibridge_proxy_cert_path = module.aibridge-proxy.cert_path
}
```
Comment on lines +172 to +189
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we ensure that prxy is ready configured before the Copilot module starts up the Copilot CLI?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point. I tried setting this up with commits:

And seems to be working. But probably still needs more testing.


> [!NOTE]
> AI Bridge Proxy is a Premium Coder feature that requires [AI Bridge](https://coder.com/docs/ai-coder/ai-bridge) to be enabled.
> See the [AI Bridge Proxy setup guide](https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy/setup) for details on configuring the proxy on your Coder deployment.
> GitHub authentication is still required for Copilot as the proxy authenticates with AI Bridge using the Coder session token, but does not replace GitHub authentication.

> [!IMPORTANT]
> When using AI Bridge Proxy, enable [startup coordination](https://coder.com/docs/admin/templates/startup-coordination) by setting `CODER_AGENT_SOCKET_SERVER_ENABLED=true` in the workspace container environment.
> This ensures the Copilot module waits for the `aibridge-proxy` module to complete before starting. Without it, the Copilot start script may fail if the certificate is not yet available.

## Authentication

The module supports multiple authentication methods (in priority order):
Expand Down
113 changes: 113 additions & 0 deletions registry/coder-labs/modules/copilot/copilot.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,116 @@ run "app_slug_is_consistent" {
error_message = "module_dir_name should be '.copilot-module'"
}
}

run "aibridge_proxy_defaults" {
command = plan

variables {
agent_id = "test-agent"
workdir = "/home/coder"
}

assert {
condition = var.enable_aibridge_proxy == false
error_message = "enable_aibridge_proxy should default to false"
}

assert {
condition = var.aibridge_proxy_auth_url == ""
error_message = "aibridge_proxy_auth_url should default to empty"
}

assert {
condition = var.aibridge_proxy_cert_path == ""
error_message = "aibridge_proxy_cert_path should default to empty"
}
}

run "aibridge_proxy_enabled" {
command = plan

variables {
agent_id = "test-agent-aibridge-proxy"
workdir = "/home/coder"
enable_aibridge_proxy = true
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
}

assert {
condition = var.enable_aibridge_proxy == true
error_message = "AI Bridge Proxy should be enabled"
}

assert {
condition = var.aibridge_proxy_auth_url == "https://coder:mock-token@aiproxy.example.com"
error_message = "AI Bridge Proxy auth URL should match the input variable"
}

assert {
condition = var.aibridge_proxy_cert_path == "/tmp/aibridge-proxy/ca-cert.pem"
error_message = "AI Bridge Proxy cert path should match the input variable"
}
}

run "aibridge_proxy_validation_missing_proxy_auth_url" {
command = plan

variables {
agent_id = "test-agent-validation"
workdir = "/home/coder"
enable_aibridge_proxy = true
aibridge_proxy_auth_url = ""
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
}

expect_failures = [
var.enable_aibridge_proxy,
]
}

run "aibridge_proxy_validation_missing_cert_path" {
command = plan

variables {
agent_id = "test-agent-validation"
workdir = "/home/coder"
enable_aibridge_proxy = true
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
aibridge_proxy_cert_path = ""
}

expect_failures = [
var.enable_aibridge_proxy,
]
}

run "aibridge_proxy_with_copilot_config" {
command = plan

variables {
agent_id = "test-agent"
workdir = "/home/coder"
copilot_model = "gpt-5"
github_token = "ghp_test123"
allow_all_tools = true
enable_aibridge_proxy = true
aibridge_proxy_auth_url = "https://coder:mock-token@aiproxy.example.com"
aibridge_proxy_cert_path = "/tmp/aibridge-proxy/ca-cert.pem"
}

assert {
condition = var.enable_aibridge_proxy == true
error_message = "AI Bridge Proxy should be enabled"
}

assert {
condition = length(resource.coder_env.github_token) == 1
error_message = "github_token environment variable should be set alongside proxy"
}

assert {
condition = length(resource.coder_env.copilot_model) == 1
error_message = "copilot_model environment variable should be set alongside proxy"
}
}
34 changes: 33 additions & 1 deletion registry/coder-labs/modules/copilot/main.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.9"
required_providers {
coder = {
source = "coder/coder"
Expand Down Expand Up @@ -173,6 +173,35 @@ variable "post_install_script" {
default = null
}

variable "enable_aibridge_proxy" {
type = bool
description = "Route Copilot traffic through AI Bridge Proxy. See https://coder.com/docs/ai-coder/ai-bridge/ai-bridge-proxy"
default = false

validation {
condition = !var.enable_aibridge_proxy || length(var.aibridge_proxy_auth_url) > 0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Raise required Terraform version for this validation

This validation references another input (var.aibridge_proxy_auth_url) from inside the enable_aibridge_proxy variable block, which requires Terraform 1.9+ for cross-variable validation expressions. The module still declares required_version = ">= 1.0", so consumers on Terraform 1.0–1.8 will fail during configuration loading/plan even if enable_aibridge_proxy is left at its default. Please either bump required_version to >= 1.9 or move this check to a resource precondition that works with older versions.

Useful? React with 👍 / 👎.

error_message = "aibridge_proxy_auth_url is required when enable_aibridge_proxy is true."
}

validation {
condition = !var.enable_aibridge_proxy || length(var.aibridge_proxy_cert_path) > 0
error_message = "aibridge_proxy_cert_path is required when enable_aibridge_proxy is true."
}
}

variable "aibridge_proxy_auth_url" {
type = string
description = "AI Bridge Proxy URL with authentication. Use the proxy_auth_url output from the aibridge-proxy module."
default = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default = ""
default = null

sensitive = true
}

variable "aibridge_proxy_cert_path" {
type = string
description = "Path to the AI Bridge Proxy CA certificate. Use the cert_path output from the aibridge-proxy module."
default = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default = ""
default = null

}

data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}

Expand Down Expand Up @@ -279,6 +308,9 @@ module "agentapi" {
ARG_TRUSTED_DIRECTORIES='${join(",", var.trusted_directories)}' \
ARG_EXTERNAL_AUTH_ID='${var.external_auth_id}' \
ARG_RESUME_SESSION='${var.resume_session}' \
ARG_ENABLE_AIBRIDGE_PROXY='${var.enable_aibridge_proxy}' \
ARG_AIBRIDGE_PROXY_AUTH_URL='${var.aibridge_proxy_auth_url}' \
ARG_AIBRIDGE_PROXY_CERT_PATH='${var.aibridge_proxy_cert_path}' \
/tmp/start.sh
EOT

Expand Down
46 changes: 46 additions & 0 deletions registry/coder-labs/modules/copilot/scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ ARG_DENY_TOOLS=${ARG_DENY_TOOLS:-}
ARG_TRUSTED_DIRECTORIES=${ARG_TRUSTED_DIRECTORIES:-}
ARG_EXTERNAL_AUTH_ID=${ARG_EXTERNAL_AUTH_ID:-github}
ARG_RESUME_SESSION=${ARG_RESUME_SESSION:-true}
ARG_ENABLE_AIBRIDGE_PROXY=${ARG_ENABLE_AIBRIDGE_PROXY:-false}
ARG_AIBRIDGE_PROXY_AUTH_URL=${ARG_AIBRIDGE_PROXY_AUTH_URL:-}
ARG_AIBRIDGE_PROXY_CERT_PATH=${ARG_AIBRIDGE_PROXY_CERT_PATH:-}

validate_copilot_installation() {
if ! command_exists copilot; then
Expand Down Expand Up @@ -118,6 +121,48 @@ setup_github_authentication() {
return 0
}

setup_aibridge_proxy() {
if [ "$ARG_ENABLE_AIBRIDGE_PROXY" != "true" ]; then
return 0
fi

echo "Setting up AI Bridge Proxy..."

# Wait for the aibridge-proxy module to finish.
# Uses startup coordination to block until aibridge-proxy-setup signals completion.
if command -v coder > /dev/null 2>&1; then
coder exp sync want "copilot-aibridge" "aibridge-proxy-setup" || true
coder exp sync start "copilot-aibridge" || true
trap 'coder exp sync complete "copilot-aibridge" > /dev/null 2>&1 || true' EXIT
fi

if [ -z "$ARG_AIBRIDGE_PROXY_AUTH_URL" ]; then
echo "ERROR: AI Bridge Proxy is enabled but no proxy auth URL provided."
exit 1
fi

if [ -z "$ARG_AIBRIDGE_PROXY_CERT_PATH" ]; then
echo "ERROR: AI Bridge Proxy is enabled but no certificate path provided."
exit 1
fi

if [ ! -f "$ARG_AIBRIDGE_PROXY_CERT_PATH" ]; then
echo "ERROR: AI Bridge Proxy certificate not found at $ARG_AIBRIDGE_PROXY_CERT_PATH."
echo " Ensure the aibridge-proxy module has completed setup."
exit 1
fi

# Set proxy environment variables scoped to this process tree only.
# These are inherited by the agentapi/copilot process below,
# but do not affect other workspace processes, avoiding routing
# unnecessary traffic through the proxy.
export HTTPS_PROXY="$ARG_AIBRIDGE_PROXY_AUTH_URL"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing with HTTP_PROXY revealed a bug in AI Bridge Proxy (coder/internal#1351), where plain HTTP requests are always rejected with 407. This caused issues with agentapi, which makes HTTP requests to Coder endpoints that were being routed through the proxy. After thinking this through, we should probably recommend only setting HTTPS_PROXY since we only need to intercept requests to AI providers, which all use https. Not setting HTTP_PROXY means http traffic goes direct without passing through the proxy. However, since these two env variables are generally set together, it should still work with the AI Bridge Proxy.

export NODE_EXTRA_CA_CERTS="$ARG_AIBRIDGE_PROXY_CERT_PATH"

echo "✓ AI Bridge Proxy configured"
echo " CA certificate: $ARG_AIBRIDGE_PROXY_CERT_PATH"
}

start_agentapi() {
echo "Starting in directory: $ARG_WORKDIR"
cd "$ARG_WORKDIR"
Expand Down Expand Up @@ -157,5 +202,6 @@ start_agentapi() {
}

setup_github_authentication
setup_aibridge_proxy
validate_copilot_installation
start_agentapi