Scenarios
Claude with a Model from Vertex AI¶
This scenario demonstrates how to configure Claude Code to use a model hosted on Google Cloud Vertex AI instead of the default Anthropic API. This is useful when you need to use Claude through your Google Cloud organization's billing or compliance setup.
Prerequisites:
- A Google Cloud project with the Vertex AI API enabled and Claude models available
- Google Cloud credentials configured on your host machine (via
gcloud auth application-default login)
Step 1: Configure Claude agent settings
Create or edit ~/.kdn/config/agents.json to add the required environment variables and mount your Google Cloud credentials into the workspace:
{
"claude": {
"environment": [
{
"name": "CLAUDE_CODE_USE_VERTEX",
"value": "1"
},
{
"name": "ANTHROPIC_VERTEX_PROJECT_ID",
"value": "my-gcp-project-id"
},
{
"name": "CLOUD_ML_REGION",
"value": "my-region"
}
],
"mounts": [
{"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true}
]
}
}
Fields:
CLAUDE_CODE_USE_VERTEX- Set to1to instruct Claude Code to use Vertex AI instead of the Anthropic APIANTHROPIC_VERTEX_PROJECT_ID- Your Google Cloud project ID where Vertex AI is configuredCLOUD_ML_REGION- The region where Claude is available on Vertex AI (e.g.,us-east5)$HOME/.config/gcloudmounted read-only - Provides the workspace access to your application default credentials
Step 2: Register and start the workspace
# Register a workspace with the Podman runtime and Claude agent
kdn init /path/to/project --runtime podman --agent claude
# Start the workspace (using name or ID)
kdn start my-project
# Connect to the workspace — Claude Code will use Vertex AI automatically
kdn terminal my-project
When Claude Code starts, it detects ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION and routes all requests to Vertex AI using the mounted application default credentials.
Sharing local Claude settings (optional)
To reuse your host Claude Code settings (preferences, custom instructions, etc.) inside the workspace, add ~/.claude and ~/.claude.json to the mounts:
{
"claude": {
"environment": [
{
"name": "CLAUDE_CODE_USE_VERTEX",
"value": "1"
},
{
"name": "ANTHROPIC_VERTEX_PROJECT_ID",
"value": "my-gcp-project-id"
},
{
"name": "CLOUD_ML_REGION",
"value": "my-region"
}
],
"mounts": [
{"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true},
{"host": "$HOME/.claude", "target": "$HOME/.claude"},
{"host": "$HOME/.claude.json", "target": "$HOME/.claude.json"}
]
}
}
~/.claude contains your Claude Code configuration directory (skills, settings) and ~/.claude.json stores your account and preferences. These are mounted read-write so that changes made inside the workspace (e.g., updated preferences) are persisted back to your host.
Notes:
- Run
gcloud auth application-default loginon your host machine before starting the workspace to ensure valid credentials are available - The
$HOME/.config/gcloudmount is read-only to prevent the workspace from modifying your host credentials - No
ANTHROPIC_API_KEYis needed when using Vertex AI — credentials are provided via the mounted gcloud configuration - To pin a specific Claude model, use
--modelflag duringinit(e.g.,--model claude-sonnet-4-20250514), which takes precedence over any model in default settings, or add anANTHROPIC_MODELenvironment variable (e.g.,"claude-opus-4-5")
Starting Claude with Default Settings¶
This scenario demonstrates how to pre-configure Claude Code's settings so that when it starts inside a workspace, it skips the interactive onboarding flow and uses your preferred defaults. kdn automatically handles the onboarding flags, and you can optionally customize other settings like theme preferences.
Automatic Onboarding Skip
When you register a workspace with the Claude agent, kdn automatically: - Sets hasCompletedOnboarding: true to skip the first-run wizard - Sets hasTrustDialogAccepted: true for the workspace sources directory (the exact path is determined by the runtime)
This happens automatically for every Claude workspace — no manual configuration required.
Optional: Customize Theme and Other Settings
If you want to customize Claude's theme or other preferences, create default settings:
Step 1: Create the agent settings directory
Step 2: Write the default Claude settings file
Fields:
theme- The UI theme for Claude Code (e.g.,"dark","light","dark-daltonized")
You don't need to set hasCompletedOnboarding or hasTrustDialogAccepted — kdn adds these automatically when creating the workspace.
Step 3: Register and start the workspace
# Register a workspace — the settings file is embedded in the container image
kdn init /path/to/project --runtime podman --agent claude
# Start the workspace (using name or ID)
kdn start my-project
# Connect — Claude Code starts directly without onboarding
kdn terminal my-project
When init runs, kdn: 1. Reads all files from ~/.kdn/config/claude/ (e.g., your theme preferences) 2. Automatically adds hasCompletedOnboarding: true and marks the workspace sources directory as trusted (the path is determined by the runtime) 3. Copies the final merged settings into the container image at /home/agent/.claude.json
Claude Code finds this file on startup and skips onboarding.
Notes:
- Onboarding is skipped automatically — even if you don't create any settings files, kdn ensures Claude starts without prompts
- The settings are baked into the container image at
inittime, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect - Any file placed under
~/.kdn/config/claude/is copied into the container home directory, preserving the directory structure (e.g.,~/.kdn/config/claude/.some-tool/configbecomes/home/agent/.some-tool/configinside the container) - This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local
~/.claudedirectory is not exposed inside the container - To apply changes to the settings, remove and re-register the workspace:
kdn remove <workspace-id>thenkdn initagain
Using Goose Agent with a Model from Vertex AI¶
This scenario demonstrates how to configure the Goose agent in a kdn workspace using Vertex AI as the backend, covering credential injection, sharing your local gcloud configuration, and pre-configuring the default model.
Authenticating with Vertex AI¶
Goose can use Google Cloud Vertex AI as its backend. Authentication relies on Application Default Credentials (ADC) provided by the gcloud CLI. Mount your local ~/.config/gcloud directory to make your host credentials available inside the workspace, and set the GCP_PROJECT_ID, GCP_LOCATION, and GOOSE_PROVIDER environment variables to tell Goose which project and region to use.
Create or edit ~/.kdn/config/agents.json:
{
"goose": {
"environment": [
{
"name": "GOOSE_PROVIDER",
"value": "gcp_vertex_ai"
},
{
"name": "GCP_PROJECT_ID",
"value": "my-gcp-project"
},
{
"name": "GCP_LOCATION",
"value": "my-region"
}
],
"mounts": [
{"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true}
]
}
}
The ~/.config/gcloud directory contains your Application Default Credentials and active account configuration. It is mounted read-only so that credentials are available inside the workspace while the host configuration remains unmodified.
Then register and start the workspace:
# Register a workspace with the Podman runtime and Goose agent
kdn init /path/to/project --runtime podman --agent goose
# Start the workspace
kdn start my-project
# Connect — Goose starts with Vertex AI configured
kdn terminal my-project
Sharing Local Goose Settings¶
To reuse your host Goose settings (model preferences, provider configuration, etc.) inside the workspace, mount the ~/.config/goose directory.
Edit ~/.kdn/config/agents.json to add the mount alongside the Vertex AI configuration:
{
"goose": {
"environment": [
{
"name": "GOOSE_PROVIDER",
"value": "gcp_vertex_ai"
},
{
"name": "GCP_PROJECT_ID",
"value": "my-gcp-project"
},
{
"name": "GCP_LOCATION",
"value": "my-region"
}
],
"mounts": [
{"host": "$HOME/.config/gcloud", "target": "$HOME/.config/gcloud", "ro": true},
{"host": "$HOME/.config/goose", "target": "$HOME/.config/goose"}
]
}
}
The ~/.config/goose directory contains your Goose configuration (settings, model preferences, etc.). It is mounted read-write so that changes made inside the workspace are persisted back to your host.
Using Default Settings¶
If you want to pre-configure Goose with default settings without exposing your local ~/.config/goose directory inside the container, create default settings files that are baked into the container image at workspace registration time. This is an alternative to mounting your local Goose settings — use one approach or the other, not both.
Automatic Onboarding Skip
When you register a workspace with the Goose agent, kdn automatically sets GOOSE_TELEMETRY_ENABLED to false in the Goose config file if it is not already defined, so Goose skips its telemetry prompt on first launch.
Step 1: Create the agent settings directory
Step 2: Write the default Goose settings file
As an example, you can configure the model and enable telemetry:
cat > ~/.kdn/config/goose/.config/goose/config.yaml << 'EOF'
GOOSE_MODEL: "claude-sonnet-4-6"
GOOSE_TELEMETRY_ENABLED: true
EOF
Fields:
GOOSE_MODEL- The model identifier Goose uses for its AI interactions. Alternatively, use--modelflag duringinitto set this (the flag takes precedence over this setting)GOOSE_TELEMETRY_ENABLED- Whether Goose sends usage telemetry; set totrueto opt in, or omit to have kdn default it tofalse
Step 3: Register and start the workspace
# Register a workspace — the settings file is embedded in the container image
kdn init /path/to/project --runtime podman --agent goose
# Start the workspace
kdn start my-project
# Connect — Goose starts with the configured provider and model
kdn terminal my-project
When init runs, kdn: 1. Reads all files from ~/.kdn/config/goose/ (e.g., your provider and model settings) 2. Automatically sets GOOSE_TELEMETRY_ENABLED: false in .config/goose/config.yaml if the key is not already defined 3. Copies the final settings into the container image at /home/agent/.config/goose/config.yaml
Goose finds this file on startup and uses the pre-configured settings without prompting.
Notes:
- Telemetry is disabled automatically — even if you don't create any settings files, kdn ensures Goose starts without the telemetry prompt
- If you prefer to enable telemetry, set
GOOSE_TELEMETRY_ENABLED: truein~/.kdn/config/goose/.config/goose/config.yaml - The settings are baked into the container image at
inittime, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect - Any file placed under
~/.kdn/config/goose/is copied into the container home directory, preserving the directory structure (e.g.,~/.kdn/config/goose/.config/goose/config.yamlbecomes/home/agent/.config/goose/config.yamlinside the container) - This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local
~/.config/goosedirectory is not exposed inside the container - To apply changes to the settings, remove and re-register the workspace:
kdn remove <workspace-id>thenkdn initagain
Using Cursor CLI Agent¶
This scenario demonstrates how to configure the Cursor agent in a kdn workspace, covering API key injection, sharing your local Cursor settings, and pre-configuring the default model.
Defining the Cursor API Key via a Secret¶
Cursor requires a CURSOR_API_KEY environment variable to authenticate with the Cursor service. Rather than embedding the key as plain text, use the secret mechanism to keep credentials out of your configuration files.
Step 1: Create the secret
For the Podman runtime, create the secret once on your host machine using podman secret create:
Step 2: Reference the secret in agent configuration
Create or edit ~/.kdn/config/agents.json to inject the secret as an environment variable for the cursor agent:
Step 3: Register and start the workspace
# Register a workspace with the Podman runtime and Cursor agent
kdn init /path/to/project --runtime podman --agent cursor
# Start the workspace
kdn start my-project
# Connect — Cursor starts with the API key available
kdn terminal my-project
The secret name (cursor-api-key) must match the secret field value in your configuration. At workspace creation time, kdn passes the secret to Podman, which injects it as the CURSOR_API_KEY environment variable inside the container.
Sharing Local Cursor Settings¶
To reuse your host Cursor settings (preferences, keybindings, extensions configuration, etc.) inside the workspace, mount the ~/.cursor directory.
Edit ~/.kdn/config/agents.json to add the mount:
{
"cursor": {
"environment": [
{
"name": "CURSOR_API_KEY",
"secret": "cursor-api-key"
}
],
"mounts": [
{"host": "$HOME/.cursor", "target": "$HOME/.cursor"}
]
}
}
The ~/.cursor directory contains your Cursor configuration (settings, model preferences, etc.). It is mounted read-write so that changes made inside the workspace are persisted back to your host.
Using Default Settings¶
If you want to pre-configure Cursor with default settings without exposing your local ~/.cursor directory inside the container, create default settings files that are baked into the container image at workspace registration time. This is an alternative to mounting your local Cursor settings — use one approach or the other, not both.
Automatic Onboarding Skip
When you register a workspace with the Cursor agent, kdn automatically creates a .workspace-trusted file in the Cursor projects directory for the workspace sources path, so Cursor skips its workspace trust dialog on first launch.
Step 1: Configure the agent environment
Create or edit ~/.kdn/config/agents.json to inject the API key. No mount is needed since settings are baked in:
Step 2: Create the agent settings directory
Step 3: Write the default Cursor settings file (optional)
You can optionally pre-configure Cursor with additional settings by creating a cli-config.json file:
cat > ~/.kdn/config/cursor/.cursor/cli-config.json << 'EOF'
{
"model": {
"modelId": "my-preferred-model",
"displayModelId": "my-preferred-model",
"displayName": "My Preferred Model",
"displayNameShort": "My Model",
"maxMode": false
},
"hasChangedDefaultModel": true
}
EOF
Fields:
model.modelId- The model identifier used internally by Cursormodel.displayName/model.displayNameShort- Human-readable model names shown in the UImodel.maxMode- Whether to enable max mode for this modelhasChangedDefaultModel- Tells Cursor that the model selection is intentional and should not prompt the user to choose a model
Note: Using the --model flag during init is the preferred way to configure the model, as it automatically sets all model fields correctly.
Step 4: Register and start the workspace
# Register a workspace with a specific model using the --model flag (recommended)
kdn init /path/to/project --runtime podman --agent cursor --model my-model-id
# Or register without --model to use settings from cli-config.json
kdn init /path/to/project --runtime podman --agent cursor
# Start the workspace
kdn start my-project
# Connect — Cursor starts with the configured model
kdn terminal my-project
When init runs, kdn: 1. Reads all files from ~/.kdn/config/cursor/ (e.g., your settings) 2. If --model is specified, updates cli-config.json with the model configuration (takes precedence over any existing model in settings files) 3. Automatically creates the workspace trust file so Cursor skips its trust dialog 4. Copies the final settings into the container image at /home/agent/.cursor/cli-config.json
Cursor finds this file on startup and uses the pre-configured model without prompting.
Notes:
- Model configuration: Use
--modelflag duringinitto set the model (e.g.,--model my-model-id). This takes precedence over any model defined in settings files - The settings are baked into the container image at
inittime, not mounted at runtime — changes to the files on the host require re-registering the workspace to take effect - Any file placed under
~/.kdn/config/cursor/is copied into the container home directory, preserving the directory structure (e.g.,~/.kdn/config/cursor/.cursor/cli-config.jsonbecomes/home/agent/.cursor/cli-config.jsoninside the container) - To apply changes to the settings, remove and re-register the workspace:
kdn remove <workspace-id>thenkdn initagain - This approach keeps your workspace self-contained — other developers using the same project are not affected, and your local
~/.cursordirectory is not exposed inside the container - Do not combine this approach with the
~/.cursormount from the previous section — the mounted directory would override the baked-in defaults at runtime
Sharing a GitHub Token¶
This scenario demonstrates how to make a GitHub token available inside workspaces using the multi-level configuration system — either globally for all projects or scoped to a specific project.
For all projects
Edit ~/.kdn/config/projects.json and add the token and your git configuration under the global "" key:
{
"": {
"environment": [
{
"name": "GH_TOKEN",
"secret": "github-token"
}
],
"mounts": [
{"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig", "ro": true}
]
}
}
The GH_TOKEN variable is automatically picked up by the gh CLI and other GitHub-aware tools running inside the workspace. The $HOME/.gitconfig mount makes your git identity (name, email, aliases, etc.) available to git commands run by the agent.
For a specific project
Use the project identifier as the key instead. The identifier is the git remote URL (without .git) as detected by kdn during init:
{
"https://github.com/my-org/my-repo/": {
"environment": [
{
"name": "GH_TOKEN",
"secret": "github-token"
}
]
}
}
This injects the token only when working on workspaces that belong to https://github.com/my-org/my-repo/, leaving other projects unaffected.
Both at once
You can combine global and project-specific entries in the same file. The project-specific value takes precedence over the global one if both define the same variable:
{
"": {
"environment": [
{
"name": "GH_TOKEN",
"secret": "github-token-default"
}
]
},
"https://github.com/my-org/my-private-repo/": {
"environment": [
{
"name": "GH_TOKEN",
"secret": "github-token-private"
}
]
}
}
Creating the secret
How secrets are created depends on the runtime being used. The secret field value is the name under which the secret is registered with that runtime.
For the Podman runtime, create the secret once on your host machine using podman secret create before registering the workspace:
# Create the secret from an environment variable
echo "$GITHUB_TOKEN" | podman secret create github-token -
# Or create it from a file
podman secret create github-token /path/to/token-file
The secret name (github-token here) must match the secret field value in your configuration. At workspace creation time, kdn passes --secret github-token,type=env,target=GH_TOKEN to Podman, which injects the secret value as the GH_TOKEN environment variable inside the container.
Podman secrets are stored locally on the host and never written to the container image.
Notes:
- The
secretfield references a secret by name rather than embedding the token value directly, keeping credentials out of the configuration file - The project identifier used as the key must match what kdn detected during
init— runkdn list -o jsonto see the project field for each registered workspace - Configuration changes in
projects.jsontake effect the next time you runkdn initfor that workspace; already-registered workspaces need to be removed and re-registered
Working with Git Worktrees¶
This scenario demonstrates how to run multiple agents in parallel, each working on a different branch of the same repository. Git worktrees allow each branch to live in its own directory, so each agent gets its own isolated workspace.
Step 1: Clone the repository
Step 2: Create a worktree for each feature branch
cd /path/to/my-project/main
git worktree add ../feature-a feature-a
git worktree add ../feature-b feature-b
This results in the following layout:
/path/to/my-project/
├── main/ ← main branch (original clone)
├── feature-a/ ← feature-a branch (worktree)
└── feature-b/ ← feature-b branch (worktree)
Step 3: Configure the main branch mount in your local project config
If you want the agents to have access to the main branch (e.g., to compare changes), add the mount in ~/.kdn/config/projects.json under the project identifier. This keeps the configuration on your machine only — not all developers of the project may use worktrees, so it does not belong in the repository's .kaiden/workspace.json.
{
"https://github.com/my-org/my-repo/": {
"mounts": [
{"host": "$SOURCES/../main", "target": "$SOURCES/../main"}
]
}
}
$SOURCES expands to the workspace sources directory (e.g., /path/to/my-project/feature-a), so $SOURCES/../main resolves to /path/to/my-project/main on both the host and inside the container.
Step 4: Register a workspace for each worktree
kdn init /path/to/my-project/feature-a --runtime podman --agent claude
kdn init /path/to/my-project/feature-b --runtime podman --agent claude
Step 5: Start and connect to each workspace independently
# Start both workspaces (using names or IDs)
kdn start feature-a
kdn start feature-b
# Connect to each agent in separate terminals
kdn terminal feature-a
kdn terminal feature-b
Each agent runs independently in its own container, operating on its own branch without interfering with the other.
Notes:
- Each worktree shares the same
.gitdirectory, so agents can run git commands that are branch-aware - Workspaces for different worktrees of the same repository share the same project identifier (derived from the git remote URL), so the mount defined in
projects.jsonautomatically applies to all of them
Managing Workspaces from a UI or Programmatically¶
This scenario demonstrates how to manage workspaces programmatically using JSON output, which is ideal for UIs, scripts, or automation tools. All commands support the --output json (or -o json) flag for machine-readable output.
Step 1: Check existing workspaces
Exit code: 0 (success, but no workspaces registered)
Step 2: Register a new workspace
Exit code: 0 (success)
Step 3: Register with verbose output to get full details
{
"id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
"name": "another-project",
"agent": "claude",
"project": "/absolute/path/to/another-project",
"state": "stopped",
"paths": {
"source": "/absolute/path/to/another-project",
"configuration": "/absolute/path/to/another-project/.kaiden"
}
}
Exit code: 0 (success)
Step 3a: Register and start immediately with auto-start flag
Exit code: 0 (success, workspace is running)
Step 4: List all workspaces
{
"items": [
{
"id": "2c5f16046476be368fcada501ac6cdc6bbd34ea80eb9ceb635530c0af64681ea",
"name": "project",
"agent": "claude",
"project": "/absolute/path/to/project",
"state": "running",
"paths": {
"source": "/absolute/path/to/project",
"configuration": "/absolute/path/to/project/.kaiden"
}
},
{
"id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
"name": "another-project",
"agent": "claude",
"project": "/absolute/path/to/another-project",
"state": "stopped",
"paths": {
"source": "/absolute/path/to/another-project",
"configuration": "/absolute/path/to/another-project/.kaiden"
}
}
]
}
Exit code: 0 (success)
Step 5: Start a workspace
Exit code: 0 (success)
Step 6: Stop a workspace
Exit code: 0 (success)
Step 7: Remove a workspace
Exit code: 0 (success)
Step 8: Verify removal
{
"items": [
{
"id": "f6e5d4c3b2a1098765432109876543210987654321098765432109876543210a",
"name": "another-project",
"agent": "claude",
"project": "/absolute/path/to/another-project",
"state": "stopped",
"paths": {
"source": "/absolute/path/to/another-project",
"configuration": "/absolute/path/to/another-project/.kaiden"
}
}
]
}
Exit code: 0 (success)
Error Handling¶
All errors are returned in JSON format when using --output json, with the error written to stdout (not stderr) and a non-zero exit code.
Error: Non-existent directory
Exit code: 1 (error)
Error: Workspace not found
Exit code: 1 (error)
Best Practices for Programmatic Usage¶
- Always check the exit code to determine success (0) or failure (non-zero)
- Parse stdout for JSON output in both success and error cases
- Use verbose mode with init (
-v) when you need full workspace details immediately after creation - Handle both success and error JSON structures in your code:
- Success responses have specific fields (e.g.,
id,items,name,paths) - Error responses always have an
errorfield
Example script pattern:
#!/bin/bash
# Register a workspace
output=$(kdn init /path/to/project --runtime podman --agent claude -o json)
exit_code=$?
if [ $exit_code -eq 0 ]; then
workspace_id=$(echo "$output" | jq -r '.id')
echo "Workspace created: $workspace_id"
else
error_msg=$(echo "$output" | jq -r '.error')
echo "Error: $error_msg"
exit 1
fi