Skip to content

Configuration Reference

The orchestrator is configured by one YAML file. It loads the path in ORCHESTRATOR_CONFIG, then /config/orchestrator.yml, then the classpath example. The environment is used only when a SecretRef names an environment variable.

apiVersion: smithy.ai/v1alpha1
kind: OrchestratorConfig

storage:
  database: /data/smithy.db
  metrics: /data/metrics.jsonl

runtime:
  docker:
    command: docker
    network: smithy-net
    taskImage: ghcr.io/smithy-ai/claude-task-default:dev
    caches: [pnpm, npm, maven, gradle]

agent:
  claude:
    model: claude-opus-5
    oauthToken: {env: CLAUDE_CODE_OAUTH_TOKEN}
    apiKey: {env: ANTHROPIC_API_KEY}
    turnTimeout: 60m          # budget for one agent turn; overrunning turns are killed
    takeoverTimeout: 5m       # budget for a turn a human drove from the dashboard
    takeoverTools: [Read, Glob, Grep, Bash, Edit, Write, WebFetch]

auth:
  admin:
    passwordHash: {env: ADMIN_PASSWORD_HASH}

connectors:
  forgejo-main:
    provider: forgejo
    url: http://forgejo:3000
    externalUrl: https://git.example.com
    webhookSecret: {env: FORGEJO_WEBHOOK_SECRET}
    actors:
      smithy:
        username: smithy-bot
        token: {env: SMITHY_FORGEJO_TOKEN}
        git:
          name: Smithy
          email: smithy@example.com
      architect:
        username: architect-bot
        token: {file: /run/secrets/architect_forgejo_token}
        git:
          name: Architect
          email: architect@example.com

defaults:
  vcs: forgejo-main
  issueTracker: event.source
  actor: smithy

workflows:
  definitionsDir: /config/workflows
  repositoryWorkflows: true
  defaults:
    branchPrefix: smithy/
    planApprovedLabel: Plan Approved

ci: {autofix: false}
knowledgebase:
  enabled: false
  url: http://knowledgebase:8000/mcp
  toolName: searchKnowledge

Unknown fields and invalid connector references fail startup. A deployment should mount the file read-only and mount writable storage separately:

volumes:
  - ./config/orchestrator.yml:/config/orchestrator.yml:ro
  - ./config/workflows:/config/workflows:ro
  - orchestrator-data:/data

Agent turn timeout

agent.claude.turnTimeout caps the wall-clock time one agent turn may take — 45m, 2h, 900s and PT45M are all accepted, and the built-in default is 60 minutes. The deadline is enforced inside the task container, so an overrunning turn is actually killed rather than left running after the orchestrator stops waiting for it.

A turn that hits the cap fails its step with:

Claude turn on <container> (session=<id>) exceeded its 60m budget and was killed.

Raise the value for workflows whose build stage legitimately runs longer, or split the step into smaller turns. Note that the budget is per turn, not per run: a workflow with five turns can run for five times this long.

agent.claude.takeoverTimeout (default 5 minutes) is the budget for a turn a person drove from the dashboard. It is deliberately much shorter: someone is waiting on that reply in a browser, so an unanswered request is a hung dashboard rather than a long job.

A run's turns all share one agent session, and that session cannot take two concurrent processes. Taking control does not interrupt a turn that is already running — the lease stops new events being dispatched, not work in flight — so a message sent mid-turn is refused with a 409 and

The agent on run <id> is in the middle of a turn.

rather than queued behind it. Wait for the turn to land, or stop the run.

agent.claude.takeoverTools is the tool list a human-driven turn runs with, defaulting to [Read, Glob, Grep, Bash, Edit, Write, WebFetch]. It is separate from the stage's tools: on purpose: a definition scopes tools per step to constrain the agent while it works on its own — read-only while planning, write while building — and that does not map onto whatever a person asks for after taking control.

Note that an empty tool list is not "no restrictions", it is "nothing allowed": the CLI is run without --allowedTools, and a headless turn on default permissions then refuses every tool call before executing it. The agent reports that it cannot read a file or push a branch, which reads as an agent refusing to cooperate rather than one that was handed no permissions.

Secrets

Every credential accepts exactly one secret source:

token: {env: SMITHY_GITHUB_TOKEN}
token: {file: /run/secrets/smithy_github_token}
token: {literal: local-development-only}

Environment and file references keep values out of the deployment file. Literal values are intended only for local development. Resolved values are never included in configuration diagnostics.

Connectors and actors

A connector ID such as github-main is the stable routing identity. provider selects the implementation (forgejo, gitlab, github, or jira). Webhooks use the connector ID:

https://smithy.example.com/webhooks/github-main

Actor identities are connector-specific and never inherit another actor's credentials. Every workflow actor must be configured on each connector it acts through; a missing identity fails the action instead of posting as the default actor.

Actors are logical identities scoped to a connector. Workflows refer to smithy, architect, or another logical name; the connector resolves it to a username, Jira account ID, credentials, and git identity. An actor omitted from a connector uses the default actor's credentials for outbound actions and is not recognized as an inbound assignee on that connector.

GitLab connectors may set tokenType: oauth2 (the default) or tokenType: private-token.

Jira with a VCS connector

Jira is an issue connector, never a VCS connector. A split deployment defines both:

connectors:
  gitlab-main:
    provider: gitlab
    url: https://gitlab.example.com
    webhookSecret: {env: GITLAB_WEBHOOK_SECRET}
    actors:
      smithy:
        username: smithy-bot
        token: {env: SMITHY_GITLAB_TOKEN}
        git: {name: Smithy, email: smithy@example.com}

  jira-product:
    provider: jira
    url: https://company.atlassian.net
    webhookSecret: {env: JIRA_WEBHOOK_SECRET}
    actors:
      smithy:
        accountId: abc123
        email: smithy@example.com
        apiToken: {env: SMITHY_JIRA_API_TOKEN}
    issueMapping:
      repositoryField: customfield_12345
      allowStoriesWithoutRepository: true
      planApprovedLabel: plan-approved
      planApprovedStatus: Ready for Smithy

defaults:
  vcs: gitlab-main
  issueTracker: event.source
  actor: smithy

Issue actions answer the connector that produced the event. Repository actions use that source when it is a VCS connector and otherwise use defaults.vcs.

Repository catalogs

Reusable coordinator catalogs belong in deployment configuration:

repositoryCatalogs:
  acme-product:
    - {source: gitlab-main, owner: acme, repo: api, description: HTTP API}
    - {source: gitlab-main, owner: acme, repo: web, description: Web client}

Workflow customization remains in /config/workflows, not in the main config. A small workflow file can extend the built-in coordinator and import a catalog:

apiVersion: smithy.ai/v1alpha1
kind: Workflow
metadata:
  name: acme-coordinator
  extends: feature-coordinator
vars:
  repositoryCatalog: acme-product
  storyRepos: [PRODUCT/PRODUCT]

The registry resolves repositoryCatalog into the workflow's vars.catalog and rejects unknown catalog names.

Repository-local configuration

Repository behavior stays with the repository. .smithy/config.yml currently supports the context repository:

context:
  repository: shared-guidelines

Repository-owned workflows live under .smithy/workflows/*.yml and can be disabled deployment-wide with workflows.repositoryWorkflows: false.