# Configuration File (https://jackin.tailrocks.com/reference/runtime/configuration/)



<Aside type="note">
  This page is an **internals reference** for contributors and operators
  who want to read, audit, or hand-edit jackin' config file directly —
  typically while debugging or shipping a fix. Day-to-day operator work
  goes through `jackin workspace …`, `jackin config …`, and the
  operator console, none of which require touching this file. Start
  from the [Operator Guide](/guides/workspaces/) instead if that is
  what you're after.
</Aside>

## Storage locations [#storage-locations]

| Path                                      | Purpose                                                                                     |
| ----------------------------------------- | ------------------------------------------------------------------------------------------- |
| `~/.config/jackin/config.toml`            | Global operator configuration (auth defaults, role trust/source, global mounts, global env) |
| `~/.config/jackin/workspaces/<name>.toml` | One saved workspace per file. The workspace name is the filename stem.                      |
| `~/.jackin/roles/`                        | Cached role repository checkouts                                                            |
| `~/.jackin/data/<container-name>/`        | Persisted agent state (Claude history, settings, GitHub CLI config, plugins)                |

### Environment overrides [#environment-overrides]

Three environment variables redirect these default storage roots without touching any TOML config, useful for testing a PR in isolation or running multiple independent configurations side by side:

| Variable                 | Default                          | Overrides                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JACKIN_CONFIG_DIR`      | `~/.config/jackin`               | Config root — `config.toml`, `workspaces/`                                                                                                                                                                                                                                                                                                                           |
| `JACKIN_HOME_DIR`        | `~/.jackin`                      | jackin' home root — `data/`, `roles/`, `cache/`                                                                                                                                                                                                                                                                                                                      |
| `JACKIN_CONSTRUCT_IMAGE` | `projectjackin/construct:trixie` | Override the construct base image used at derived-build time. Role Dockerfiles always reference the canonical name; this substitutes it during `docker build`. Set to `jackin-local/construct:trixie` after `mise run construct-build-local` to test against a local construct build. See [The Construct Image](/developing/construct-image/) for the full workflow. |

Setting `JACKIN_CONFIG_DIR` and `JACKIN_HOME_DIR` to fresh directories before running jackin means every command reads and writes only those directories, leaving the live operator state completely untouched. Unset behavior is identical to not setting the variables — jackin falls back to the defaults above.

### One file per workspace, by design [#one-file-per-workspace-by-design]

Saved workspaces live one-per-file under `~/.config/jackin/workspaces/` rather than as `[workspaces.<name>]` tables inside a single `config.toml`. The layout works directly with a **dotfile manager** like [chezmoi](https://www.chezmoi.io/) tracking `~/.config/jackin/`. Operators commonly want to:

* **Version-control selectively** — track `config.toml` globally, include only team-relevant workspace files, leave the rest out via `.gitignore`, or keep workspaces private per-machine.
* **Restore the full setup on a new machine** by checking out the dotfile repo and running `chezmoi apply` (or equivalent), with no manual TOML reconstruction.
* **Diff a single workspace** in PR review without seeing churn in unrelated workspaces, which is impossible when every change touches a shared `[workspaces.*]` block.

Each file carries its own `version = "v1alpha6"` stamp, so a dotfile-managed setup that is partially old (some workspaces stamped, some not) migrates per-file on first load. See [Schema Versions](/reference/runtime/schema-versions/) for the full version history and migration matrix.

## config.toml schema [#configtoml-schema]

The configuration is TOML. Global settings live at
`~/.config/jackin/config.toml`; saved workspaces live one-per-file under
`~/.config/jackin/workspaces/`. The CLI and console manage both locations
for you, so you generally don't need to edit them by hand.

Top-level `version` is the config schema version. jackin' writes `version = "v1alpha6"` today. Older config files with no `version` are treated as legacy and migrated automatically on startup before parsing. See [Schema Versions](/reference/runtime/schema-versions/) for the full version history and migration matrix.

### Agent auth-forward settings [#agent-auth-forward-settings]

`auth_forward` is configured per agent (`claude`, `codex`, `amp`, `kimi`, `opencode`) at three
layers, with most-specific wins:

```toml
version = "v1alpha6"

# Layer 1 — global default (per agent)
[claude]
auth_forward = "sync"  # "sync" (default), "api_key", "oauth_token", or "ignore"
sync_source_dir = "/Users/operator/.claude-work"

