182 lines
5.6 KiB
Markdown
182 lines
5.6 KiB
Markdown
|
|
---
|
|||
|
|
title: 'Multi-Agent Deployment (Optional)'
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
Clawdie supports running multiple independent agents on the same FreeBSD host,
|
|||
|
|
but the default path is **one agent per host or per bhyve VM**. Use this guide
|
|||
|
|
only when you intentionally want a second agent later.
|
|||
|
|
|
|||
|
|
## Quick decision
|
|||
|
|
|
|||
|
|
- **Easiest isolation:** one agent per **bhyve VM** (each VM has its own `warden0`)
|
|||
|
|
- **Same-host multi-agent:** one host `warden0` bridge carries **multiple /24 subnets**
|
|||
|
|
|
|||
|
|
## Concepts (what must stay unique)
|
|||
|
|
|
|||
|
|
Separate runtime names from portable agent identity. A host may run multiple
|
|||
|
|
agents, and an agent may also carry a portable soul bundle (`SOUL.md`,
|
|||
|
|
`USER.md`, `IDENTITY.md`, `AGENTS.md`) cloned from a dedicated repo. Runtime
|
|||
|
|
names keep services, jails, datasets, and sockets from colliding; soul files
|
|||
|
|
carry who the agent is and what context it may reuse across hosts or harnesses.
|
|||
|
|
|
|||
|
|
- `AGENT_NAME` is the runtime identity key:
|
|||
|
|
- rc.d service name (`/usr/local/etc/rc.d/${AGENT_NAME}`)
|
|||
|
|
- tmux session name
|
|||
|
|
- jail name prefix (recommended)
|
|||
|
|
- DB identifiers (users + databases)
|
|||
|
|
- `AGENT_SUBNET_BASE` assigns a dedicated `/24` to that agent:
|
|||
|
|
- pick any private `/24` that does not collide with another agent
|
|||
|
|
- repo examples below use `10.0.1`
|
|||
|
|
- live installs can also use `192.168.72`, `172.16.50`, etc
|
|||
|
|
- you can also use `172.16.50`, `192.168.100`, etc — pick any private `/24` and stay consistent
|
|||
|
|
- `warden0` is the canonical bridge name. A single `warden0` can host multiple
|
|||
|
|
subnets; the host must own the `.1` gateway IP **for each subnet**.
|
|||
|
|
- Prefer **VNET jails** (`bastille create -B ... warden0`) for cleaner isolation.
|
|||
|
|
|
|||
|
|
## Portable soul repos
|
|||
|
|
|
|||
|
|
For multi-host or multi-harness continuity, keep durable agent identity in a
|
|||
|
|
small dedicated repository instead of baking it into one install. The same repo
|
|||
|
|
can be pulled by Pi, Hermes, Codex, Claude Code, or Colibri and adapted into the
|
|||
|
|
harness-specific prompt/config layer.
|
|||
|
|
|
|||
|
|
Recommended shape:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SOUL.md # durable agent identity, values, voice, operating style
|
|||
|
|
USER.md # operator/user context shared with this agent when allowed
|
|||
|
|
IDENTITY.md # short runtime identity and boundaries
|
|||
|
|
AGENTS.md # harness-facing rules when supported
|
|||
|
|
manifest.json # schema version, provenance, sharing rules
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Rules:
|
|||
|
|
|
|||
|
|
- keep secrets out of the soul repo;
|
|||
|
|
- keep runtime state in `system_ops`, not in identity files;
|
|||
|
|
- import long-lived memories into `system_brain` only when the operator allows;
|
|||
|
|
- keep skills/reviewed procedures in `system_skills` or referenced skill repos.
|
|||
|
|
|
|||
|
|
## Same-host multi-agent (VNET path)
|
|||
|
|
|
|||
|
|
Example: add a second agent named `atlas` on `10.0.1.0/24`.
|
|||
|
|
|
|||
|
|
### 1) Create a second project directory
|
|||
|
|
|
|||
|
|
Use a separate clone so each agent has its own `.env`, `logs/`, and runtime state.
|
|||
|
|
|
|||
|
|
### 2) Configure `.env` for the second agent
|
|||
|
|
|
|||
|
|
Minimum keys to set (example values):
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
AGENT_NAME=atlas
|
|||
|
|
ASSISTANT_NAME=Atlas
|
|||
|
|
|
|||
|
|
AGENT_SUBNET_BASE=10.0.1
|
|||
|
|
WARDEN_GATEWAY=10.0.1.1
|
|||
|
|
WARDEN_SUBNET=10.0.1.0/24
|
|||
|
|
|
|||
|
|
WARDEN_GIT_IP=10.0.1.2
|
|||
|
|
WARDEN_CMS_IP=10.0.1.3
|
|||
|
|
WARDEN_OLLAMA_IP=10.0.1.4
|
|||
|
|
WARDEN_LLAMA_CPP_IP=10.0.1.4
|
|||
|
|
WARDEN_DB_IP=10.0.1.5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
These are example values only. If your host already uses another private
|
|||
|
|
subnet, keep that and stay consistent across the rest of the file.
|
|||
|
|
|
|||
|
|
Jail name overrides (avoid collisions with an existing agent’s jails):
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
CMS_JAIL_NAME=atlas-cms
|
|||
|
|
OLLAMA_JAIL_NAME=atlas-ollama
|
|||
|
|
LLAMA_CPP_JAIL_NAME=atlas-llamacpp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Notes:
|
|||
|
|
|
|||
|
|
- `setup/git.ts` already defaults to `${AGENT_NAME}-git`, so `GIT_JAIL_NAME` is
|
|||
|
|
usually not required.
|
|||
|
|
- If you decide to share a local LLM jail across agents, keep a single `ollama`
|
|||
|
|
jail and point both agents at the same IP instead of creating per-agent LLM jails.
|
|||
|
|
|
|||
|
|
### Locale per agent (optional but supported)
|
|||
|
|
|
|||
|
|
Each agent can display in its own language without changing the host locale.
|
|||
|
|
Keep the host system locale stable (UTF-8), then set per-agent display/assistant
|
|||
|
|
locales in that agent’s `.env`:
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
DISPLAY_LOCALE=sl-SI
|
|||
|
|
ASSISTANT_LOCALE=sl-SI
|
|||
|
|
SYSTEM_LOCALE=sl_SI.UTF-8
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
For a different agent, choose different values (e.g. `de-DE`, `ru-RU`, `zh-CN`).
|
|||
|
|
Do not use legacy encodings; `SYSTEM_LOCALE` must remain UTF-8.
|
|||
|
|
|
|||
|
|
If multiple agents share one tmux server, new panes inherit the server locale.
|
|||
|
|
Either use separate tmux sessions per agent or set locale env vars inside each
|
|||
|
|
agent’s rc.d service environment.
|
|||
|
|
|
|||
|
|
### 3) Add the new gateway IP to `warden0`
|
|||
|
|
|
|||
|
|
Runtime (immediate):
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
sudo ifconfig warden0 inet 10.0.1.1/24 alias
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Persist across reboots (choose the next free alias index):
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
sudo sysrc ifconfig_warden0_alias0="inet 10.0.1.1/24"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4) Update PF NAT to include both subnets
|
|||
|
|
|
|||
|
|
If you use an include such as `/etc/pf.warden.conf`, the key idea is:
|
|||
|
|
|
|||
|
|
```pf
|
|||
|
|
warden_net = "{ 10.0.1.0/24, 192.168.72.0/24 }"
|
|||
|
|
nat on $ext_if from $warden_net to any -> ($ext_if)
|
|||
|
|
pass quick on warden0 inet from $warden_net to any keep state
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Reload PF:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
sudo pfctl -nf /etc/pf.conf
|
|||
|
|
sudo pfctl -f /etc/pf.conf
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5) Provision the second agent’s service jails
|
|||
|
|
|
|||
|
|
From the second agent repo directory:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
sudo just setup-db
|
|||
|
|
sudo just setup-git
|
|||
|
|
sudo just setup-cms
|
|||
|
|
sudo just setup -- --step ollama # optional
|
|||
|
|
sudo just setup -- --step llama-cpp # optional
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Database provisioning can be done either via `just setup-db` (or `npm run setup -- --step db`) or via Ansible
|
|||
|
|
playbooks in `infra/ansible/playbooks/`.
|
|||
|
|
|
|||
|
|
### 6) Install and start the second rc.d service
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
sudo just setup -- --step service
|
|||
|
|
sudo service atlas onestart
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## bhyve note
|
|||
|
|
|
|||
|
|
If you deploy one agent per **bhyve VM**, you typically do **not** need any of
|
|||
|
|
the same-host subnet aliasing or multi-subnet PF rules. Each VM can keep a
|
|||
|
|
single-agent `warden0` + single `/24` internally.
|