Configuration Reference

cc-deck uses XDG-compliant paths for configuration and data storage.

Config File

Location: $XDG_CONFIG_HOME/cc-deck/config.yaml (typically ~/.config/cc-deck/config.yaml)

The config file is optional. All settings have sensible defaults and can be overridden via CLI flags.

# Default namespace for Kubernetes commands
namespace: cc-deck

# Default credential profile
profile: default

# Verbose output
verbose: false

# Voice relay defaults
defaults:
  voice:
    threshold: 45    # VAD sensitivity (0-100, logarithmic)
    glossary:         # Terms to bias Whisper recognition (optional)
      - Kubernetes
      - gRPC
      - OpenShell
    commands:         # Custom command words (optional)
      submit:
        - send
        - done
        - enter
      attend:
        - next

The defaults.voice.threshold setting controls how sensitive voice activity detection is. Lower values capture quieter speech but pick up more background noise. A value between 30 and 50 works well for most indoor environments. This value can be overridden per invocation with --threshold.

The defaults.voice.commands setting maps action names to trigger words. Two actions are available by default:

Action Default Word Description

submit

send

Sends a carriage return to the attended pane (same as pressing Enter)

attend

next

Cycles to the next session needing attention (same as Alt+a)

When the entire transcribed utterance, after stripping filler words, matches a trigger word, the corresponding action fires. Words embedded in longer sentences are treated as normal dictation.

When you set commands in the config, it replaces the defaults entirely. Each action can have multiple trigger words, and you can change the defaults to avoid conflicts with your natural speech patterns.

Voice Glossary

The defaults.voice.glossary setting provides a list of terms that bias Whisper’s recognition toward correct spellings of domain-specific vocabulary. Terms are passed as an initial prompt to the Whisper model.

In addition to the global glossary, each project can provide a .cc-deck/voice-glossary.txt file in its root directory. This file uses one term per line, with blank lines and lines starting with # treated as comments.

When the attended session switches to a project that has a glossary file, the relay merges the global and project terms automatically. Global terms come first, project terms last. Duplicate terms are removed case-insensitively, with the project’s casing taking precedence.

Whisper’s prompt window is limited to approximately 224 tokens (roughly 800 characters or 50 terms). The relay logs a warning when the merged glossary exceeds this limit but still passes the full list. Whisper truncates from the beginning, so project-specific terms at the end are preserved.

Project glossary files are cached per directory for the lifetime of the relay process. Changes to glossary files require a relay restart to take effect.

Config Validation

The cc-deck config check command validates the configuration file and reports issues with fix suggestions. Validation covers badge icon widths, profile required fields, voice parameter ranges, and badge rule structure.

Every cc-deck command that loads the config also runs a quick validation check. When issues are found, a one-line summary is printed to stderr pointing to cc-deck config check for details. This passive check does not affect command behavior or exit codes.

Badge Icon Width

Badge icons appear in the sidebar, which expects single-column characters. Emoji and East Asian Wide characters render as 2 columns in most terminals, breaking alignment.

  • Error: Emoji characters (always 2 columns)

  • Error: East Asian Width W characters (always 2 columns)

  • Warning: East Asian Width Ambiguous characters (may be 2 columns in some terminals)

Safe icons include characters like ⋮ (U+22EE VERTICAL ELLIPSIS) or │ (U+2502 BOX DRAWINGS LIGHT VERTICAL).

Badge Color Prefix

Badge values may include a color prefix in #RRGGBB:icon format. The hex portion must be exactly 6 valid hexadecimal characters.

Severity Levels

Severity Exit code Meaning

error

1

Will break something (layout misalignment, deployment failure, missing functionality)

warning

0

May cause issues in some environments but is not guaranteed to break

Environment Variables

Variable Description

XDG_CONFIG_HOME

Base directory for config files. Default: ~/.config

XDG_DATA_HOME

Base directory for data files. Default: ~/.local/share

