Schema Versions
Historical config, workspace, and role manifest schema versions
This page is the canonical schema-version history for jackin-owned configuration files and role manifests. It is written for operators, role authors, migration authors, and agents that need to understand which fields exist in which schema version.
Why versioning
Breaking changes to schemas are expected — that is how the project explores ideas without dragging compatibility shims forward. The trade-off is that operators and role authors should never have to hand-edit a TOML file to recover from an upgrade. Versioning resolves the tension:
- Each schema file carries its own
versionstamp. When jackin reads the file, it compares the on-disk version against the binary'sCURRENT_*_VERSIONand applies any pending migration steps before parsing, so operators see a working setup after every upgrade. - Operator-owned files migrate automatically.
config.tomland~/.config/jackin/workspaces/<name>.tomlare rewritten in place on startup; the operator does nothing. - Role-owned files migrate explicitly. Role authors run
jackin role migrate <role-repo-path>on their desktop to rewrite ajackin.role.tomlagainst the latest schema, then commit and push the result. jackin' never writes back into a role repo on its own — that would silently mutate someone else's source tree. CI, validation workflows, and Renovate-style migration automation use the standalonejackin-role migrate <role-repo-path>binary so role repos do not need the fulljackinoperator CLI. - Migrations are a hard PR-review rule. Every change to one of the three versioned files must bump the corresponding
CURRENT_*_VERSION, add a migration step, and update this page. Adding a new enum variant counts as a schema-breaking change: old binaries cannot parse manifests that use the new variant. The full rule lives inAGENTS.mdandPULL_REQUESTS.md. - Version mismatches produce actionable errors. The version check runs before full TOML deserialization, so old binaries always surface a clear message rather than a cryptic parse error. When a manifest's
versionis newer than what the binary knows about, jackin rejects it with:role manifest is at {version}, this binary only understands up to {CURRENT_MANIFEST_VERSION}; upgrade jackin. Current binaries accept older role manifests when the file only uses fields and enum values that existed at that older version. If an older-stamped manifest uses a newer feature, jackin rejects it with a feature-specificjackin role migrate <role-repo-path>hint.
The split-workspace layout described in Configuration File makes per-file migration tractable: legacy global files and per-workspace files migrate independently, so a dotfile repo that is partially old still upgrades cleanly.
Current versions
| File | Current version | Owner | Migration mode |
|---|---|---|---|
~/.config/jackin/config.toml | v1alpha6 | jackin | Automatic on startup |
~/.config/jackin/workspaces/<name>.toml | v1alpha6 | jackin | Automatic on startup |
jackin.role.toml | v1alpha5 | Role repository | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> |
Timeline
Each entry is one upgrade path the current binary applies. crates/jackin/tests/migration_fixtures.rs walks every fixture on every CI run, so a regression breaks here before it breaks on a delayed operator's machine. Newest first; append, never edit.
Manifest v1alpha5 — 2026-06-10
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Role manifest (jackin.role.toml) | v1alpha4 | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> | crates/jackin/tests/fixtures/migrations/manifest/from-v1alpha4/meta.toml |
Summary: adds optional [<agent>.providers.<id>] tables to the [claude], [codex], and [opencode] agent configs for per-provider model overrides. The providers map is an additive field with a serde default of empty, so existing manifests round-trip cleanly and the migration is a no-op restamp. A manifest stamped older than v1alpha5 that uses a providers table is rejected with a jackin role migrate hint, the same way the OpenCode (v1alpha3) and Kimi (v1alpha4) agent fields are gated.
Before (v1alpha4 manifest):
version = "v1alpha4"
[opencode]
model = "zai-coding-plan/glm-5.1"After (v1alpha5 — no change needed; the providers map defaults to empty):
version = "v1alpha5"
[opencode]
model = "zai-coding-plan/glm-5.1"Example with a per-provider override (a role pins a model when MiniMax is picked for OpenCode):
version = "v1alpha5"
[opencode]
model = "zai-coding-plan/glm-5.1"
[opencode.providers.minimax]
model = "minimax/MiniMax-M3"v1alpha6 — 2026-06-04
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | v1alpha5 | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-v1alpha5/meta.toml |
Workspace (workspaces/<name>.toml) | v1alpha5 | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-v1alpha5/meta.toml |
Summary: adds the optional sync_source_dir field to AgentAuthConfig for per-scope auth-sync source folders. This is an additive, Option-typed field with a serde default of None, so all existing config and workspace files round-trip cleanly through the migration without any transformation. The migration is a no-op restamp.
When sync_source_dir is set, the sync auth mode copies credentials from the operator-chosen agent credential/config folder instead of the per-agent hardcoded default (~/.claude, ~/.codex, ~/.local/share/amp, ~/.kimi-code, ~/.local/share/opencode). For Amp, that folder is the amp directory under XDG_DATA_HOME, not XDG_DATA_HOME itself. Absence means "inherit from the next lower layer → hardcoded default." The field is resolved by jackin_config::resolve_sync_source_dir(cfg, agent, workspace, role) — the same three-layer precedence as resolve_mode.
Before (v1alpha5 config with Claude sync mode):
version = "v1alpha5"
[claude]
auth_forward = "sync"After (v1alpha6 — no change needed; sync_source_dir defaults to None):
version = "v1alpha6"
[claude]
auth_forward = "sync"Example with explicit source folder (operator sets a company-specific folder):
version = "v1alpha6"
[claude]
auth_forward = "sync"
sync_source_dir = "/Users/alice/company/.claude"Workspace v1alpha5 — 2026-05-26
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Workspace (workspaces/<name>.toml) | v1alpha4 | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-v1alpha4/meta.toml |
Summary: moves the workspace-level op_account onto each op:// env reference as a per-ref account key, then removes the top-level op_account. A workspace can hold references from different 1Password accounts; a single workspace-level account cannot serve them, so a reference whose vault lives in a non-default account previously failed to resolve. Each reference now records the account it was created under, and reads pin to it. The migration walks [env], every [roles.<role>.env], [github.env], and every [roles.<role>.github.env] table, stamping account onto each op reference; plain string values are untouched. A workspace with no op_account is a no-op restamp.
Before (v1alpha4 workspace):
version = "v1alpha4"
workdir = "/workspace/prod"
op_account = "ACCT123"
[env]
CLAUDE_CODE_OAUTH_TOKEN = { op = "op://v/i/f", path = "Vault/Item/Field" }
PLAIN_ENV = "literal-value"After (v1alpha5):
version = "v1alpha5"
workdir = "/workspace/prod"
[env]
CLAUDE_CODE_OAUTH_TOKEN = { op = "op://v/i/f", path = "Vault/Item/Field", account = "ACCT123" }
PLAIN_ENV = "literal-value"Config v1alpha5 — 2026-05-17
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | v1alpha4 | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-v1alpha4/meta.toml |
Summary: Introduces the [git] table with two independent boolean fields (both default false): coauthor_trailer enables Co-authored-by agent trailer injection, and dco enables Signed-off-by DCO injection. When either flag is enabled, jackin installs a prepare-commit-msg hook inside containers via core.hooksPath; each trailer is injected only when its corresponding flag is set, and both are deduplicated. The migration is a no-op restamp.
Before (v1alpha4 config):
version = "v1alpha4"
[roles.the-architect]
git = "https://github.com/jackin-project/jackin-the-architect.git"
trusted = trueAfter (v1alpha5):
version = "v1alpha5"
[roles.the-architect]
git = "https://github.com/jackin-project/jackin-the-architect.git"
trusted = truev1alpha4 — 2026-05-17
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | v1alpha3 | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-v1alpha3/meta.toml |
Workspace (workspaces/<name>.toml) | v1alpha3 | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-v1alpha3/meta.toml |
Manifest (jackin.role.toml) | v1alpha3 | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> | crates/jackin/tests/fixtures/migrations/manifest/from-v1alpha3/meta.toml |
Summary: formalizes Kimi agent support after v1alpha3 shipped with OpenCode. The kimi enum variant in agents, the [kimi] manifest table, and the [kimi] auth config tables require v1alpha4; older-stamped files still load as long as they do not use Kimi-specific fields. The migration is a no-op restamp: no field renames, no data moved.
Before (v1alpha3 manifest with Kimi):
version = "v1alpha3"
dockerfile = "Dockerfile"
agents = ["claude", "kimi", "opencode"]
[claude]
plugins = []
[kimi]
model = "kimi-k2"
[opencode]
model = "zai-coding-plan/glm-5.1"After (v1alpha4):
version = "v1alpha4"
dockerfile = "Dockerfile"
agents = ["claude", "kimi", "opencode"]
[claude]
plugins = []
[kimi]
model = "kimi-k2"
[opencode]
model = "zai-coding-plan/glm-5.1"v1alpha3 — 2026-05-14
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | v1alpha2 | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-v1alpha2/meta.toml |
Workspace (workspaces/<name>.toml) | v1alpha2 | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-v1alpha2/meta.toml |
Manifest (jackin.role.toml) | v1alpha2 | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> | crates/jackin/tests/fixtures/migrations/manifest/from-v1alpha2/meta.toml |
Summary: formalizes OpenCode agent support. The opencode enum variant in agents, the [opencode] manifest table, and the [opencode] auth config tables require v1alpha3; older-stamped files still load as long as they do not use OpenCode-specific fields. The migration is a no-op restamp: no field renames, no data moved.
Before (v1alpha2 manifest with OpenCode):
version = "v1alpha2"
dockerfile = "Dockerfile"
agents = ["claude", "opencode"]
[claude]
plugins = []
[opencode]
model = "zai-coding-plan/glm-5.1"After (v1alpha3):
version = "v1alpha3"
dockerfile = "Dockerfile"
agents = ["claude", "opencode"]
[claude]
plugins = []
[opencode]
model = "zai-coding-plan/glm-5.1"v1alpha2 — 2026-05-11
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | v1alpha1 | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-v1alpha1/meta.toml |
Workspace (workspaces/<name>.toml) | v1alpha1 | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-v1alpha1/meta.toml |
Manifest (jackin.role.toml) | v1alpha1 | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> | crates/jackin/tests/fixtures/migrations/manifest/from-v1alpha1/meta.toml |
Summary: restamps existing v1alpha1 files as v1alpha2 and moves version = "v1alpha2" to the first line. This is intentionally a sequential migration rather than a silent rewrite of v1alpha1: operators already have v1alpha1 files where the version key may sit below other top-level keys.
Before (v1alpha1 workspace file — excerpt):
workdir = "/workspace/prod"
allowed_roles = ["the-architect"]
last_role = "the-architect"
version = "v1alpha1"After (v1alpha2):
version = "v1alpha2"
workdir = "/workspace/prod"
allowed_roles = ["the-architect"]
last_role = "the-architect"v1alpha1 — 2026-05-10
| File kind | Predecessor | Migration mode | Fixture |
|---|---|---|---|
Config (config.toml) | legacy | Automatic on startup | crates/jackin/tests/fixtures/migrations/config/from-legacy/meta.toml |
Workspace (workspaces/<name>.toml) | legacy | Automatic on startup | crates/jackin/tests/fixtures/migrations/workspace/from-legacy/meta.toml |
Manifest (jackin.role.toml) | legacy | Desktop: jackin role migrate <role-repo-path>; CI/automation: jackin-role migrate <role-repo-path> | crates/jackin/tests/fixtures/migrations/manifest/from-legacy/meta.toml |
Summary: introduces the top-level version field. The migration adds version = "v1alpha1" to every file kind and leaves all existing keys, comments, and section ordering untouched. Splitting legacy [workspaces.<name>] tables out of config.toml runs as a separate startup step (see Configuration File); the version migration above is independent of that split.
Before (legacy operator config — excerpt):
[roles.agent-smith]
git = "https://github.com/jackin-project/jackin-agent-smith.git"
trusted = trueAfter (v1alpha1):
version = "v1alpha1"
[roles.agent-smith]
git = "https://github.com/jackin-project/jackin-agent-smith.git"
trusted = trueVersion format
Schema versions use Kubernetes-style names:
| Form | Meaning |
|---|---|
v1alpha1 | Unstable schema for the future stable v1 family |
v1alpha2 | Next unstable schema in the same family |
v1beta1 | More mature but still not stable |
v1 | Stable schema |
The current project is still unstable, so all current schemas use the v1alpha* family.
config.toml
legacy
legacy means the file has no top-level version field. For operator
config, jackin migrates this automatically before parsing.
Change to v1alpha1 | Detail |
|---|---|
| Added | version = "v1alpha1" |
| Preserved | Global agent auth, GitHub auth, env, role sources, Docker mounts |
| Removed | None |
| Renamed | None |
The split-from-config.toml work for legacy [workspaces.<name>] tables runs as a separate startup migration alongside the version stamp; see Configuration File.
v1alpha6
| Field | Type | Required | Since | Notes |
|---|---|---|---|---|
version | string | Yes | v1alpha1 | Current value: v1alpha6 |
[claude].auth_forward | string | No | legacy | sync, api_key, oauth_token, or ignore |
[claude].sync_source_dir | string | No | v1alpha6 | Host folder override for sync auth forwarding; omitted means use the per-agent default |
[codex].auth_forward | string | No | legacy | sync, api_key, or ignore |
[codex].sync_source_dir | string | No | v1alpha6 | Host folder override for sync auth forwarding; omitted means use the per-agent default |
[amp].auth_forward | string | No | legacy | sync, api_key, or ignore |
[amp].sync_source_dir | string | No | v1alpha6 | Host folder override for sync auth forwarding; omitted means use the per-agent default |
[kimi].auth_forward | string | No | v1alpha4 | sync, api_key, or ignore |
[kimi].sync_source_dir | string | No | v1alpha6 | Host folder override for sync auth forwarding; omitted means use the per-agent default |
[opencode].auth_forward | string | No | v1alpha3 | sync, api_key, or ignore |
[opencode].sync_source_dir | string | No | v1alpha6 | Host folder override for sync auth forwarding; omitted means use the per-agent default |
[github].auth_forward | string | No | legacy | sync, token, or ignore |
[github.env] | table | No | legacy | Operator env values for GitHub token auth |
[env] | table | No | legacy | Global operator env layer |
[roles.<role>] | table | No | legacy | Role source config |
[roles.<role>].git | string | Yes when role is declared | legacy | Role repository URL |
[roles.<role>].trusted | bool | No | legacy | Trust-on-first-use state |
[roles.<role>.env] | table | No | legacy | Per-role operator env layer |
[docker.mounts] | table | No | legacy | Global and scoped Docker mount config |
[git].coauthor_trailer | bool | No | v1alpha5 | Enable Co-authored-by agent trailer via prepare-commit-msg hook; omitted when false |
[git].dco | bool | No | v1alpha5 | Enable Signed-off-by DCO trailer via prepare-commit-msg hook; omitted when false |
[workspaces] | table | No | legacy | Legacy read-only compatibility location; migrated into workspace files |
Full operator configuration examples live in Configuration File.
Workspace files
Workspace files live at ~/.config/jackin/workspaces/<name>.toml.
legacy
legacy means either a standalone workspace file with no version, or
a [workspaces.<name>] table embedded in legacy config.toml.
Change to v1alpha1 | Detail |
|---|---|
| Added | version = "v1alpha1" |
| Preserved | Workdir, mounts, role selection, env, auth overrides, keep-awake, 1Password account |
| Removed | None |
| Renamed | None |
Legacy embedded [workspaces.<name>] tables in config.toml are split into per-workspace files by a separate startup migration; see Configuration File.
v1alpha6
| Field | Type | Required | Since | Notes |
|---|---|---|---|---|
version | string | Yes | v1alpha1 | Current value: v1alpha6 |
workdir | string | Yes | legacy | Container working directory |
allowed_roles | string array | No | legacy | Roles allowed in this workspace |
default_role | string | No | legacy | Preferred role for new sessions |
default_agent | string | No | legacy | claude, codex, amp, kimi, or opencode; omitted lets launch resolution use the role manifest or prompt |
last_role | string | No | legacy | Last role selected by the operator |
git_pull_on_entry | bool | No | legacy | Pull mounted repositories on entry |
[env] | table | No | legacy | Workspace operator env layer; each op:// reference carries an optional account (the 1Password account it resolves against), since v1alpha5 |
[[mounts]] | array of tables | No | legacy | Workspace mounts |
[[mounts]].src | string | Yes | legacy | Host path |
[[mounts]].dst | string | Yes | legacy | Container path |
[[mounts]].readonly | bool | No | legacy | Defaults to false |
[[mounts]].isolation | string | No | legacy | shared, worktree, or clone; legacy default is shared |
[keep_awake].enabled | bool | No | legacy | macOS keep-awake toggle |
[claude].auth_forward | string | No | legacy | Workspace Claude auth override |
[claude].sync_source_dir | string | No | v1alpha6 | Workspace Claude host folder override for sync auth forwarding |
[codex].auth_forward | string | No | legacy | Workspace Codex auth override |
[codex].sync_source_dir | string | No | v1alpha6 | Workspace Codex host folder override for sync auth forwarding |
[amp].auth_forward | string | No | legacy | Workspace Amp auth override |
[amp].sync_source_dir | string | No | v1alpha6 | Workspace Amp host folder override for sync auth forwarding |
[kimi].auth_forward | string | No | v1alpha4 | Workspace Kimi auth override |
[kimi].sync_source_dir | string | No | v1alpha6 | Workspace Kimi host folder override for sync auth forwarding |
[opencode].auth_forward | string | No | v1alpha3 | Workspace OpenCode auth override |
[opencode].sync_source_dir | string | No | v1alpha6 | Workspace OpenCode host folder override for sync auth forwarding |
[github].auth_forward | string | No | legacy | Workspace GitHub auth override |
[github.env] | table | No | legacy | Workspace GitHub token env values; op:// references carry the per-ref account since v1alpha5 |
[roles.<role>.env] | table | No | legacy | Per-workspace, per-role env override; op:// references carry the per-ref account since v1alpha5 |
[roles.<role>.claude].auth_forward | string | No | legacy | Per-workspace, per-role Claude auth override |
[roles.<role>.claude].sync_source_dir | string | No | v1alpha6 | Per-workspace, per-role Claude host folder override for sync auth forwarding |
[roles.<role>.codex].auth_forward | string | No | legacy | Per-workspace, per-role Codex auth override |
[roles.<role>.codex].sync_source_dir | string | No | v1alpha6 | Per-workspace, per-role Codex host folder override for sync auth forwarding |
[roles.<role>.amp].auth_forward | string | No | legacy | Per-workspace, per-role Amp auth override |
[roles.<role>.amp].sync_source_dir | string | No | v1alpha6 | Per-workspace, per-role Amp host folder override for sync auth forwarding |
[roles.<role>.kimi].auth_forward | string | No | v1alpha4 | Per-workspace, per-role Kimi auth override |
[roles.<role>.kimi].sync_source_dir | string | No | v1alpha6 | Per-workspace, per-role Kimi host folder override for sync auth forwarding |
[roles.<role>.opencode].auth_forward | string | No | v1alpha3 | Per-workspace, per-role OpenCode auth override |
[roles.<role>.opencode].sync_source_dir | string | No | v1alpha6 | Per-workspace, per-role OpenCode host folder override for sync auth forwarding |
[roles.<role>.github].auth_forward | string | No | legacy | Per-workspace, per-role GitHub auth override |
[roles.<role>.github.env] | table | No | legacy | Per-workspace, per-role GitHub token env values; op:// references carry the per-ref account since v1alpha5 |
jackin.role.toml
legacy
legacy means the manifest has no top-level version field. jackin'
does not auto-write role repositories during normal role loading. Run
jackin role migrate <role-repo-path> to rewrite a local copy on a
desktop, or jackin-role migrate <role-repo-path> in CI and
automated migration jobs.
Change to v1alpha1 | Detail |
|---|---|
| Added | version = "v1alpha1" |
| Moved | None |
| Preserved | Dockerfile, published image, identity, agents, runtime settings, hooks, env prompts |
| Removed | None |
| Renamed | None |
v1alpha4
| Field | Type | Required | Since | Notes |
|---|---|---|---|---|
version | string | Yes | v1alpha1 | Current value: v1alpha4 |
dockerfile | string | Yes | legacy | Relative path to role Dockerfile |
published_image | string | No | legacy | Pre-built role image override |
agents | string array | No | legacy | claude, codex, or amp; opencode added in v1alpha3; kimi added in v1alpha4; omitted means Claude-only |
[identity].name | string | No | legacy | Display name |
[claude].model | string | No | legacy | Claude model override |
[claude].plugins | string array | No | legacy | Claude plugin identifiers |
[[claude.marketplaces]] | array of tables | No | legacy | Claude plugin marketplace registrations |
[[claude.marketplaces]].source | string | Yes | legacy | Marketplace source |
[[claude.marketplaces]].sparse | string array | No | legacy | Sparse checkout paths |
[codex].model | string | No | legacy | Codex model override |
[amp] | table | No | legacy | Empty marker table for Amp support |
[kimi].model | string | No | v1alpha4 | Kimi model override passed to kimi --model |
[opencode].model | string | No | v1alpha3 | OpenCode model override in provider/model format (e.g. zai-coding-plan/glm-5.1) |
[hooks].setup_once | string | No | legacy | Relative path to a one-time install script that runs on first launch only |
[hooks].source | string | No | legacy | Relative path to a script dot-sourced into the entrypoint shell so export and PATH mutations reach the launched agent |
[hooks].preflight | string | No | legacy | Relative path to a per-start preflight script |
[env.<NAME>] | table | No | legacy | Interactive env prompt declaration |
[env.<NAME>].default | string | No | legacy | Default env value |
[env.<NAME>].interactive | bool | No | legacy | Prompt operator before launch |
[env.<NAME>].skippable | bool | No | legacy | Allow empty response |
[env.<NAME>].prompt | string | No | legacy | Prompt text |
[env.<NAME>].options | string array | No | legacy | Fixed choices |
[env.<NAME>].depends_on | string array | No | legacy | Dependency references such as env.PROJECT |
Full role manifest examples live in Role Manifest.
Migration retention
jackin' does not keep every migration forever. If a file is too old and the current binary no longer includes the full path to the current schema, jackin' errors and tells the operator to upgrade through an older jackin' first.
Migration entry points:
| File kind | Code entry point |
|---|---|
| Config | crates/jackin-config/src/migrations.rs |
| Workspace | crates/jackin-config/src/migrations.rs |
| Role manifest | crates/jackin-manifest/src/migrations.rs |