brainos-core 0.1.0

Configuration and bootstrapping for Brain OS cognitive engine
Documentation
# Brain - Default Configuration
# Override with ~/.brain/config.yaml or environment variables

brain:
  version: "0.1.0"
  data_dir: "~/.brain"

storage:
  ruvector_path: "~/.brain/ruvector/"
  sqlite_path: "~/.brain/db/brain.db"
  hnsw:
    ef_construction: 200
    m: 16
    ef_search: 50

llm:
  provider: "ollama"
  model: "qwen2.5-coder:7b"
  base_url: "http://localhost:11434"
  api_key: ""                 # Required for openai provider; also settable via BRAIN_LLM__API_KEY env var
  temperature: 0.7
  max_tokens: 4096
  intent_llm_fallback: false  # Deprecated: intent routing is LLM-first when LLM is available

embedding:
  model: "nomic-embed-text"    # must be pulled: `ollama pull nomic-embed-text`
  dimensions: 768              # must match the model output size (nomic-embed-text → 768)

memory:
  episodic:
    max_entries: 100000
    retention_days: 365
  semantic:
    similarity_threshold: 0.65
    max_results: 20
  search:
    hybrid_weight: 0.7         # Weight for vector search (1-weight for BM25)
    rrf_k: 60                  # RRF fusion parameter
    pre_fusion_limit: 50       # Candidates fetched from each source before RRF fusion
    importance_weight: 0.3     # Weight for importance in reranking (0.0–1.0)
    recency_weight: 0.2        # Weight for recency in reranking (0.0–1.0)
    decay_rate: 0.01           # Forgetting curve decay rate (higher = faster forgetting)
  consolidation:
    enabled: true
    interval_hours: 24
    forgetting_threshold: 0.05 # Memories below this are candidates for pruning

encryption:
  enabled: false               # Run `brain init --encrypt` to generate a salt and enable

security:
  exec_allowlist:
    - ls
    - cat
    - grep
    - find
    - git
    - cargo
    - rustc
  exec_timeout_seconds: 30

actions:
  web_search:
    enabled: true
    provider: "searxng"       # searxng | tavily | custom
    endpoint: "http://localhost:8888"  # SearXNG default; override for your instance
    api_key: ""               # Required for tavily provider
    timeout_ms: 3000
    default_top_k: 5
  scheduling:
    enabled: false
    mode: "persist_only"      # Persist intents only; no internal cron runner
  messaging:
    enabled: false
    timeout_ms: 3000
    channels: {}
    # Channel config — URL-only shorthand or full config with body template + headers:
    #   alerts: "https://example.com/hook"              # shorthand: URL only, default JSON body
    #   slack:
    #     url: "https://hooks.slack.com/services/T/B/x"
    #     body: '{"text": "{{content}}"}'               # template: {{channel}} {{recipient}} {{content}} {{namespace}} {{timestamp}}
    #   telegram:
    #     url: "https://api.telegram.org/bot<TOKEN>/sendMessage"
    #     body: '{"chat_id": "<CHAT_ID>", "text": "[{{channel}}] {{content}}"}'
  resilience:
    max_retries: 2            # retries on transient errors (5xx, timeout, connection refused)
    retry_base_ms: 500        # base delay; doubles each retry (500 → 1000 → 2000)
    circuit_breaker_threshold: 5    # consecutive failures before circuit opens
    circuit_breaker_cooldown_secs: 60  # seconds to wait before retrying after circuit opens

proactivity:
  enabled: false               # Opt-in
  max_per_day: 5
  min_interval_minutes: 60
  quiet_hours:
    start: "22:00"
    end: "08:00"
  delivery:
    outbox: true               # Always write to outbox; drain on next interaction
    broadcast: true            # Push to live WS/SSE sessions
    webhook_channels: []       # Channel keys from actions.messaging.channels to receive proactive notifications
    max_outbox_age_days: 7     # Prune stale undelivered outbox items during consolidation
  open_loop:
    enabled: true              # Detect unresolved commitments (requires proactivity.enabled: true)
    scan_window_hours: 72      # How far back to scan for commitments
    resolution_window_hours: 24  # Hours before an unresolved commitment triggers a reminder
    check_interval_minutes: 120  # How often to check for open loops

adapters:
  http:
    enabled: true
    host: "127.0.0.1"
    port: 19789
    cors: true
  ws:
    enabled: true
    port: 19790
  mcp:
    enabled: true
    stdio: true
    http: true
    port: 19791
  grpc:
    enabled: true
    port: 19792

access:
  api_keys:
    - key: "demokey123"
      name: "Demo Key"
      permissions:
        - read
        - write