Balda is a self-hosted engineering agent that lives in your team chat and works inside your project.
You can give it a task in chat, open a focused topic for a piece of work, run a longer goal loop, or wire external events into the same workflow. Balda keeps context, uses your configured tools, and returns something reviewable: a summary, changed files, validation output, a commit, or a concrete next step.
- chat-native engineering help in Telegram, Zulip, or Slack
- focused task threads instead of one shared bot conversation
- long-running goal execution with progress updates and final results
wedgestyle operation: put the agent in the middle of your team workflow so chat, tools, schedules, and external events all feed the same execution path- self-hosted deployment close to your repo, config, and credentials
You need:
- one chat surface: Telegram, Zulip, or Slack
- one supported provider CLI installed on the host or in Docker:
codex,opencode,copilot,gemini, orclaude - Node.js/npm, unless you run the Docker Compose path
Install:
npm install -g -y @normahq/baldaInitialize in your project:
balda initStart:
balda startbalda init creates .config/balda/config.yaml, initializes
.config/balda/state.db, detects available provider CLIs, and prints the next
step for your selected chat provider.
For Telegram, authenticate the owner with the command printed by balda init:
/start owner=<owner_token>
Then send a normal direct message to the bot, or open an isolated topic:
/topic release
From there you can:
- ask for ordinary help in chat
- start a goal loop with
/goalkeeper <objective> - stop the current turn with
/cancel - reset the current session with
/reset
Send a message in the session where you want work to happen. Balda keeps that conversation as the execution context.
Use /topic <name> to create a separate session for a task, incident, release,
or stream of work.
Use /goalkeeper <objective> when you want Balda to keep working until there is a
result to review.
Balda will:
- work in repeated passes
- post progress updates
- ask follow-up questions when critical input is missing
- return a terminal result with outcome details
See docs/goal-workflow.md for the detailed goal contract.
Balda can act as a wedge between team chat and the rest of your engineering system:
- chat messages start work
- scheduled jobs wake work up
- inbound webhooks turn external events into session work
- the same session can continue through follow-up questions and delayed work
This is useful when you want one operational path for human requests, automation, and agent execution instead of separate bots and scripts.
- Telegram
- Zulip
- Slack
Balda maps each conversation scope to its own session:
- Telegram direct chat or topic
- Zulip stream + topic
- Slack thread
Balda ships a root Dockerfile and compose.yaml for local Docker Compose deployment.
The current directory is mounted as /workspace, so Balda sees your checkout,
config, git metadata, and local state.
docker compose build balda
docker compose run --rm balda init
docker compose up -d baldaPolling mode is the default, so Telegram does not require publishing a port. Webhook deployment details live in docs/balda.md.
Balda publishes a release image at ghcr.io/baldaworks/balda:latest.
That image contains the balda binary only. It does not bundle provider CLIs.
Use it as a source stage in your own image and add the provider runtime you
want.
Example with Codex:
FROM node:24-bookworm-slim AS cli-builder
RUN npm install -g @openai/codex
FROM ghcr.io/baldaworks/balda:latest AS balda
FROM node:24-bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
git \
openssh-client \
ripgrep \
&& rm -rf /var/lib/apt/lists/*
COPY --from=cli-builder /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=cli-builder /usr/local/bin/codex /usr/local/bin/codex
COPY --from=balda /usr/local/bin/balda /usr/local/bin/balda
WORKDIR /workspace
ENTRYPOINT ["balda"]/start owner=<owner_token>— owner bootstrap in direct messages/start invite=<invite_token>— collaborator onboarding/topic <name>— open a focused session/goalkeeper <objective>— run a longer goal loop/goalkeeper clear— stop active goal work in the current session/cancel— stop the current turn/resetor/restart— clear the current session and start fresh/locator— show the current session locator for scheduler/webhook routing
Full command behavior is documented in docs/balda.md.
Balda loads .config/balda/config.yaml and then applies BALDA_* environment
overrides. If a local .env exists, Balda loads it before resolving config.
Minimal shape:
runtime:
providers:
codex:
type: codex_acp
mcp_servers: {}
balda:
provider: codex
telegram:
token: ""Common settings:
balda.provider— which configured provider runtime to usebalda.telegram.token— Telegram bot tokenbalda.zulip.*— Zulip outgoing webhook bot credentials and receiver configbalda.slack.*— Slack bot token, signing secret, and HTTP receiver configbalda.webhooks.*— optional inbound webhook routesbalda.scheduler.jobs— recurring scheduled jobsbalda.workspace.*— workspace/worktree behavior for goal executionbalda.permissions.mode— agent permission policy:allow_all,ask, ordeny_allbalda.permissions.timeout— maximum wait for an interactive permission decision (default2m)balda.mcp_servers— MCP servers injected into Balda-started sessions
allow_all preserves historical behavior and should be used only where every
agent tool call is trusted. Production chat deployments should normally set
BALDA_PERMISSIONS_MODE=ask; unsupported channels, missing requester context,
cancellation, and timeout fail closed.
For complete configuration, examples, and provider-specific details, see docs/balda.md.
telegram token is required— runbalda initor setBALDA_TELEGRAM_TOKENno supported agent CLI detected— install or expose one ofcodex,opencode,copilot,gemini, orclaudebalda.provider is required— rerunbalda initor set a configured provider id manually- webhook or Slack/Zulip startup issues — verify the matching
balda.*integration settings in config - workspace import/export issues — check
balda.workspace.mode,balda.workspace.base_branch, and the git checkout Balda is running in
- Product and operator docs: docs/balda.md
- Goal workflow: docs/goal-workflow.md
- Architecture map: docs/architecture/index.md
- Telegram formatting: docs/telegram-formatting.md
- Zulip webhook setup: docs/zulip-webhook.md
- Slack setup: docs/slack.md
- Contributing: CONTRIBUTING.md
- GitHub Releases: https://github.com/baldaworks/balda/releases
- npm package: https://www.npmjs.com/package/@normahq/balda