[codex]
auth_forward = "sync"  # "sync" (default), "api_key", or "ignore" — Codex rejects "oauth_token"

[amp]
auth_forward = "sync"  # "sync" (default), "api_key", or "ignore" — Amp rejects "oauth_token"

[kimi]
auth_forward = "sync"  # "sync" (default), "api_key", or "ignore" — Kimi rejects "oauth_token"

[opencode]
auth_forward = "sync"  # "sync" (default), "api_key", or "ignore" — OpenCode rejects "oauth_token"

# Layer 2 — workspace-wide override
[claude]
auth_forward = "oauth_token"

[codex]
auth_forward = "api_key"

[amp]
auth_forward = "api_key"

[kimi]
auth_forward = "sync"

[opencode]
auth_forward = "sync"

# Layer 3 — most-specific (workspace × role × agent) override
[roles.agent-smith.claude]
auth_forward = "ignore"
sync_source_dir = "/Users/operator/.claude-smith"

[roles.agent-smith.codex]
auth_forward = "sync"

[roles.agent-smith.amp]
auth_forward = "ignore"

[roles.agent-smith.kimi]
auth_forward = "sync"

[roles.agent-smith.opencode]
auth_forward = "sync"
```

The layer 2 and 3 examples above belong in
`~/.config/jackin/workspaces/my-app.toml`; the global defaults belong in
`config.toml`.

| Field             | Description                                                                                                                                                                                                                                                                                                                          | Default               |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- |
| `auth_forward`    | How the host's agent credentials (or env-supplied keys/tokens) are made available to agent containers. See [Authentication Forwarding](/guides/authentication).                                                                                                                                                                      | `sync`                |
| `sync_source_dir` | Optional host-side source folder for file-backed `sync` auth. The value is the agent credential/config directory itself, such as `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, or an Amp data directory like `$XDG_DATA_HOME/amp`, and is valid on global, workspace, and workspace-role agent blocks for agents that support file-backed sync. | agent runtime default |

Accepted values: `sync` | `api_key` | `oauth_token` | `ignore`.
`oauth_token` is **Claude only** — setting it under any `[codex]`,
`[amp]`, `[kimi]`, or `[opencode]` block is rejected at parse time.

Locations the field may appear at, in resolution order
(most-specific first):

* `[roles.<role>.claude].auth_forward` in `workspaces/<ws>.toml`
* `[roles.<role>.codex].auth_forward` in `workspaces/<ws>.toml`
* `[roles.<role>.amp].auth_forward` in `workspaces/<ws>.toml`
* `[roles.<role>.kimi].auth_forward` in `workspaces/<ws>.toml`
* `[roles.<role>.opencode].auth_forward` in `workspaces/<ws>.toml`
* `[claude].auth_forward` in `workspaces/<ws>.toml`
* `[codex].auth_forward` in `workspaces/<ws>.toml`
* `[amp].auth_forward` in `workspaces/<ws>.toml`
* `[kimi].auth_forward` in `workspaces/<ws>.toml`
* `[opencode].auth_forward` in `workspaces/<ws>.toml`
* `[claude].auth_forward` (global default)
* `[codex].auth_forward` (global default)
* `[amp].auth_forward` (global default)
* `[kimi].auth_forward` (global default)
* `[opencode].auth_forward` (global default)

Credentials referenced by `api_key` and `oauth_token` live in ordinary
`[*.env]` blocks under the well-known names `ANTHROPIC_API_KEY`,
`CLAUDE_CODE_OAUTH_TOKEN`, `OPENAI_API_KEY`, `AMP_API_KEY`, and
`KIMI_API_KEY`, and `OPENCODE_API_KEY`.

The workspace editor stages Auth-tab edits in memory until the operator confirms the workspace save. The save path in <RepoFile path="crates/jackin/src/console/services.rs">crates/jackin/src/console/services.rs</RepoFile> applies separate diff passes for general workspace fields, agent auth modes, agent sync source folders, and credential env values; the sync-source pass writes through `ConfigEditor` setters in <RepoFile path="crates/jackin-config/src/editor.rs">crates/jackin-config/src/editor.rs</RepoFile> for both workspace and role layers. This keeps the dialog staging model consistent: the Auth dialog changes pending state only, and disk writes happen only after the workspace save confirmation.

### GitHub CLI auth-forward settings [#github-cli-auth-forward-settings]