XDG_CACHE_HOME

Base directory for cache files. Default: ~/.cache

KUBECONFIG

Path to kubeconfig file. Default: ~/.kube/config

ZELLIJ_CONFIG_DIR

Zellij configuration directory. Default: ~/.config/zellij

File Locations

Path Purpose

~/.config/cc-deck/config.yaml

CLI configuration

~/.config/zellij/plugins/cc_deck.wasm

Sidebar plugin binary

~/.config/zellij/layouts/cc-deck.kdl

Default layout (symlink to chosen variant)

~/.config/zellij/layouts/cc-deck-standard.kdl

Standard layout variant

~/.config/zellij/layouts/cc-deck-minimal.kdl

Minimal layout variant

~/.config/zellij/layouts/cc-deck-clean.kdl

Clean layout variant

~/.config/zellij/layouts/cc-deck-personal.kdl

Personal layout (never overwritten by install)

~/.claude/settings.json

Claude Code hooks (registered by plugin install)

~/.config/opencode/plugins/cc-deck.ts

OpenCode plugin (generated by plugin install)

~/.codex/hooks.json

Codex CLI hooks (registered by plugin install, requires Codex CLI v0.1.2505+)

~/.config/cc-deck/domains.yaml

User-defined domain groups for network filtering

~/.config/cc-deck/sessions/

Snapshot storage

~/.cache/cc-deck/models/

Whisper model files for voice relay

~/.local/state/cc-deck/voice.log

Voice relay diagnostic log (created with --verbose)

<project>/.cc-deck/voice-glossary.txt

Project-specific voice glossary (one term per line)

~/.local/state/cc-deck/pane-map.json

Hook pane ID cache. Entries are scoped to the Zellij session that recorded them and expire after 12 hours.

~/.local/state/cc-deck/hook-debug

Flag file. When it exists, the hook command records diagnostics.

~/.local/state/cc-deck/hook.log

Hook diagnostic log, written only while the flag file above exists.

Workspace Definition Fields

Workspace definitions are stored in ~/.config/cc-deck/workspaces.yaml. The following credential-related fields are available:

Field YAML Key Description

Agent

agent

Agent name (e.g., claude, opencode, codex). Links the workspace to the agent’s credential specs.

AuthMode

auth-mode

Selected auth mode name (e.g., api, vertex, bedrock, openai). Determines which credentials are validated and injected at start time.

ExternalCredentials

external-credentials

When true, host-side credential validation is skipped. Use for K8s workspaces where credentials are provided via Secrets or for OpenShell workspaces using provider-managed credentials.

Plugin Configuration

Plugin settings are specified in the layout file’s plugin block:

plugin location="file:~/.config/zellij/plugins/cc_deck.wasm" {
    navigate_key "Alt s"      // Key to toggle navigation mode
    attend_key "Alt a"        // Key for smart attend
    voice_key "Alt m"         // Key for voice mute toggle
    auto_sort "true"          // Auto-sort paused sessions below active ones
}
Setting Default Description

navigate_key

Alt s

Global shortcut for navigation mode

attend_key

Alt a

Global shortcut for smart attend

voice_key

Alt m

Global shortcut for voice mute toggle

voice_timeout_secs

15

Seconds without a poll from the voice relay before the sidebar clears the voice indicator. The relay polls every five seconds, so keep this above the poll interval. Values below five are ignored.

auto_sort

true

When enabled, the sidebar groups active sessions above a visual separator and paused sessions below it. The sort is display-only and does not reorder physical tabs. Sessions that are unpaused appear at the end of the active zone. Set to "false" to disable and show sessions in tab order with no separator.

Key format follows Zellij conventions: Alt, Ctrl, Super (Cmd on macOS) as modifiers, followed by the key name.

Build Manifest Credentials

Location: .cc-deck/setup/build.yaml (relative to project root)

