Skip to content

Colony layout and configuration

Start here for where colony config lives, how .paseka/ relates to machine-local state, and what paseka init creates.

For adapters, run directories, worktrees, and package layout see Architecture overview.

1. Colony-centric model

Concept Location Role
Colony Git repo root Source of truth for code, history, and shareable hive config
Apiary Developer machine Hosts Hive Runtime, NATS, and local adapter credentials
Bee Config + runtime A role (Scout, Guard, Builder…) bound to an adapter that drives an external agent

The runtime never owns LLM logic. It launches external tools through adapters — the Cursor Agent CLI (agent), the Pi CLI (pi), Claude Code, OpenCode, and declared script commands — reads their output, and publishes results to the NATS bus as contract events.

To run the apiary on a separate always-on host (containerized toolbelt + Queen Console, reuse an existing NATS), see Homelab deployment.


2. Two-tier configuration

Project-local: .paseka/ (in repo)

Version-controlled colony definition. Safe to commit; no secrets.

.paseka/
├── colony.yaml          # colony manifest: bees, routes, defaults
├── profiles/            # optional committed config overlays (not created by init)
│   └── pi.yaml          # shareable recipe: adapter remap, params, model_aliases
├── bees/                # per-bee adapter bindings and non-secret params
│   ├── scout.yaml
│   └── builder.yaml
├── prompts/             # prompt templates (committed); see §2.1
│   ├── _partials/       # shared snippets (JSON contract, tone, etc.)
│   ├── scout.md
│   └── builder.md
├── cues/                # Forage Cue ingress shortcuts (committed); see [cues](cues.md)
│   ├── feature.yaml
│   └── hotfix.yaml
├── .gitignore           # ignores worktrees/, runs/, cache/, *.local.yaml
├── runs/                # gitignored — per-agent file IPC and trail comb (architecture overview; purge --runs wipes standing checkpoints)
│   └── <traceId>/
│       ├── trail-events.ndjson   # the trail's own record — every domain event the colony published for it
│       ├── <agentId>/
│       │   ├── prompt.txt
│       │   ├── system.txt   # optional — rendered system_template
│       │   ├── summary.md
│       │   ├── meta.json
│       │   └── status.json
│       └── tasks/
│           └── <taskId>/
│               ├── task.md
│               └── runs.ndjson
│       └── proposals/           # proposal patches — runtime-owned code history (not a comb)
│           ├── <agentId>.patch  # the diff the collector returned at finalization
│           └── <agentId>.json   # sidecar metadata + publication state
└── worktrees/           # gitignored — isolated mutation workspaces
    └── <traceId>/

colony.yaml — colony identity, default branch, bee registry, optional sectors (module/subfolder workspace scopes), NATS subject prefixes (optional overrides), colony-wide defaults including per-trace honey reserve (defaults.energy_budget, default 12), optional defaults.delivery (local_merge or pull_request; see pull-request delivery), optional model_aliases (stable names → vendor model ids for params.model; see spec 019), and optional auto_invites (HITL choreography that publishes session.invite when bus events match — see bee routing). Do not put profile: here — a sticky overlay belongs only in home config.yaml.

defaults:
  prompt_template: default.md
  system_template: default-system.md   # optional colony-wide role context
  energy_budget: 12
  default_bee: builder                 # task role when task.bee is omitted
  # delivery: pull_request             # isolated trails land as origin PRs; default is local_merge
model_aliases:
  small: composer-2.5
  medium: cursor-grok-4.6-medium
  high: cursor-grok-4.6-high-fast

Each traceId shares one Honey Reserve (energyToken): every adapter dispatch (task.ready and direct routing) consumes one token. When the reserve is empty, tasks move to blocked with summary Honey reserve exhausted. Beekeepers can top up a live tick via paseka energy add --trace <id> --amount <n>. Per-cue initial reserve overrides on fresh trails; standing cues then replace remaining each tick from standing.stipend: Forage Cues § Honey.

Example sectors for monorepos or git-submodule layouts:

sectors:
  frontend:
    path: frontend
  backend-users:
    path: backend/users

