Files
obsidian-vault/personal/projects/balda/setup.md
T

4.6 KiB

Balda Setup & Status

Last updated: 2026-06-07

Runtime

Item Value
Binary ~/Developer/balda/balda (built from source)
Process Managed by launchd (com.normahq.balda)
Log /private/tmp/balda.log
Config ~/Developer/balda/.config/balda/config.yaml
State dir ~/Developer/balda/.config/balda/
Sessions SQLite persistence
Provider deepseek (openai type, model: deepseek-chat, via https://api.deepseek.com/v1)

Balda is managed by launchd. Full restart (reloads plist env vars):

launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.normahq.balda.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.normahq.balda.plist

Quick restart (does NOT reload plist env vars — use bootout/bootstrap for env changes):

launchctl kickstart -k gui/$(id -u)/com.normahq.balda

Zulip Webhook

Item Value
Status ENABLED
Listen address 0.0.0.0:8091
Path /zulip/webhook
Webhook bot URL http://host.docker.internal:8091/zulip/webhook

Port was changed from 8090 → 8091 on 2026-06-07 (see Port Conflict below).

Architecture

Balda uses the outgoing webhook mechanism — Zulip pushes events to Balda's HTTP endpoint. This differs from Eagle/Kit which use polling.

  • Zulip runs in Docker (container: zulip-zulip-1), exposed on 127.0.0.1:8000
  • Balda runs natively on Mac (not in Docker)
  • Colima maps host.docker.internal:PORT → 127.0.0.1:PORT on the Mac host, so the bot URL uses host.docker.internal to reach Balda from inside Docker

Soul / Prompt

The global_instruction field in config.yaml defines Balda's persona (Валера). The canonical source is soul.md in this folder — sync manually to config after edits, then restart Balda.

Provider-level system_instructions are documented in workspace-context.md.


Bug Fixed 2026-06-07 — Context Canceled on First DB Operation

File: zulip_handler.go, line 236

Root cause: The message-processing goroutine was launched with the HTTP request context:

// Before (broken)
go h.processMessage(r.Context(), payload)

When the handler returns HTTP 200, Go cancels r.Context(). The goroutine then hits its first database operation and fails with context canceled.

Fix:

// After (fixed)
go h.processMessage(context.WithoutCancel(r.Context()), payload)

context.WithoutCancel creates a copy of the parent context that is never canceled when the parent is — so the goroutine lives past the HTTP handler return.


Port Conflict 2026-06-07 — Moved 8090 → 8091

Root cause: Colima (Docker runtime) maps host.docker.internal:PORT → 127.0.0.1:PORT on the Mac host. openclaw/claude-proxy was occupying 127.0.0.1:8090, intercepting Balda's webhook traffic before it reached Balda.

Solution: Changed Balda's listen port from 8090 to 8091 in config.yaml.

Resolution

Webhook bot URL updated in Zulip DB via docker exec psql on 2026-06-07: http://host.docker.internal:8090/zulip/webhookhttp://host.docker.internal:8091/zulip/webhook


DeepSeek Provider via norma-local Fork

Balda's openai provider type in norma hardcoded api.openai.com. To support DeepSeek (or any OpenAI-compatible API), the norma source was forked locally:

  • Fork path: ~/Developer/norma-local
  • Patched files:
    • pkg/runtime/hostedagent/openai.go — added openAIBaseURL() reading OPENAI_BASE_URL env var
    • pkg/runtime/agentfactory/agentfactory.go — removed MCP restriction for openai type
  • Env var OPENAI_BASE_URL=https://api.deepseek.com/v1 is set in the launchd plist
  • go.mod replace directive (normahq/norma v0.0.6 → ../norma-local) is local only (not committed to the PR branch — needs a separate norma upstream PR or fork)

To rebuild after patching norma:

cd ~/Developer/balda
go build -o balda ./cmd/balda/

CLAUDECODE Environment Bug (Fixed 2026-06-07)

When launched from an Eagle terminal session, Balda inherited CLAUDECODE=1, which caused the Claude Code CLI to refuse to start (it detected a nested invocation). Fix: Use launchctl bootout + bootstrap to start Balda clean — launchd does not inherit parent shell env vars.


Config Snippet (key sections)

balda:
  global_instruction: |
    <contents of soul.md>

runtime:
  providers:
    deepseek:
      type: openai
      openai:
        api_key: "<DeepSeek API key>"
        model: deepseek-chat

zulip:
  webhook:
    enabled: true
    listen: "0.0.0.0:8091"
    path: /zulip/webhook
  allowed_owners:
    - amartemyanov@duckduckgo.com