# Kanbus Configuration (Python + Rust)

Kanbus uses a single configuration file to define project identity, hierarchy, workflows, and priorities. The same schema and behaviors apply to both implementations.

## Location

```
.kanbus.yml
```

The file must exist; there is no fallback path. A missing file is a hard error with a clear message.

## Event history storage

- Event files are written to `project/events/` (shared issues) or `project-local/events/` (local issues).
- Each event is a standalone JSON file named with a UTC timestamp and UUID suffix. Filenames use hyphens instead of colons in the time portion so they are valid on Windows, e.g. `2026-02-21T06-09-40.180Z__<event_id>.json`.
- Inside the JSON record, `occurred_at` remains RFC3339 with colons, e.g. `2026-02-21T06:09:40.180Z`.
- Event records include `schema_version`, `event_id`, `issue_id`, `event_type`, `occurred_at`, `actor_id`, and an action-specific `payload`.
- Beads mode (`kanbus --beads`) still writes event history to `project/events/` even when issue records live in `.beads/issues.jsonl`.
- Some repositories, including Kanbus itself, gitignore `project/events/` because event files are runtime audit data. Other Anthus repos may commit them. A missing `project/events/` directory in git does not mean Kanbus skips event recording.

## Loading semantics (dotyaml parity)

1. Load `.env` (unless disabled), setting variables only when they are not already defined.
2. Parse `.kanbus.yml`, interpolating `{{ VAR|default }}` expressions from the environment.
3. Flatten the resulting config into environment variables with the `KANBUS_` prefix (nested keys joined by underscores, lists as comma-separated strings, booleans as `true`/`false`, null as empty string).
4. Never override an environment variable that is already set unless `override=true` is explicitly requested.

These rules mirror the Python `dotyaml` package and must be matched in the Rust crate.

## Field reference

Every key accepted at the top level of `.kanbus.yml`. Unknown keys are errors. This table is generated from the configuration model by `tools/generate_configuration_reference.py` and checked in CI.

<!-- BEGIN GENERATED: project-configuration-fields -->
<!-- Generated by tools/generate_configuration_reference.py; do not edit by hand. -->
| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `project_directory` | `str` | required | Relative path to the primary project directory. |
| `virtual_projects` | `Dict[str, VirtualProjectConfig]` | `{}` | Map of label to virtual project configuration. |
| `new_issue_project` | `Optional[str]` | `None` | Project label for new issue creation routing. |
| `ignore_paths` | `List[str]` | `[]` | Paths to exclude from project discovery. |
| `console_port` | `Optional[int]` | `None` | Optional port for the web console server. |
| `project_key` | `str` | required | Issue ID project key (prefix). |
| `project_id` | `Optional[str]` | `None` | Stable UUID identifying the project across clones and worktrees; scopes realtime topics. |
| `name` | `Optional[str]` | `None` | Optional human-readable board title. |
| `project_management_template` | `Optional[str]` | `None` | Optional template path for CONTRIBUTING_AGENT.md. |
| `methodologies` | `Optional[List[Union[str, ProjectMethodologyBundleReference]]]` | `None` | Methodology names assembled into CONTRIBUTING_AGENT.md. |
| `hierarchy` | `List[str]` | required | Hierarchy ordering. |
| `types` | `List[str]` | required | Non-hierarchical types. |
| `workflows` | `Dict[str, Dict[str, List[str]]]` | required | Workflow definitions. |
| `transition_labels` | `Dict[str, Dict[str, Dict[str, str]]]` | `{}` | Display label for every workflow transition, keyed by workflow, from-status, then to-status. |
| `initial_status` | `str` | required | Initial status for new issues. |
| `priorities` | `Dict[int, PriorityDefinition]` | required | Priority map. |
| `default_priority` | `int` | required | Default priority. |
| `assignee` | `Optional[str]` | `None` | Default assignee identifier. |
| `time_zone` | `Optional[str]` | `None` | Preferred display time zone. |
| `statuses` | `List[StatusDefinition]` | `[]` | Status definitions with display names, categories, and colors. |
| `categories` | `List[CategoryDefinition]` | `[]` | Status category definitions with display names and colors. |
| `sort_order` | `Dict[str, Any]` | `{}` | Optional status/category sort rules for console columns. |
| `type_colors` | `Dict[str, str]` | `{}` | Optional map of issue type to color name. |
| `beads_compatibility` | `bool` | `False` | Default Beads compatibility mode. |
| `events` | `bool` | `True` | Whether Kanbus generates events under project/events and commits them with kbs commit. |
| `wiki_directory` | `Optional[str]` | `None` | Optional directory containing wiki pages. |
| `ai` | `Optional[AiConfiguration]` | `None` | Optional AI provider and model configuration. |
| `right_now` | `RightNowConfiguration` | see section | Right-now summary configuration. |
| `standup` | `StandupConfiguration` | see section | Standup report configuration. |
| `jira` | `Optional[JiraConfiguration]` | `None` | Optional Jira synchronization configuration. |
| `snyk` | `Optional[SnykConfiguration]` | `None` | Optional Snyk vulnerability synchronization configuration. |
| `realtime` | `RealtimeConfig` | see section | Realtime gossip configuration. |
| `coordination` | `CoordinationConfiguration` | see section | Multi-agent coordination configuration. |
| `router` | `Optional[IssueRouterConfiguration]` | `None` | Optional issue router configuration. |
| `overlay` | `OverlayConfig` | see section | Overlay cache configuration. |
| `hooks` | `HooksConfiguration` | see section | Lifecycle hook configuration. |
| `github_security` | `Optional[GithubSecurityConfiguration]` | `None` | Optional GitHub security synchronization configuration. |
<!-- END GENERATED: project-configuration-fields -->