The credentials section of the build manifest declares which credential providers an OpenShell workspace needs. It stores only provider type identifiers and environment variable names, never actual credential values. Values are resolved from the host environment at workspace creation time (cc-deck ws new).

credentials:
  # API key credentials (auto-injected via OpenShell providers)
  - type: claude
    env_vars: [ANTHROPIC_API_KEY]
  - type: github
    env_vars: [GITHUB_TOKEN]

  # File-based credentials (uploaded into the sandbox at runtime)
  - type: vertex
    file: GOOGLE_APPLICATION_CREDENTIALS
    env_vars: [ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGION]

  # Custom service with explicit endpoints for network policy
  - type: generic
    env_vars: [CUSTOM_API_KEY]
    endpoints:
      - host: api.custom.com
        port: 443

Credential Entry Fields

Field Type Required Description

type

string

yes

Provider profile type. Known types: claude, claude-vertex, anthropic, github, gitlab, openai, nvidia, vertex, generic.

env_vars

list of strings

no

Environment variable names to resolve from the host. Auto-populated from known profiles if omitted.

file

string

no

Name of an env var whose value is a file path (for example, GOOGLE_APPLICATION_CREDENTIALS). The file is uploaded into the sandbox at runtime.

endpoints

list of host/port

no

Custom network endpoints for generic type. Added to the generated network policy.

Known Provider Types

Type Default env_vars Description

claude

ANTHROPIC_API_KEY

Anthropic API key for Claude Code (direct API access)

claude-vertex

CLAUDE_CODE_USE_VERTEX, ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGION, ANTHROPIC_MODEL

Claude Code via Google Vertex AI. Detected when both CLAUDE_CODE_USE_VERTEX and ANTHROPIC_VERTEX_PROJECT_ID are set. Adds GCP OAuth endpoint to network policy.

github

GITHUB_TOKEN, GH_TOKEN

GitHub personal access token

gitlab

GITLAB_TOKEN, GLAB_TOKEN

GitLab personal access token

openai

OPENAI_API_KEY

OpenAI API key

nvidia

NVIDIA_API_KEY

NVIDIA API key

vertex

GOOGLE_APPLICATION_CREDENTIALS, ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGION

Google Vertex AI (file-based auth). Adds GCP endpoints to network policy automatically.

generic

(must be specified)

Custom service. Requires explicit env_vars and optional endpoints.

When a credential’s required env var is not set in the host environment, the provider is skipped with a warning. The workspace still starts, but the corresponding service will not be available.

Use /cc-deck.capture to detect available credentials automatically. Use /cc-deck.capture --all to auto-accept all proposals without prompting.

Agent Selection

The agents field in the build manifest specifies which AI coding agents the workspace supports. Each agent has its own set of backend domain groups that are automatically included in the network policy.

agents:
  - claude
  - opencode
  - codex

When the agents field is omitted, the manifest defaults to ["claude"] for backward compatibility. Agent adapters declare their required domain groups (e.g., Claude declares anthropic, OpenCode and Codex declare openai), and the policy assembly resolves those groups to concrete endpoints automatically.

Network Configuration

The network section of the build manifest controls domain-level egress filtering for containerized sessions.

network:
  allowed_domains:
    - pypi.org
    - files.pythonhosted.org
    - crates.io
    - github       # expands to domain group

network.allowed_domains

A list of domain names or domain group names that should be permitted for outbound network access. Each entry produces a network policy in the assembled openshell/policy.yaml with port 443.

Entries can be:

  • Literal domains (contain a dot): Used as-is with port 443. Example: pypi.org.

  • Domain group names (no dot): Expanded to the set of domains defined in ~/.config/cc-deck/domains.yaml. Example: github expands to github.com, api.github.com, etc.

Domains already covered by catalog components are not duplicated. If a catalog component (e.g., python) already includes pypi.org in its endpoints, adding pypi.org to allowed_domains has no effect.

This field is populated automatically by cc-deck build record, which captures DNS queries during an interactive session and appends new domains. It can also be edited manually.