GitHub CLI (`gh`) auth uses a parallel `[github]` block with three
modes — `sync` (default), `token`, `ignore`. The `[github]` axis has
**no agent dimension** because `~/.config/gh/` is shared by every
agent in the container.

```toml
# Layer 1 — global default
[github]
auth_forward = "sync"   # "sync" (default), "token", or "ignore"

# Layer 2 — workspace-wide override (e.g. a customer-facing PAT)
[github]
auth_forward = "token"

[github.env]
GH_TOKEN = { op = "op://abc.../def.../fld...", path = "Work/ACME/github-fine-grained-pat" }

# Layer 3 — workspace × role override (e.g. a tighter release-bot token)
[roles.release-bot.github]
auth_forward = "token"

[roles.release-bot.github.env]
GH_TOKEN = { op = "op://abc.../def.../fld...", path = "Work/ACME/github-release-pat" }

# Per-role opt-out — throwaway role with no GitHub access
[roles.scratch.github]
auth_forward = "ignore"
```

Workspace-scoped GitHub blocks live in
`~/.config/jackin/workspaces/customer-acme.toml`.

Mode behaviour:

* `sync` — materialize `~/.config/gh/hosts.yml` from the host's `gh`
  login on each launch (`gh auth token --hostname github.com`,
  with `~/.config/gh/hosts.yml` parse as a fallback). When the host is
  logged out, any existing in-container `hosts.yml` is preserved.
* `token` — wipe any prior `hosts.yml` and inject `GH_TOKEN` (and
  `GITHUB_TOKEN` from the same source) into the container's process
  env from the resolved `[github.env]` map. Pre-flight aborts launch
  when `GH_TOKEN` is missing.
* `ignore` — wipe any forwarded `hosts.yml` and export nothing.

Resolution order, most-specific first:

* `[roles.<role>.github].auth_forward` in `workspaces/<ws>.toml`
* `[github].auth_forward` in `workspaces/<ws>.toml`
* `[github].auth_forward` (global default)

Operator-only — role manifests cannot set or override `[github]`,
same rule as `[claude]` / `[codex]` / `[kimi]` / `[opencode]`. Optional GHE-targeted env vars
`GH_HOST` and `GH_ENTERPRISE_TOKEN` may also be set in the
`[…github.env]` block; they are forwarded to the container as-is.

### Operator env layers [#operator-env-layers]

Four layers contribute env vars to the agent container, merged with later-wins semantics:

```toml
# Layer 1 — global
[env]
OPERATOR_ORG = "acme-corp"

# Layer 2 — per role
[roles.agent-smith.env]
API_TOKEN = "op://Personal/api/token"

# Layer 3 — per workspace
[env]
REGISTRY = "${COMPANY_REGISTRY_URL}"

# Layer 4 — per (workspace, role)
[roles.agent-smith.env]
API_TOKEN = "op://Work/shared-smith/token"
```

Layer 3 and 4 env blocks are stored in
`~/.config/jackin/workspaces/big-monorepo.toml`.

Values can be:

* `op://VAULT/ITEM/FIELD` — resolved via the 1Password CLI (`op read`)
* `$NAME` or `${NAME}` — read from the host env
* Anything else — literal

<Aside type="caution" title="Reserved names cannot be overridden">
  A small set of names is owned by the jackin' runtime and set internally on every launch. Declaring them at any of the four layers above is rejected at config load. See [Reserved names](/guides/environment-variables/#reserved-names) for the canonical list.
</Aside>

See [Environment Variables](/guides/environment-variables/) for the full guide.

### Workspaces [#workspaces]

```toml
workdir = "/home/user/Projects/my-app"
allowed_roles = ["agent-smith", "the-architect"]
default_role = "agent-smith"
default_agent = "amp"
last_role = "the-architect"

[[mounts]]
src = "/home/user/Projects/my-app"
dst = "/home/user/Projects/my-app"
readonly = false
isolation = "worktree"

[[mounts]]
src = "/home/user/cache"
dst = "/cache"
readonly = true

[keep_awake]
enabled = true

git_pull_on_entry = true
```

This example is the contents of
`~/.config/jackin/workspaces/my-app.toml`; there is no
`[workspaces.my-app]` header inside the file.