## Minimal configuration

```yaml
project_directory: project
project_key: kbs
hierarchy: [initiative, epic, task, sub-task]
types: [bug, story, chore]
workflows:
  default:
    open: [in_progress, closed]
    in_progress: [open, closed]
    closed: [open]
initial_status: open
priorities:
  0: {name: critical}
  1: {name: high}
  2: {name: medium}
  3: {name: low}
default_priority: 2
transition_labels:
  default:
    open: {in_progress: Start work, closed: Drop}
    in_progress: {open: Pause, closed: Finish}
    closed: {open: Reopen}
categories:
  - {name: To do}
  - {name: Done}
statuses:
  - {key: open, name: Open, category: To do}
  - {key: in_progress, name: In Progress, category: To do}
  - {key: closed, name: Closed, category: Done}
```

`kbs init` writes a complete starting configuration; edit that rather than starting from scratch.

## Validation rules

Beyond the schema, loading fails when:

- `hierarchy` is empty, or a type name appears twice across `hierarchy` and `types`.
- `workflows.default` is missing, or `transition_labels` does not label every transition of every workflow.
- `default_priority` is not a key of `priorities`.
- `categories` or `statuses` is empty, a category or status key or name is duplicated, or a status references an undefined category.
- `wiki_directory` escapes the project root.
- A virtual project label equals `project_key`, or `new_issue_project` names an unknown project.
- The enabled issue router references undefined provider profiles, classes, or statuses, or `review_wip` exceeds `project_wip`.

## Environment integration

- Prefix for exported env vars is fixed to `KANBUS_`.
- `.env` loading is enabled by default; disable with a loader flag.
- `override=true` is an opt-in flag to let YAML values overwrite existing env vars; default is non-overriding.

## Realtime gossip + overlay

```yaml
realtime:
  transport: auto            # auto | uds | mqtt
  broker: auto               # auto | off | mqtt://... | mqtts://...
  autostart: false           # autostart mosquitto if unreachable (the gossip bridge always autostarts)
  keepalive: false           # keep autostarted broker running
  uds_socket_path: null      # override UDS socket path
  mqtt_custom_authorizer_name: null  # optional AWS IoT custom authorizer name
  mqtt_api_token: null       # optional API token for custom authorizer auth
  topics:
    project_events: "projects/{project}/events"

overlay:
  enabled: true
  ttl_s: 86400

coordination:
  providers: [git]             # git | [mqtt, git] | [mutex_api, mqtt, git]
  contention_window: 5s        # Competing claims use this window for tie-break
  default_lease_ttl: 300s      # Initial claim lifetime and default renewal step
  mutex_api:
    endpoint: null              # e.g. https://mutex.example.test
    bearer_token: null          # sent as Authorization: Bearer <token>
```

