Workspace Configuration
Each workspace can optionally include a configuration file that customizes the environment, mount, and skills behavior for that specific workspace. The configuration is stored in a workspace.json file within the workspace's configuration directory (typically .kaiden in the sources directory).
Configuration File Location¶
By default, workspace configuration is stored at:
The configuration directory (containing workspace.json) can be customized using the --workspace-configuration flag when registering a workspace with init. The flag accepts a directory path, not the file path itself.
Configuration Structure¶
The workspace.json file uses a nested JSON structure:
{
"environment": [
{
"name": "DEBUG",
"value": "true"
},
{
"name": "API_KEY",
"secret": "github-token"
}
],
"mounts": [
{"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
{"host": "$HOME/.ssh", "target": "$HOME/.ssh"},
{"host": "/absolute/path/to/data", "target": "/workspace/data"}
],
"skills": [
"/absolute/path/to/commit-skill",
"$HOME/review-skill"
],
"mcp": {
"commands": [
{
"name": "my-local-tool",
"command": "python3",
"args": ["/workspace/sources/scripts/mcp_server.py"],
"env": {"DEBUG": "true"}
}
],
"servers": [
{
"name": "remote-api",
"url": "https://api.example.com/mcp",
"headers": {"Authorization": "Bearer token123"}
}
]
},
"network": {
"mode": "deny",
"hosts": ["api.github.com"]
},
"secrets": ["my-github-token", "my-api-key"],
"features": {
"ghcr.io/devcontainers/features/go:1": {"version": "1.23"},
"./tools/my-feature": {}
}
}
Environment Variables¶
Define environment variables that will be set in the workspace runtime environment.
Structure:
{
"environment": [
{
"name": "VAR_NAME",
"value": "hardcoded-value"
},
{
"name": "SECRET_VAR",
"secret": "secret-reference"
}
]
}
Fields: - name (required) - Environment variable name - Must be a valid Unix environment variable name - Must start with a letter or underscore - Can contain letters, digits, and underscores - value (optional) - Hardcoded value for the variable - Mutually exclusive with secret - Empty strings are allowed - secret (optional) - Reference to a runtime secret (e.g., a Podman secret) containing the value; the runtime injects it as an environment variable inside the workspace - Mutually exclusive with value - Cannot be empty - Use this when a local tool inside the workspace needs the credential via an environment variable - For credentials used in outbound network requests, use the secrets list field and kdn secret create instead — those are injected as HTTP headers by OneCLI
Validation Rules: - Variable name cannot be empty - Exactly one of value or secret must be defined - Variable names must follow Unix conventions (e.g., DEBUG, API_KEY, MY_VAR_123) - Invalid names include those starting with digits (1INVALID) or containing special characters (INVALID-NAME, INVALID@NAME)
Mount Paths¶
Configure additional directories to mount in the workspace runtime.
Structure:
{
"mounts": [
{"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
{"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
{"host": "/absolute/path/to/data", "target": "/workspace/data", "ro": true}
]
}
Fields: - host (required) - Path on the host filesystem to mount - target (required) - Path inside the container where the host path is mounted - ro (optional) - Mount as read-only (default: false)
Path Variables:
Both host and target support the following variables: - $SOURCES - Expands to the workspace sources directory on the host, or /workspace/sources in the container - $HOME - Expands to the user's home directory on the host, or /home/agent in the container
Paths can also be absolute (e.g., /absolute/path).
Validation Rules: - host and target cannot be empty - Each path must be absolute or start with $SOURCES or $HOME - $SOURCES-based container targets must not escape above /workspace - $HOME-based container targets must not escape above /home/agent
Skills¶
Configure skill directories to make available to the agent inside the workspace.
Each entry is a path to a directory on the host that contains a single skill — a SKILL.md file and any related files. The directory is mounted read-only inside the agent's skills directory using the directory's basename as the skill name, allowing the agent to discover and use it.
Structure:
Fields: - Each entry is a path to a host directory containing a single skill (SKILL.md and related files)
Path Variables:
Skills paths support the following variables: - $HOME - Expands to the user's home directory on the host
Paths can also be absolute (e.g., /absolute/path/to/commit-skill).
Mount targets per agent:
Each skill directory is mounted read-only under the agent's skills directory inside the container. The subdirectory name matches the basename of the host path:
| Agent | Mount target |
|---|---|
| Claude Code | ~/.claude/skills/<basename>/ |
| Goose | ~/.agents/skills/<basename>/ |
| Cursor | ~/.cursor/skills/<basename>/ |
| OpenCode | ~/.opencode/skills/<basename>/ |
| OpenClaw | ~/.openclaw/skills/<basename>/ |
For example, a skills path of /home/user/commit-skill is mounted at ~/.claude/skills/commit-skill/ for Claude Code, making the skill discoverable by the agent.
Validation Rules: - Each path cannot be empty - Each path must be an absolute path or start with $HOME - $SOURCES-based paths are not supported for skills
MCP Servers¶
Configure MCP (Model Context Protocol) servers to give the agent access to external tools and data sources. Two types are supported:
- Commands — local MCP servers launched by the agent inside the workspace using stdio transport
- Servers — remote MCP servers accessed over SSE (Server-Sent Events)
Structure:
{
"mcp": {
"commands": [
{
"name": "my-tool",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace/sources"],
"env": {"NODE_ENV": "production"}
}
],
"servers": [
{
"name": "remote-api",
"url": "https://api.example.com/mcp",
"headers": {"Authorization": "Bearer token123"}
}
]
}
}
Command fields (commands[*]): - name (required) - Unique name for this MCP server - command (required) - Executable to run (e.g., npx, python3, node) - args (optional) - Arguments to pass to the command - env (optional) - Environment variables to set for the process
Server fields (servers[*]): - name (required) - Unique name for this MCP server - url (required) - SSE endpoint URL of the remote MCP server - headers (optional) - HTTP headers to include in requests (e.g., Authorization)
Agent support:
MCP server configuration is applied to agents that support it at workspace registration time. For Claude Code, both command-based and URL-based MCP servers are written to ~/.claude.json under the top-level mcpServers key (user scope), so they are available across all projects inside the workspace.
Validation Rules: - name cannot be empty and must be unique across both commands and servers combined — a command and a server cannot share the same name, since all entries map to the same flat mcpServers key in the agent settings - command cannot be empty for command entries - url cannot be empty for server entries
Network Access¶
Control network access for the workspace. By default, network access is denied (deny mode). You can allow all network access or restrict it to specific hosts.
Structure:
Fields: - mode (optional) - Network access mode - "allow" - Permits all network access (no restrictions) - "deny" - Blocks all outbound network access from the workspace agent, except for the hosts listed in hosts and the hosts associated with configured secrets - hosts (optional) - List of hostnames to allow when in deny mode - Only meaningful when mode is "deny" - Each entry must be a non-empty string - Omitting hosts (or leaving it empty) is valid: the workspace is fully isolated, with no outbound access permitted unless secrets contribute hosts
Automatic host injection: When mode is "deny", kdn automatically adds the required hosts to the allowed list from two sources — no explicit hosts entry is needed for either:
- Secrets: The hosts associated with each configured secret are added automatically. For example, a
githubsecret automatically allowsapi.github.com. - Credentials: The hosts required by each intercepted credential mount are added automatically. For example, mounting
$HOME/.config/gcloudautomatically allowsoauth2.googleapis.comandaiplatform.googleapis.com.
Validation Rules: - If mode is set, it must be either "allow" or "deny" - If mode is "allow", hosts must not be set (they are meaningless in allow mode) - Host entries cannot be empty strings
Secrets¶
Configure secrets to inject into the workspace. Each entry is the name of a secret previously created with kdn secret create. At workspace creation time, kdn looks up the secret value from the system keychain and provisions it into the workspace via OneCLI, which injects it as an HTTP header into matching outbound requests. This is distinct from the secret field in environment variables, which references runtime secrets by name for environment variable injection.
When network.mode is "deny", the hosts associated with each secret are automatically added to the allowed list — you do not need to duplicate them under network.hosts.
Structure:
Fields: - Each entry is a secret name (string) referencing a secret stored with kdn secret create
Validation Rules: - Secret names cannot be empty - Duplicate names within the list are rejected
Dev Container Features¶
Install Dev Container Features into the workspace image at build time. Features are reusable environment components that add languages, runtimes, and tools to your workspace.
Structure:
Each key is a feature ID — either an OCI reference (ghcr.io/org/repo/feature:tag) or a relative path to a local directory (./my-feature). Each value is a map of options that override the feature's defaults; use an empty object {} to accept all defaults.
Fields: - Feature ID (required) — OCI reference or relative path to a local directory - Options (required, can be empty) — key/value pairs that customise the feature
Validation Rules: - Feature IDs must be OCI references or relative paths (./…); https:// tarball URIs are not supported - Local paths are resolved relative to the workspace configuration directory (e.g. .kaiden/)
Example — install Go and Node.js:
{
"features": {
"ghcr.io/devcontainers/features/go:1": {"version": "1.23"},
"ghcr.io/devcontainers/features/node:1": {"version": "20"}
}
}
Example — use a local feature:
Port Forwarding¶
Forward ports from the workspace to the host so that services running inside the workspace are reachable from the host machine.
Structure:
Fields: - Each entry is an integer workspace port to forward
At workspace creation time, kdn allocates a free host port for each requested workspace port and binds it to 127.0.0.1. The assigned host ports are reported in the forwards field of the workspace JSON output (kdn list --output json / kdn workspace list --output json). Use kdn open / kdn workspace open to open a forwarded port directly in the browser:
{
"forwards": [
{"bind": "127.0.0.1", "port": 54321, "target": 8080},
{"bind": "127.0.0.1", "port": 54322, "target": 3000}
]
}
Merging behaviour: When configuration is merged across levels, port lists are union-merged and deduplicated (base ports first, then override ports with duplicates removed).
Configuration Validation¶
When you register a workspace with kdn init, the configuration is automatically validated. If workspace.json exists and contains invalid data, the registration will fail with a descriptive error message.
Example - Invalid configuration (both value and secret set):
Error: workspace configuration validation failed: invalid workspace configuration:
environment variable "API_KEY" (index 0) has both value and secret set
Example - Invalid configuration (missing host in mount):
Error: workspace configuration validation failed: invalid workspace configuration:
mount at index 0 is missing host
Configuration Examples¶
Basic environment variables:
{
"environment": [
{
"name": "NODE_ENV",
"value": "development"
},
{
"name": "DEBUG",
"value": "true"
}
]
}
Using secrets:
git worktree:
Sharing user configurations:
{
"mounts": [
{"host": "$HOME/.claude", "target": "$HOME/.claude"},
{"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"},
{"host": "$HOME/.kube/config", "target": "$HOME/.kube/config", "ro": true}
]
}
MCP command server (local tool):
{
"mcp": {
"commands": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace/sources"]
}
]
}
}
MCP remote server with authentication:
{
"mcp": {
"servers": [
{
"name": "company-api",
"url": "https://mcp.company.com/sse",
"headers": {"Authorization": "Bearer mytoken"}
}
]
}
}
Network access - allow all:
Network access - deny with exceptions:
Network access - fully isolated (deny, no hosts):
Network access - deny with secrets (hosts inferred automatically):
The my-github-token secret (type github) automatically allows api.github.com without any hosts entry.
Secrets:
Complete configuration:
{
"environment": [
{
"name": "NODE_ENV",
"value": "development"
},
{
"name": "DATABASE_URL",
"secret": "local-db-url"
}
],
"mounts": [
{"host": "$SOURCES/../main", "target": "$SOURCES/../main"},
{"host": "$HOME/.claude", "target": "$HOME/.claude"},
{"host": "$HOME/.gitconfig", "target": "$HOME/.gitconfig"}
],
"mcp": {
"commands": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace/sources"]
}
],
"servers": [
{
"name": "remote-api",
"url": "https://api.example.com/mcp"
}
]
},
"network": {
"mode": "deny"
},
"secrets": ["my-github-token"]
}
The my-github-token secret (type github) automatically allows api.github.com, so no explicit hosts entry is needed.
Notes¶
- Configuration is optional - workspaces can be registered without a
workspace.jsonfile - The configuration file is validated only when it exists
- Validation errors are caught early during workspace registration (
initcommand) - All validation rules are enforced to prevent runtime errors
- The configuration model is imported from the
github.com/openkaiden/kdn-api/workspace-configuration/gopackage for consistency across tools