| Field                | Description                                                                                                                                                                                                                                                                                                                                                       |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workdir`            | Container working directory                                                                                                                                                                                                                                                                                                                                       |
| `mounts`             | Array of mount specifications                                                                                                                                                                                                                                                                                                                                     |
| `mounts[].src`       | Host path                                                                                                                                                                                                                                                                                                                                                         |
| `mounts[].dst`       | Container path                                                                                                                                                                                                                                                                                                                                                    |
| `mounts[].readonly`  | Read-only flag                                                                                                                                                                                                                                                                                                                                                    |
| `mounts[].isolation` | Per-mount isolation mode: `shared` (default), `worktree`, or `clone`. Omitting the field deserializes to `shared`, but every save writes the field explicitly so old configs migrate to the new shape on first save. Global mounts (under `[docker.mounts]`) reject this field at parse time. See [Per-mount isolation](/guides/workspaces/#per-mount-isolation). |
| `allowed_roles`      | List of allowed role selectors                                                                                                                                                                                                                                                                                                                                    |
| `default_role`       | Default role selector                                                                                                                                                                                                                                                                                                                                             |
| `default_agent`      | Optional workspace default agent (`"claude"`, `"codex"`, `"amp"`, `"kimi"`, or `"opencode"`). Omit to let launch resolution use the role manifest: a single-agent role uses its only runtime, while a multi-agent role prompts interactively.                                                                                                                     |
| `last_role`          | Most recently used role selector for this workspace                                                                                                                                                                                                                                                                                                               |
| `keep_awake.enabled` | macOS-only: keep the host awake while any agent in this workspace is running (see [Keeping the host awake](/guides/workspaces/#keeping-the-host-awake)). On Linux/Windows the flag is silently accepted but has no effect — useful when sharing a `config.toml` across machines, but the host will still sleep.                                                   |
| `git_pull_on_entry`  | Run `git pull` on every mounted git repository from the host before the agent container starts. Failures are non-fatal — the launch continues even when offline or the working tree is dirty. Omitted from TOML when `false` (the default). See [Git pull on entry](/guides/workspaces/#git-pull-on-entry).                                                       |

When you use `jackin workspace create`, `workdir` is only the container start path. Add an explicit `--mount` for every directory the container should see.

### Global mounts [#global-mounts]

```toml
[docker.mounts]
gradle-cache = { src = "/home/user/.gradle/caches", dst = "/home/agent/.gradle/caches", readonly = true }

