Thank you for your interest in contributing to Backplaned. This guide covers project setup, conventions, and the contribution workflow.
- Python 3.12+
- uv (recommended package manager)
- Git
- An OpenAI-compatible LLM endpoint for testing (e.g., Ollama)
git clone https://github.com/SJK-py/backplaned.git
cd backplaned
# Create virtual environment and install dependencies
uv venv
uv pip install -e .
# Configure
cp start.config.example start.config
# Edit start.config with your LLM endpoint and admin credentials
# Run
./start.shFor development, you can run specific agents in isolation:
# Router only
.venv/bin/uvicorn router:app --host 0.0.0.0 --port 8000
# A specific external agent (after router is running)
cd agents_external/reminder_agent
python agent.pyExternal agents need their data/.env configured with ROUTER_URL, AGENT_PORT, and either INVITATION_TOKEN (first run) or saved credentials in data/credentials.json.
backplaned/
├── router.py # Central router (all routing logic, ACL, proxy files)
├── helper.py # Shared library (models, RouterClient, builders)
├── config_ui.py # Shared config UI utilities for agent web UIs
├── start.sh # Startup orchestrator
├── agents/ # Embedded agents (loaded by router at startup)
├── agents_external/ # External agents (separate processes)
├── docker/ # Docker deployment files
└── docs/ # Documentation
| File | Responsibility |
|---|---|
router.py |
All routing logic, task lifecycle, ACL, proxy files, agent registration, API endpoints |
helper.py |
Pydantic models (ProxyFile, AgentInfo, LLMData, etc.), RouterClient, message builders, LLM tool builders, ProxyFileManager |
config_ui.py |
Reusable config editor endpoints for agent web UIs |
start.sh |
Config propagation from start.config into agent .env/config.json, agent bootstrapping and startup sequencing |
Each agent (embedded or external) follows a consistent pattern:
| File | Purpose |
|---|---|
agent.py |
Main application — FastAPI app, AGENT_INFO, /receive endpoint, task processing |
config.default.json |
Default runtime config values (source-controlled) |
config.example |
JSON describing each config field (shown in web admin UI) |
.env.example |
Environment variable template (external agents only) |
data/ |
Runtime data directory (gitignored) |
- Python 3.12+ features are welcome (type hints, match statements, etc.)
- Use type hints for function signatures
- Follow existing code patterns — consistency across agents is valued
- Prefer
asyncioandhttpxfor async operations - Use
loggingmodule for log output, notprint()(exception: router startup messages)
- Agent directories use
snake_case(e.g.,my_agent) - Agent IDs match their directory name
- Config keys use
UPPER_SNAKE_CASE - Python modules and variables use
snake_case
- Infrastructure config (URLs, ports, credentials) goes in
.env - Behavioral config (limits, toggles, model IDs) goes in
config.json - All config keys should have sensible defaults
- Document every config field in
config.example
- Return
status_code >= 400in agent results to indicate failure - Use descriptive error messages in
AgentOutput.content - Use timeouts for all network calls and sub-agent interactions
- Cap iteration counts to prevent runaway loops
- Never silently swallow errors in task processing — always report back
- Never log or expose auth tokens in responses
- Use
secrets.compare_digest()for token comparison (constant-time) - Validate and sanitize file paths — the router has
_is_safe_path()and_sanitize_task_id()helpers - External agents should verify the
Authorizationheader on/receive - Use
PasswordFilefromhelper.pyfor admin password storage (PBKDF2-SHA256)
git checkout -b feature/my-feature
# or
git checkout -b fix/bug-description- Follow existing code patterns and conventions
- Add config defaults and examples for new config keys
- Update documentation if you're adding features or changing behavior
# Start the full stack
./start.sh
# Or test specific components
.venv/bin/python -c "from helper import AgentInfo; print('imports ok')"Test your changes end-to-end:
- Start the router and relevant agents
- Verify agent registration (check web admin UI)
- Send test messages through the system
- Check that tasks complete successfully
- Write a clear PR title and description
- Reference any related issues
- Describe what you changed and why
See the Agent Development Guide for a complete walkthrough. In summary:
- Create
agents/my_agent/agent.pywithappandAGENT_INFO - Create
agents/my_agent/config.default.jsonwith sensible defaults - Create
agents/my_agent/config.examplewith field descriptions - Add group assignment in
router.py_EMBEDDED_AGENT_GROUPS - Add the data directory to
docker/Dockerfile.routerif using Docker
- Create
agents_external/my_agent/withagent.py,config.py, configs - Implement
/receive,/health, and/refresh-infoendpoints - Add
.env.examplewith required environment variables - Add bootstrap and startup logic to
start.sh - Add the agent to
docker/Dockerfile.routerCOPY commands - Add the data directory and port mapping to
docker/docker-compose.yml - Update
start.config.exampleif the agent introduces new user-facing config
cd docker
docker compose builddocker compose build router # or: coding
docker compose up -d routerdocker compose logs -f router
docker compose logs -f codingIf you get permission errors, ensure your host UID/GID match the container user:
docker compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g)SQLite in WAL mode provides sufficient performance for the router's workload (task tracking, agent registry, proxy file metadata). It avoids external dependencies and simplifies deployment. The codebase is structured for a straightforward migration to aiosqlite if needed for high-traffic deployments.
Embedded agents benefit from zero-latency ASGI transport and shared filesystem access. This eliminates HTTP overhead for tightly-coupled agents (LLM inference, memory, document conversion) while keeping the same interface contract as external agents.
Invitation tokens provide a controlled registration mechanism. Admins create tokens with specific group memberships, and agents use them once to register. This prevents unauthorized agents from joining the system while allowing self-service setup for known agents.
Group-based routing rules provide a scalable way to define access policies. Instead of managing N*N individual agent permissions, you define policies between functional groups (core, infra, tool, usertool, channel, notify, bridge, admin). Individual overrides are available when needed.