Coordination durations are positive integer values followed by `s`, `m`, or
`h` (for example `5s`, `2m`, or `1h`). The provider lists are ordered strongest
first: `[git]`, `[mqtt, git]`, or `[mutex_api, mqtt, git]`. Git records remain
the durable coordination history. Git-only claims are soft; MQTT adds shared
visibility; the Mutex API adds hard exclusion for live leases. The Mutex API
client uses `POST`, `PUT`, `DELETE`, and `GET` on
`{endpoint}/api/coordination/leases/{resource}` with bearer authentication.
When its endpoint or token is missing, or the service is unavailable, Kanbus
falls back through configured MQTT and Git providers. A live-lease conflict
and owner mismatch remain errors. For a hard claim, Kanbus appends the Git
claim event only after the API accepts the acquire; if that append fails it
tries to release the lease and reports the durable-write failure.

Claims, renewals, and releases are immutable records in `project/events/`,
keyed by the resource in the event's `issue_id` subject field. These events do
not change issue assignees or statuses. Git coordination is soft: workers can
record competing claims, and the winner is selected from claims in the initial
contention window by `(claim_id, owner, event_id)`. Later claims do not replace
an active winner; expiry or release makes the resource eligible again.

Notes:

- `broker=auto` uses discovery precedence: `~/.kanbus/run/broker.json` then `mqtt://127.0.0.1:1883`. This is an explicit exception to the no-fallback policy.
- Overlay snapshots live under `project/.overlay/` and are safe to delete.
- Environment overrides (higher precedence than YAML): `KANBUS_COORDINATION_MUTEX_API_ENDPOINT`, `KANBUS_COORDINATION_MUTEX_API_BEARER_TOKEN`, `KANBUS_REALTIME_TRANSPORT`, `KANBUS_REALTIME_BROKER`, `KANBUS_REALTIME_AUTOSTART`, `KANBUS_REALTIME_KEEPALIVE`, `KANBUS_REALTIME_UDS_SOCKET_PATH`, `KANBUS_REALTIME_MQTT_CUSTOM_AUTHORIZER_NAME`, `KANBUS_REALTIME_MQTT_API_TOKEN`, `KANBUS_REALTIME_TOPICS_PROJECT_EVENTS`, `KANBUS_OVERLAY_ENABLED`, `KANBUS_OVERLAY_TTL_S`.

## Examples

### Default software project

```yaml
project_key: "KAN"
issue_types: [bug, story, chore]
workflow_bindings:
  bug: default
  story: default
  chore: default
initial_status: open
priorities: [critical, high, medium, low, trivial]
default_priority: medium
priority_import_aliases:
  P0: critical
  P1: high
  P2: medium
  P3: low
priority_accept_unmapped: true
```

### Beads import mapping

```yaml
project_key: "BDX"
issue_types: [bug, task]
workflow_bindings:
  bug: default
  task: default
priorities: [urgent, high, normal, low]
default_priority: normal
priority_import_aliases:
  P0: urgent
  P1: high
  P2: normal
  P3: low
priority_accept_unmapped: false
```

## Methodologies

`methodologies` selects the process guidance assembled into the generated `CONTRIBUTING_AGENT.md`, in the listed order. Entries are built-in names (`bdd`, `git-flow`) or `{ path: <folder> }` for a bundle in the repository. When the key is absent the default is `[bdd, git-flow]`; `methodologies: []` produces core guidance only.

```yaml
methodologies:
  - bdd
  - git-flow
  - path: methodologies/crap
```

See [Methodologies](methodologies.html) for the bundle format, tools, and the `kbs methodology` commands.

## AI and right-now defaults

Kanbus routes LLM calls through LiteLLM (`ai.provider: litellm`). The product default model is `gpt-5.6-luna` (OpenAI via `OPENAI_API_KEY`).