[docker.mounts."chainargos/*"]
secrets = { src = "/home/user/.chainargos/secrets", dst = "/secrets", readonly = true }
```

Global mounts are stored by name. Unscoped mounts live directly under `[docker.mounts]`. Scoped mounts live under a scope key such as `[docker.mounts."chainargos/*"]`.

### Git co-author trailer [#git-co-author-trailer]

When `coauthor_trailer` is `true`, jackin installs a shared `prepare-commit-msg` hook inside the role container the first time a session needs it. The hook is a symlink to `jackin-capsule`, so trailer normalization runs through the same Rust binary that owns deterministic runtime setup. It reads which agent is running and appends that agent's `Co-authored-by` trailer whenever Git asks the hook to prepare a commit message, including amended, reused, squash, and merge messages. Existing agent trailers are preserved, so a commit touched by multiple agents keeps each involved agent in the final trailer block.

When `dco` is `true`, the same hook also appends a `Signed-off-by` DCO trailer read from `git config user.name` and `git config user.email` inside the container. Matching existing trailer lines are normalized into Git's final trailer block before commit creation. The two fields are independent: enable either or both.

```toml
[git]
coauthor_trailer = true  # enable Co-authored-by agent trailer
dco = true               # enable Signed-off-by DCO trailer
```

The `[git]` table is omitted from `config.toml` when all fields are at their defaults (the table is not written unless the operator sets it). See [Schema Versions](/reference/runtime/schema-versions/#v1alpha6) for the field-level reference.

## Persisted state [#persisted-state]

Each running role stores state at `~/.jackin/data/<container-name>/`:

| File/Directory                          | Purpose                                                                                                                                                                                                                                                                                                                                                                                                    |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `claude/account.json`                   | Claude Code account metadata (mounted at `/jackin/claude/account.json` in the container; only forwarded under `auth_forward = "sync"`)                                                                                                                                                                                                                                                                     |
| `claude/credentials.json`               | Claude Code OAuth credentials (mounted at `/jackin/claude/credentials.json`; only forwarded under `auth_forward = "sync"`)                                                                                                                                                                                                                                                                                 |
| `codex/auth.json`                       | Synced copy of host `~/.codex/auth.json` under `auth_forward = "sync"`, `0600` perms (mounted at `/jackin/codex/auth.json`; absent when host has no auth.json)                                                                                                                                                                                                                                             |
| `amp/secrets.json`                      | Synced copy of host `~/.local/share/amp/secrets.json` under `auth_forward = "sync"`, `0600` perms (mounted at `/jackin/amp/secrets.json`; absent when host has no secrets.json). `secrets.json` is the XDG\_DATA file the Amp CLI writes the `apiKey@https://ampcode.com/` token into; the XDG\_CONFIG path `~/.config/amp/settings.json` is preferences only and is intentionally not forwarded           |
| `kimi-code/config.toml`                 | Synced copy of host `~/.kimi-code/config.toml` under `auth_forward = "sync"`, `0600` perms (mounted under `/jackin/kimi-code/` and copied into `/home/agent/.kimi-code/` before Kimi starts). Kimi stores OAuth tokens separately, but its login flow writes the OAuth-backed provider/model/service references into this config file, so a fresh default config is not enough to use the forwarded token. |
| `kimi-code/credentials/`                | Synced copy of host `~/.kimi-code/credentials/` OAuth token files under `auth_forward = "sync"`, `0600` perms                                                                                                                                                                                                                                                                                              |
| `kimi-code/device_id`                   | Synced copy of host `~/.kimi-code/device_id` under `auth_forward = "sync"`, `0600` perms; Kimi includes this device id in OAuth/device headers                                                                                                                                                                                                                                                             |
| `opencode/auth.json`                    | Synced copy of host `~/.local/share/opencode/auth.json` under `auth_forward = "sync"`, `0600` perms (mounted at `/jackin/opencode/auth.json`; absent when host has no auth.json). OpenCode stores provider credentials in this file — for example, a Z.AI Coding Plan API key                                                                                                                              |
| `home/.claude/` and `home/.claude.json` | Durable Claude Code home state, including conversation history, runtime-local plugins, account metadata, and settings written during the session                                                                                                                                                                                                                                                           |
| `home/.codex/`                          | Durable Codex home state, including conversation history, runtime-local config, and login state created inside the session                                                                                                                                                                                                                                                                                 |
| `home/.local/share/amp/`                | Durable Amp data state created inside the session                                                                                                                                                                                                                                                                                                                                                          |
| `home/.kimi-code/`                      | Durable Kimi home state, including runtime-local config and login state created inside the session                                                                                                                                                                                                                                                                                                         |
| `home/.local/share/opencode/`           | Durable OpenCode data state (auth credentials, model preferences) created inside the session                                                                                                                                                                                                                                                                                                               |
| `state/`                                | jackin runtime state mounted at `/jackin/state`, including the `setup_once` marker                                                                                                                                                                                                                                                                                                                         |
| `.config/gh/`                           | GitHub CLI authentication and settings (agent-neutral)                                                                                                                                                                                                                                                                                                                                                     |

Host→container auth handoff lives under a single tree (`/jackin/`) the operator can audit at a glance. The agent home directories are bind-mounted from jackin-managed per-instance state, not from the operator's host home. `jackin-capsule runtime-setup` seeds image-baked defaults into that durable home on first launch, then copies the current auth handoff files from `/jackin/` into the durable home before starting the agent. This keeps conversation history and runtime-local plugins recoverable when Docker containers disappear while still letting the current auth mode override stale forwarded credentials.

For roles that list multiple supported agents in their manifest, jackin provisions `claude/`, `codex/`, `amp/`, `kimi-code/`, and `opencode/` auth handoff directories and their corresponding `home/.claude/`, `home/.codex/`, `home/.local/share/amp/`, `home/.kimi-code/`, and `home/.local/share/opencode/` durable home directories for every agent the role supports — not just the agent selected for the initial launch. This means starting a second agent via `jackin hardline --new` does not require re-authentication: the credentials and durable home were already placed in the container when it first launched.

`claude/account.json` is rebuilt as `{}` (empty object) by every non-sync auth mode so it is always present on disk; the bind mount is the gate for whether the file flows into the container, not the file's existence. `claude/credentials.json` is wiped on every non-sync mode and never bind-mounted under `ignore`/`api_key`/`oauth_token`.

Delete with `jackin purge <role>` to start fresh.