network.allowed_domains_per_agent

A mapping of agent names to lists of domain group names. Groups listed under an agent are only included in the network policy when that agent appears in the manifest’s agents field.

network:
  allowed_domains_per_agent:
    claude:
      - docker       # Docker Hub access, only when Claude is an active agent
    opencode:
      - internal-registry   # custom group for OpenCode workspaces

These per-agent groups are merged with each agent adapter’s declared groups (from RequiredDomainGroups()). For example, Claude always declares anthropic as a required group. If you also add docker under claude in allowed_domains_per_agent, both groups are resolved when claude is in the manifest.

Groups that do not exist in the built-in or user-defined domain groups produce a warning during policy assembly but do not fail the build.

Policy Component System

The build refresh command assembles openshell/policy.yaml deterministically from YAML component files. Each component file defines a set of network endpoints, match conditions, and optional binary restrictions.

Component File Locations

Components are loaded from three tiers in precedence order. A component at a higher tier replaces one with the same filename stem entirely (no merging).

Tier Path Precedence

Embedded

Built into the cc-deck binary

Lowest

Cached catalog

.cc-deck/setup/openshell/components/*.yaml

Middle

User-local

.cc-deck/setup/openshell/policies/*.yaml

Highest

The capture command fetches catalog components from the remote catalog repo and caches them in the middle tier. User-local components can be created manually for project-specific endpoints.

Built-in Components

The following components are embedded in the cc-deck binary:

Component Match Endpoints

claude-code.yaml

Agents: claude

api.anthropic.com, statsig.anthropic.com, sentry.io, downloads.claude.ai, raw.githubusercontent.com

opencode.yaml

Agents: opencode

api.openai.com, openrouter.ai

git-hosting.yaml

Always included

github.com, api.github.com

rust.yaml

Tools: rust, cargo

crates.io, index.crates.io, static.crates.io

go.yaml

Tools: go

proxy.golang.org, sum.golang.org, storage.googleapis.com

node.yaml

Tools: node, npm

registry.npmjs.org, npmjs.org

python.yaml

Tools: python, pip, uv

pypi.org, files.pythonhosted.org

vertex-ai.yaml

Credentials: claude-vertex, vertex

All Vertex AI regional endpoints, oauth2.googleapis.com, accounts.google.com

Components with always: true in their match conditions are always included regardless of the manifest. Agent-matched components (e.g., agents: [claude]) are included when any listed agent appears in the manifest’s agents field. When the manifest omits the agents field, it defaults to ["claude"] for backward compatibility. Tool, credential, and agent matches use OR semantics: any single condition match includes the component.

Probe and Runtime Glob Fields

Tool-matched components (those without explicit binaries) support two optional fields for the two-pass binary probing system:

Field Type Description

probe_binaries

[]string

Binary names to search for via which/find inside the built image. Falls back to match.tools entries if omitted. Entries must not contain path separators (binary names only).

runtime_globs

[]string

Glob patterns for binaries created at runtime (virtual environments, toolchain installs). Merged into the policy alongside probed paths. Entries must start with / (absolute paths).

Components with explicit binaries (like claude-code.yaml and git-hosting.yaml) ignore both fields.

Custom Component File Format

Create a YAML file in .cc-deck/setup/openshell/policies/ with this schema:

key: internal_api
name: Internal API
match:
  always: true
endpoints:
  - host: api.internal.corp
    port: 8443

To add a tool-matched component with probing support:

key: pkg_elixir
name: Elixir packages
match:
  tools:
    - mix
    - elixir
probe_binaries:
  - mix
  - elixir
runtime_globs:
  - /sandbox/.mix/escripts/*
endpoints:
  - host: hex.pm
    port: 443
  - host: repo.hex.pm
    port: 443

Required fields: key, name, match (with at least one condition), endpoints (at least one entry). Each endpoint requires host and port. Endpoints with protocol: rest must also have access or rules (OpenShell 0.0.46 compliance).

MCP Endpoint Policy Entries

MCP servers configured in the manifest can include an optional endpoint field in host:port format. During policy assembly, each MCP entry with a non-empty endpoint generates a network policy entry keyed as mcp_<slugified_name>. This allows Claude Code to connect to remote MCP servers at startup without being blocked by the OpenShell supervisor.

The capture command extracts endpoints automatically from HTTP/SSE MCP servers (via the url field) and from stdio servers that use mcp-remote (by scanning arguments for HTTPS URLs). Local stdio servers without a URL produce no endpoint.

mcp:
  - name: google-work
    transport: http
    endpoint: mcp-google-work.int-tichny.org:8443
    description: "Google Workspace (work)"
  - name: jira-redhat
    transport: stdio
    endpoint: mcp.atlassian.com:443
    description: "Red Hat Jira (npx mcp-remote)"
  - name: playwright
    transport: stdio
    description: "Browser automation via Playwright"

The generated policy entries use binary paths collected from all agent-matched components (those with agents in their match condition). For example, when both Claude and OpenCode are active agents, binaries from both claude-code.yaml and opencode.yaml are merged into MCP policy entries.

When the pkg_node component is also present (because node or npm is in the tools list), agent binary paths are appended to its binary list. This allows agents to spawn npx processes that download and run npm packages for MCP stdio proxies.

MCP entries without an endpoint, or with a malformed endpoint (missing port), are skipped with a warning during policy assembly. If no agent-matched components are found, all MCP policy entries are skipped gracefully.

Containerfile Template Snippets

The cc-deck build init command renders fixed Containerfile layers into snippet files at <target>/snippets/. These snippets are pre-rendered Go templates with all paths and variables resolved. The /cc-deck.build Claude Code command assembles the final Containerfile by copying snippets verbatim and generating only the variable parts (tool installs, plugin commands, settings) between them.

Snippet files:

File Content

01-header.txt

FROM line and ARG TARGETARCH

02-user-setup.txt

User creation and shell setup (container target only, empty for OpenShell)

03-mandatory-stack.txt

cc-session, cc-setup, cc-deck self-install with Zellij, Claude Code install

04-openshell-extras.txt

Skills directories, policy file COPY, ENV SHELL (OpenShell only)

05-shell-finalize.txt

Starship prompt init, Zellij auto-start on login (OpenShell only)

06-footer.txt

Final chown, USER, WORKDIR, CMD/ENTRYPOINT

The Zellij binary installed by --install-zellij defaults to the latest GitHub release. Pin a specific version with cc-deck config plugin install --install-zellij --zellij-version 0.45.1. Zellij 0.45.0 is the minimum: earlier releases could load the background controller plugin twice on startup.

Domain Groups Configuration

Location: $XDG_CONFIG_HOME/cc-deck/domains.yaml (typically ~/.config/cc-deck/domains.yaml)

This file defines custom domain groups for network filtering in containerized sessions. Create it with cc-deck config domains init to get a commented template, or write it manually.

The file is a YAML mapping where each top-level key is a group name and the value defines its domains and resolution behavior.

Custom Groups

Define a new group with a domains list:

internal-registry:
  domains:
    - registry.internal.corp
    - artifacts.internal.corp

Extending Built-in Groups

Use extends: builtin to merge additional domains with a built-in group of the same name. The user-defined domains are appended to the built-in domains rather than replacing them.

python:
  extends: builtin
  domains:
    - pypi.internal.corp

This produces a python group containing all built-in Python domains plus pypi.internal.corp. Without extends: builtin, the user-defined group replaces the built-in group entirely.

Including Other Groups

Use includes to compose a group from other groups. Included groups are resolved recursively.

dev-stack:
  includes:
    - python
    - golang
    - github
  domains:
    - artifacts.internal.corp

This produces a group containing all domains from python, golang, and github, plus the literal domain artifacts.internal.corp.

Resolution Rules

  • Groups defined in domains.yaml take precedence over built-in groups of the same name.

  • If extends: builtin is set, the built-in domains are included as a base.

  • Circular includes are detected and reported as errors.

  • Source labels in cc-deck config domains list reflect the resolution: builtin, user (override or new), or extended (extends builtin).

Base Image Registry

Location: .cc-deck/setup/base-images.yaml (extracted during cc-deck build init)

The base image registry lists known base images grouped by target type. When a manifest does not specify a base: field, the build pipeline uses the default: true entry from this file. If the file is missing, hardcoded defaults compiled into the binary are used as a final fallback.

openshell:
  - name: nvidia-upstream
    ref: ghcr.io/nvidia/openshell-community/sandboxes/base:latest
    default: true
  - name: rh-ubi-openshell
    ref: quay.io/aipcc/openshell-base:latest
container:
  - name: fedora-41
    ref: registry.fedoraproject.org/fedora:41
    default: true
Field Type Description

name

string

Human-readable identifier, used in test output and Make target filtering

ref

string

Full container image reference

default

boolean

When true, this entry is used as the fallback base image for its target type. Exactly one entry per target type should be marked as default.

The /cc-deck.base-images command checks upstream registries for new or updated images and helps maintain this file. Run make test-images after updating to validate that the build pipeline works with all listed base images.

Voice Defaults

Location: ~/.config/cc-deck/config.yaml under defaults.voice

These settings control how the voice relay separates speakers in a recording and which VAD parameters apply while recording. The speaker_split setting selects the separation mode that is active when the voice TUI starts. The recording block adjusts VAD behavior during transcript recording, where shorter passages make the reading view more responsive.

defaults:
  voice:
    speaker_split: pause
    recording:
      threshold: 20
      silence: 1.0
      max_chunk: 12.0
      pause_break: 2.0

defaults.voice.speaker_split

How a recording decides where one speaker stops and the next one starts. Accepted values are pause (default) and voice (case-insensitive). With pause, a new block starts after a silence longer than recording.pause_break, or where Whisper marks a speaker change with a leading dash. With voice, the tinydiarize model detects a change of voice through whisper-cli, even without a pause; this mode recognizes English only and needs the small.en-tdrz model. Pressing s in the idle voice TUI switches the mode for the current session. Any other value produces a configuration error in category voice, and the relay starts with pause.

defaults.voice.speech_filter

Whether the relay skips non-speech audio before transcription (default true). When enabled and installed, whisper-server and whisper-cli run with the Silero voice activity model (ggml-silero-v6.2.0.bin in the model directory), which keeps keyboard noise or breathing from turning into phantom words such as "Thank you.". cc-deck ws voice --setup installs the model. Set the value to false to transcribe all audio that passes the VAD threshold, as earlier versions did.

defaults:
  voice:
    speech_filter: true

defaults.voice.recording

VAD parameters applied while a transcript recording is active. These override the main dictation VAD settings for the duration of the recording and are restored when recording stops. All fields are optional. An omitted field uses the recording default from the table below, not the dictation setting.

Field Default Valid values Description

threshold

20

0 to 100

Logarithmic VAD sensitivity during recording, on the same scale as defaults.voice.threshold. Lower values capture quieter speech, such as remote voices played through laptop speakers. The dictation default corresponds to about 44.

silence

1.0

greater than 0, at most 10

Seconds of silence that end a recording passage. Shorter values produce faster updates in the reading view.

max_chunk

12.0

2 to 30

Maximum seconds per recording passage. Continuous speech is split into passages of this length.

pause_break

2.0

greater than the effective silence

Seconds of silence between two passages that start a new block in the reading view and in the transcript file. The silence is measured from the last loud audio of one passage to the first loud audio of the next, so --pre-roll and the hangover do not change it.

Values outside these ranges produce a configuration warning in category voice, and the relay uses the default for that field.