```yaml
ai:
  provider: litellm
  model: gpt-5.6-luna

right_now:
  enabled: true
  default_tree_expanded: false
  max_length: 120
  model: gpt-5.6-luna
```

Right-now summaries are generated only when they are displayed: `kbs now` and the console Now panel generate a summary that is missing or older than the issue or one of its direct children. Board-mutating commands (`create`, `update`, `comment`, `dep`, `delete`) never call an LLM. Each generation is logged to `project/events/llm_usage.jsonl`; a call whose model has no known price is logged with `"cost": null`, and `kbs cost` reports it under `Unpriced Calls` instead of counting it as $0.

### Credentials

Run `kbs setup ai` (or `kanbus setup ai`) once per user to store an LLM API key. It prompts for
the key (hidden input) and writes it to `~/.kanbus.env` (mode `600`), so every project on the
machine picks it up without a per-project `.env`.

Lookup order: shell environment, then `~/.kanbus.env`, then the project `.env` file.

```bash
kbs setup ai                         # interactive prompt
pbpaste | kbs setup ai --from-stdin  # read from stdin, e.g. a clipboard or secrets manager
kbs setup ai --variable ANTHROPIC_API_KEY   # store a different provider's variable
kbs setup ai --status                # show which source (if any) provides the key
```

Check `kbs setup ai --status` or `kbs doctor` to confirm a key is configured. Never store API
keys in `.kanbus.yml`.

For a LiteLLM proxy instead of a direct provider key, set `LITELLM_API_KEY` alongside
`LITELLM_API_BASE` or `LITELLM_PROXY_URL` (the Rust `kbs now`/standup path also accepts
`OPENAI_API_BASE`); these follow the same shell-environment / `~/.kanbus.env` / project-`.env`
lookup order.

#### Machine-wide settings

Any `KANBUS_*` environment override can also live in `~/.kanbus.env`, so every clone and
worktree on the machine shares it, using the same `kbs setup env NAME --value ...` (or
`--from-stdin`) command used for credentials. Environment overrides always beat `.kanbus.yml`.
This is useful for settings that should be consistent machine-wide rather than per project, such
as the realtime transport/broker/autostart/keepalive variables, `KANBUS_REALTIME_MQTT_API_TOKEN`,
`KANBUS_REALTIME_MQTT_CUSTOM_AUTHORIZER_NAME`, and the coordination mutex endpoint/token
(`KANBUS_COORDINATION_MUTEX_API_ENDPOINT`, `KANBUS_COORDINATION_MUTEX_API_BEARER_TOKEN`).

```bash
kbs setup env KANBUS_REALTIME_BROKER --value mqtt://127.0.0.1:1883
pbpaste | kbs setup env KANBUS_REALTIME_MQTT_API_TOKEN --from-stdin
```

## Standup windows

Standup reports resolve completed vs active buckets from shared window settings. Field names are locked for config, CLI, and console API:

```yaml
standup:
  window: rolling        # rolling | calendar
  lookback: 24h          # rolling duration; hours required (1d = 24h sugar)
  skip_weekends: false   # calendar only; ignored when window is rolling
  timezone: America/New_York  # optional IANA zone; unset uses system local
```

Defaults:

- Global: `window: rolling`, `lookback: 24h`, `skip_weekends: false`
- `meeting-script` profile override: `window: calendar`, `skip_weekends: true`
- `director-brief` profile: inherits global rolling + 24h

`standup.lookback_hours` is not accepted (`extra=forbid`). Migrate to `standup.lookback` with a duration string such as `24h` or `1d`.

CLI overrides (same names as config/API): `--window`, `--lookback`, `--skip-weekends` / `--no-skip-weekends`.

## Optional sync sections

```yaml
github_security:
  repo: owner/repo
  dependabot:
    min_severity: low
    state: open
    parent_epic: kanbus-abc123
```

- `github_security.repo` is optional; if omitted, Kanbus auto-detects from `git remote origin`.
- `github_security.dependabot.min_severity` defaults to `low`.
- `github_security.dependabot.state` defaults to `open`.
- `github_security.dependabot.parent_epic` is optional.