A sector is a named path inside the colony. Tasks may optionally set sector; bees may declare a default sector in bees/*.yaml. Runtime resolves the adapter workspace as colonyRoot/<sector.path> or .paseka/worktrees/<traceId>/<sector.path> when worktree: true. The colony root remains the audit boundary for .paseka/runs/.

bees/*.yaml — one file per role: binds the bee to an adapter, prompt template(s), optional command / post_exec, sector/worktree, and routing rules. Full schema, examples, and variable substitution: bee config. Event routing (subscribes / publishes): bee routing. Project-local overrides that must not be committed live in *.local.yaml (gitignored).

2.1 Prompt templates

Templates live in .paseka/prompts/ — version-controlled, one colony, shareable across machines. Each bee may reference one or two templates from its bees/<role>.yaml:

Field Artifact Role
system_template (optional) system.txt Standing role context — injected by the adapter, not the first chat turn
prompt_template prompt.txt User/task turn for AFK runs; optional kickoff for interactive chat

When system_template is unset, behavior matches the previous single-template model (full prompt as positional argv only). Full variable list, partials, and override precedence: prompt templates. Bee YAML schema: bee config.

Bee config → templates:

# .paseka/bees/builder.yaml
role: builder
adapter: cursor
system_template: builder-system.md   # optional — role / standing instructions
prompt_template: builder.md          # user/task turn
worktree: true

Rendering: Go text/template at dispatch time. Runtime builds a PromptContext from bus event + colony state, writes prompt.txt (and system.txt when system_template is set) under .paseka/runs/<traceId>/<agentId>/, then passes rendered strings to the adapter.

Colony-wide fallbacks when a bee omits a field: defaults.prompt_template and optional defaults.system_template in colony.yaml.

Do not store prompts in ~/.config/paseka/ — they belong to the colony and should ride with the repo. Home config only holds secrets and runtime state.

Bee Language vs technical: UI/docs may say «Scout Bee»; templates can use bee tone for HITL readability. Bus payloads and JSON partials stay technical (SIGNAL, traceId, etc.) — see glossary.

Machine-local: ~/.config/paseka/<project-slug>/

Per-colony state on this machine. Not committed.

~/.config/paseka/<project-slug>/
├── config.yaml                 # secrets refs, NATS URL, adapter env (overridable via PASEKA_NATS_URL)
│                               # optional model_aliases — overlays colony.yaml keys on this machine
│                               # optional profile: <name> — sticky overlay for this machine only
│                               # optional forge.command — argv for PR hosting (tea/gh script; not a bee adapter)
├── profiles/<name>/            # optional machine-local overlay for that name
│   ├── config.yaml             # nats + model_aliases only
│   └── adapters/*.yaml         # binary / api_key_env overlays
├── state.json                  # runtime: active worktrees, last traceId, hive status
├── telegram.yaml               # optional: Telegram Human Gateway (not created by init)
├── telegram-notify-state.json  # optional: gate notify dedup (runtime)
├── adapters/                   # adapter-specific local overrides
│   ├── cursor.yaml             # CLI binary path, API key env
│   ├── pi.yaml                 # Pi CLI binary path, API key env
│   ├── claude.yaml             # Claude Code CLI binary, optional API key env
│   └── opencode.yaml           # OpenCode CLI binary

Telegram bot tokens and allowlists stay machine-local — see Telegram gateway.

Common environment overrides:

Variable Purpose
PASEKA_NATS_URL Overrides machine-local nats.url for CLI, runtime, Console, and gateways
PASEKA_PROFILE Selects a config profile when --profile / --no-profile are omitted
CURSOR_API_KEY Cursor Agent CLI authentication when not using agent login
Adapter api_key_env target Vendor key named by adapters/<adapter>.yaml, for example GEMINI_API_KEY
PASEKA_TRACE_ID, PASEKA_AGENT_ID, PASEKA_TASK_ID Injected into declared script adapter processes

Split rule:

Kind Project .paseka/ Home ~/.config/paseka/<slug>/
Bee roles & adapter choice yes —
Config profile recipes yes (.paseka/profiles/<name>.yaml) yes (profiles/<name>/, sticky profile:)
Model alias map (model_aliases) yes (base) yes (overlay same keys)
Prompt templates (shareable) yes —
API keys, tokens — yes (or env var refs)
NATS connection override — yes
Isolated trail delivery (defaults.delivery) yes —
Forge CLI argv (forge.command) — yes (tokens stay in tea/gh login)
Active worktrees registry pointer only authoritative state
Published PR identity — state.json pullRequests[]
Active agent runs registry pointer only optional mirror in state.json
Event replay cache — yes

A profile is an optional named overlay for the whole Queen Shell process (paseka --profile pi, PASEKA_PROFILE, or sticky profile: in home config.yaml). --no-profile ignores sticky. Unknown names fail closed and list what exists on both layers. paseka init does not create profiles; Nuc does not pack .paseka/profiles/. Global adapter: replaces LLM bees without command:; script and custom argv stay committed unless bees.<role>.adapter is set. Details: Bee config, CLI.


3. Project slug

Stable identifier for the home config directory.

  1. If origin remote exists → canonical slug from host/path (e.g. github.com-acme-api → acme-api, or full github-com-acme-api).
  2. Else → sanitized directory name of repo root (e.g. paseka).
  3. Collision on same machine → suffix with short hash of absolute repo path.

Stored in .paseka/colony.yaml as slug after first paseka init so later commands resolve the same home path.


4. paseka init

Run from inside a git repository (or at repo root).

paseka init [--adapter cursor|pi|opencode]
  │
  ├─► resolve git root (fail if not a repo)
  ├─► compute / persist project slug
  ├─► create .paseka/colony.yaml (defaults)
  ├─► create .paseka/prompts/ with starter templates (scout, builder, hivewright)
  ├─► create .paseka/bees/ with starter bees (scout, builder, hivewright) for the selected adapter
  ├─► create .paseka/cues/ with starter cues (feature, hotfix) when missing
  ├─► create .paseka/.gitignore (worktrees/, runs/, *.local.yaml, cache/)
  ├─► create ~/.config/paseka/<slug>/config.yaml
  ├─► create ~/.config/paseka/<slug>/state.json (empty)
  ├─► create adapter config stubs (cursor, pi, claude, opencode)
  └─► print next steps (adapter-specific auth / CLI setup, then `paseka run`)

--adapter selects which LLM adapter the starter bees use (cursor by default; pi and opencode are supported scaffold choices). Unknown adapter names fall back to cursor. Claude and script adapters can be selected later in individual bee YAML.

paseka init is idempotent: existing files are preserved; missing pieces are added.


5. First local run

The choreographic runtime requires NATS with JetStream. The repository docker-compose.yml starts both NATS and a browser UI:

docker compose up -d
curl http://127.0.0.1:8222/jsz
# Optional NATS UI: http://127.0.0.1:31311

Ports are 4222 for NATS clients, 8222 for NATS monitoring, and 31311 for the optional UI. Data is persisted in Docker volumes.

Then initialize and verify the colony:

paseka init
agent login                    # Cursor default
paseka doctor
paseka run

For Pi or OpenCode, pass the corresponding --adapter during init and authenticate that CLI. paseka bee run and paseka bee chat can launch one-off work without the reactor; commands that publish or project choreography events still need NATS. See the CLI NATS dependency matrix.