Balda exists to give teams a persistent AI worker they can assign real work to.
It takes work from the team's conversation, keeps project context, uses the team's tools, and keeps moving until there is a concrete result to review. A task can start as a message, a topic, a goal, a schedule, or an external event; Balda turns that intent into an active worker session.
The name comes from Pushkin's работник Балда: practical, direct, and focused on getting the job done. That is the project goal: an autonomous worker comrade for teams.
npm install -g -y @normahq/balda
balda init
balda start| Feature | What it means |
|---|---|
| Assignable work | People can give Balda a task or goal the same way they would assign work to a teammate. |
| Persistent worker context | Balda remembers team and project facts and carries task context across sessions, interruptions, and restarts. |
| Team conversation as work intake | Work can start from chat, topics, mentions, commands, schedules, or incoming external events. |
| Focused work sessions | Separate threads/topics can become separate work contexts, so different tasks do not collapse into one conversation. |
| Autonomous execution loops | Balda can keep working toward a goal, validate progress, and continue until there is a result. |
| Project tool access | Balda can use the same project tools the team relies on, including repo/workspace operations and configured integrations. |
| Event-driven work | Webhooks and scheduled triggers become inputs to agents, not just notifications. |
| Collaborative visibility | The team can see progress, cancel work, and manage who can assign work. |
| Reviewable outcomes | Balda should return something the team can evaluate: a summary, changed files, a commit, validation output, or a next action. |
| Operationally simple deployment | Teams can run Balda close to their project without building a platform first. |
- Pick a provider runtime.
- Connect a Telegram bot token.
- Chat, create topics, and let Balda persist session state, memory, and workspaces.
Balda runs one provider runtime per process and maps Telegram chats/topics to separate agent sessions. That keeps the bot simple to operate while preserving session boundaries.
You need:
- a Telegram bot token from BotFather
- at least one provider CLI for host installs:
codex,opencode,copilot,gemini, orclaude - Node.js/npm, unless you use the Docker Compose flow
Install Balda:
npm install -g -y @normahq/baldaInitialize Balda in your project:
balda initbalda init detects provider CLIs, validates the Telegram token, writes
.config/balda/config.yaml, initializes .config/balda/state.db, and prints
the next commands. By default, the Telegram token is stored in .env.
Start Balda:
balda startLocal repo development helper:
make devRun fake ingress scenarios (Telegram/webhook/scheduler command publish paths):
make scenariosInspect the runtime streams and consumers:
make runtime-stateReplay projection events through the deterministic projector replay suite:
make projection-replayAuthenticate in Telegram with the printed auth link, or send the printed command directly to your bot:
/start owner=<owner_token>
After owner auth, send a normal direct message to use the owner session. Create a named topic session when you want an isolated workspace and conversation:
/topic <name>
Balda ships a root Dockerfile and compose.yaml
for local Docker Compose runtime.
This path is designed for real project work. The current directory is mounted as
/workspace, so Balda sees your host checkout, .git, .env,
.config/balda/config.yaml, and .config/balda/state.db.
docker compose build balda
docker compose run --rm balda init
docker compose up -d baldaProvider credentials are not baked into the image. Authenticate with provider
environment variables or provider login commands run through Compose.
balda-home persists provider CLI home config across container recreates.
Polling mode is the default and does not require publishing a port. Webhook
setup and image details are documented in docs/balda.md.
Balda has built-in provider types for common CLIs and a generic provider adapter for anything else that speaks the same runtime protocol.
runtime:
providers:
my-agent:
type: generic_acp
generic_acp:
cmd: ["my-acp-agent", "--stdio"]
model: "my-model"
balda:
provider: my-agentBuilt-in provider types:
codex_acpopencode_acpcopilot_acpgemini_acpclaude_code_acpgeneric_acppool
/start owner=<owner_token>: authenticate the owner in direct messages./start invite=<invite_token>: onboard a collaborator in direct messages./topic <name>: owner/collaborator, direct messages only; create a named topic session./goal <objective>: owner/collaborator; start goal work in the current session context/workspace. Goal updates usebalda.telegram.formatting_mode; terminal updates include Result, Artifacts, Confidence, and Next action sections. See the goal workflow doc./close: owner/collaborator, direct messages only; reset the current session history. In a topic, it also closes that topic./cancel: owner/collaborator; request cancellation of in-flight work in the current session, including active/goalruns./user add|list|remove: owner only; manage collaborators.
Balda loads .config/balda/config.yaml, then applies BALDA_* environment
overrides. If .env exists in the working directory, Balda loads it before
config resolution.
Minimal shape:
runtime:
providers:
<provider_id>:
# generic_acp | gemini_acp | codex_acp | opencode_acp | copilot_acp | claude_code_acp | pool
type: <provider_type>
mcp_servers: {}
balda:
provider: <provider_id>
telegram:
token: ""
formatting_mode: "markdownv2"
plan_updates: true
webhook:
enabled: false
listen_addr: "0.0.0.0:8080"
path: "/telegram/webhook"
url: ""
auth_token: ""
webhooks:
enabled: false
listen_addr: "127.0.0.1:8090"
routes: {}
logger:
level: "info"
pretty: true
working_dir: ""
state_dir: ".config/balda"
sessions:
persistence: "sqlite"
memory:
enabled: true
goal:
max_iterations: 25
nats:
embedded: true
host: "127.0.0.1"
port: -1
store_dir: ".balda/nats"
max_memory: "256mb"
max_store: "2gb"
sync_always: false
expose_monitoring: false
swarm: {}
scheduler:
tasks: []
workspace:
mode: "auto"
base_branch: ""
sessions_dir: "sessions"
mcp_servers: []
global_instruction: ""Common settings:
balda.provider: provider ID selected duringbalda init.balda.telegram.token: Telegram bot token, usually supplied by.envasBALDA_TELEGRAM_TOKEN.balda.telegram.webhook.auth_token: required when Telegram webhook mode is enabled; Telegram sends it asX-Telegram-Bot-Api-Secret-Token.balda.webhooks.*: optional local inbound webhook receiver for external event-to-session ingress. Each route definespath,prompt_template,envelope(target,key, optionalmode=task|session, optionalreport_to),auth(type=none|header,header,valueorsecret_env), anddedupe(source=request_id|header|body_sha256, optionalheaderfor header source).balda.webhooks.*security: set routeauth(for example shared-token header) and keeplisten_addrprivate (localhost/private network) or front it with trusted gateway auth.balda.sessions.persistence:sqliteby default; keeps conversation history across restarts until the session is explicitly closed.balda.memory.enabled:trueby default; controls${balda.state_dir}/MEMORY.mdandbalda.memory.*MCP tools.balda.goal.max_iterations: maximum/goalworker-validator loop iterations; defaults to25.balda.nats.*: built-in command/event runtime settings. Defaults bind to127.0.0.1on a random local port, keep monitoring disabled, and store runtime files under.balda/nats.balda.swarm: optional advanced runtime tuning for goals, scheduled work, retries, and webhook delivery. Most installs should leave it at defaults.balda.scheduler.tasks: startup-reconciled recurring tasks. Each task hasid,cron, andenvelopewithtarget,key,content, and optionalreport_to. Scheduled work publishes first-class task commands; replies are fire-and-forget unlessreport_tois set.${balda.state_dir}/SOUL.md: optional operator instructions read at session start/restore when the file exists.balda.workspace.mode:autoby default; uses git worktrees when Balda runs in a git repository.balda.workspace.sessions_dir: directory name underbalda.state_dirused for per-session worktrees (defaults tosessions).balda.mcp_servers: extra MCP server IDs added to every Balda-started session.
MCP servers can be attached to providers or injected into every Balda session.
Balda also includes a built-in balda MCP server for memory and workspace
tools.
runtime:
mcp_servers:
local-tools:
type: stdio
cmd: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
remote-tools:
type: http
url: https://mcp.example.com/mcp
providers:
codex:
type: codex_acp
mcp_servers:
- local-tools
balda:
provider: codex
mcp_servers:
- remote-toolsEffective MCP IDs are built-in balda + provider mcp_servers + balda.mcp_servers.
Do not define runtime.mcp_servers.balda; Balda owns that bundled server.
telegram token is required: runbalda init, setBALDA_TELEGRAM_TOKENin.env, or setbalda.telegram.tokenin config.no supported agent CLI detected: install or expose one ofcodex,opencode,copilot,gemini, orclaude.balda.provider is required: rerunbalda initor setbalda.providerto a configured provider ID.- Session history should not survive restarts: set
balda.sessions.persistence=memoryorBALDA_SESSIONS_PERSISTENCE=memory. - Memory facts are not visible in an active session: memory is snapshotted when a session starts or restores; close and reopen the session to refresh it.
- Workspace import/export issues: check
balda.workspace.mode,balda.workspace.base_branch, and that Balda is running in the expected git checkout. - Progress updates are too noisy: set
balda.telegram.plan_updates=false. - Startup fails while initializing the built-in runtime streams: keep the default
balda.natssettings unless you have a specific local runtime need, ensurebalda.nats.store_diris writable, and verify disk space. - Startup fails while initializing built-in runtime consumers: stop any other Balda process sharing the same embedded store and restart.
- Runtime issues show up in structured logs; check recent command failures and retry pressure before increasing transport limits.
- Technical specification:
docs/balda.md - Architecture map:
docs/architecture/index.md - Telegram formatting guide:
docs/telegram-formatting.md - Contributing guide:
CONTRIBUTING.md - Agent workflow/policies:
AGENTS.md
- GitHub Releases: https://github.com/normahq/balda/releases
- npm package: https://www.npmjs.com/package/@normahq/balda