fix(registry/modules/claude-code): default subdomain to false (#419)

Relates to https://github.com/coder/coder/issues/18779

By default, we set `subdomain = true`. Most folks testing this out don't
have a wildcard subdomain setup. This switches to path-based behaviour
by default and adds a note to the troubleshooting section.
This commit is contained in:
Cian Johnston 2025-09-15 13:09:12 +01:00 committed by GitHub
parent 213aabb3b0
commit cb990bbee0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 9 deletions

View File

@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "2.2.0" version = "2.2.1"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder" folder = "/home/coder"
install_claude_code = true install_claude_code = true
@ -26,6 +26,9 @@ module "claude-code" {
> this enables more functionality, it also means Claude Code can potentially execute commands with the same privileges as > this enables more functionality, it also means Claude Code can potentially execute commands with the same privileges as
> the user running it. Use this module _only_ in trusted environments and be aware of the security implications. > the user running it. Use this module _only_ in trusted environments and be aware of the security implications.
> [!NOTE]
> By default, this module is configured to run the embedded chat interface as a path-based application. In production, we recommend that you configure a [wildcard access URL](https://coder.com/docs/admin/setup#wildcard-access-url) and set `subdomain = true`. See [here](https://coder.com/docs/tutorials/best-practices/security-best-practices#disable-path-based-apps) for more details.
## Prerequisites ## Prerequisites
- You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template - You must add the [Coder Login](https://registry.coder.com/modules/coder-login) module to your template
@ -83,7 +86,7 @@ resource "coder_agent" "main" {
module "claude-code" { module "claude-code" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "2.2.0" version = "2.2.1"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder" folder = "/home/coder"
install_claude_code = true install_claude_code = true
@ -101,7 +104,7 @@ Run Claude Code as a standalone app in your workspace. This will install Claude
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "2.2.0" version = "2.2.1"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
folder = "/home/coder" folder = "/home/coder"
install_claude_code = true install_claude_code = true

View File

@ -3,6 +3,7 @@ import {
afterEach, afterEach,
expect, expect,
describe, describe,
it,
setDefaultTimeout, setDefaultTimeout,
beforeAll, beforeAll,
} from "bun:test"; } from "bun:test";
@ -100,6 +101,7 @@ const writeAgentAPIMockControl = async ({
interface SetupProps { interface SetupProps {
skipAgentAPIMock?: boolean; skipAgentAPIMock?: boolean;
skipClaudeMock?: boolean; skipClaudeMock?: boolean;
extraVars?: Record<string, string>;
} }
const projectDir = "/home/coder/project"; const projectDir = "/home/coder/project";
@ -112,6 +114,7 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
install_claude_code: "false", install_claude_code: "false",
agentapi_version: "preview", agentapi_version: "preview",
folder: projectDir, folder: projectDir,
...props?.extraVars,
}, },
}); });
await execContainer(id, ["bash", "-c", `mkdir -p '${projectDir}'`]); await execContainer(id, ["bash", "-c", `mkdir -p '${projectDir}'`]);
@ -335,6 +338,36 @@ describe("claude-code", async () => {
id, id,
"/home/coder/agentapi-mock.log", "/home/coder/agentapi-mock.log",
); );
expect(agentApiStartLog).toContain("AGENTAPI_ALLOWED_HOSTS: *"); expect(agentApiStartLog).toContain("AGENTAPI_ALLOWED_HOSTS=*");
});
describe("subdomain", async () => {
it("sets AGENTAPI_CHAT_BASE_PATH when false", async () => {
const { id } = await setup();
const respModuleScript = await execModuleScript(id);
expect(respModuleScript.exitCode).toBe(0);
await expectAgentAPIStarted(id);
const agentApiStartLog = await readFileContainer(
id,
"/home/coder/agentapi-mock.log",
);
expect(agentApiStartLog).toContain(
"AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/ccw/chat",
);
});
it("does not set AGENTAPI_CHAT_BASE_PATH when true", async () => {
const { id } = await setup({
extraVars: { subdomain: "true" },
});
const respModuleScript = await execModuleScript(id);
expect(respModuleScript.exitCode).toBe(0);
await expectAgentAPIStarted(id);
const agentApiStartLog = await readFileContainer(
id,
"/home/coder/agentapi-mock.log",
);
expect(agentApiStartLog).toMatch(/AGENTAPI_CHAT_BASE_PATH=$/m);
});
}); });
}); });

View File

@ -106,7 +106,7 @@ variable "agentapi_version" {
variable "subdomain" { variable "subdomain" {
type = bool type = bool
description = "Whether to use a subdomain for the Claude Code app." description = "Whether to use a subdomain for the Claude Code app."
default = true default = false
} }
locals { locals {

View File

@ -22,7 +22,8 @@ if (
fs.writeFileSync( fs.writeFileSync(
"/home/coder/agentapi-mock.log", "/home/coder/agentapi-mock.log",
`AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`, `AGENTAPI_ALLOWED_HOSTS=${process.env.AGENTAPI_ALLOWED_HOSTS}
AGENTAPI_CHAT_BASE_PATH=${process.env.AGENTAPI_CHAT_BASE_PATH}`,
); );
console.log(`starting server on port ${port}`); console.log(`starting server on port ${port}`);