Documentation
From zero to running conspiracy in about five minutes.
Prerequisites
- Docker or Podman
- Go 1.22+ (both paths require building a Go binary)
- An API key (Anthropic or OpenRouter)
The container runs Ubuntu 24.04 or Debian 12 with systemd as PID 1. Your host just needs a container runtime.
Quick Start
Build the conos CLI, then let it handle the rest.
git clone https://github.com/ConspiracyOS/conos.git
cd conos && go build -o conos ./cmd/conos/
CONOS_API_KEY is the env var that holds your LLM provider's
API key (Anthropic, OpenRouter, etc.). The name is configurable via
api_key_env in the config.
CONOS_API_KEY=sk-your-key-here ./conos install
This pulls the image, writes the env file, and starts the container. One command, one running system.
./conos status
Build from Source
For contributors, or if you want to modify the inner runtime.
git clone https://github.com/ConspiracyOS/conctl.git
git clone https://github.com/ConspiracyOS/container.git
# Build the inner binary
cd conctl && go build -o conctl ./cmd/conctl/ && cd ..
# Configure
cd container
cp .env.example srv/dev/container.env
# Edit srv/dev/container.env — set CONOS_API_KEY=your-key-here
# Build and deploy
make image CONCTL_BIN=../conctl/conctl
make deploy
The container boots, provisions agent users and inboxes, and starts
watching for tasks. Verify with make status.
First Task
Talk to the concierge. It routes your request to the right agent.
With conos:
conos agent "What agents are running in this conspiracy?"
conos agent logs -f # watch the audit log
With make (from the container repo):
make task MSG="What agents are running in this conspiracy?"
make logs # tail the audit log
make responses # check agent outboxes
The concierge reads the task from the outer inbox, picks the right agent, and routes it. Responses land in agent outboxes.
To task a specific agent directly:
conos agent task sysadmin "Run the contracts"
How Tasks Flow
A conspiracy has one entry point: the outer inbox
(/srv/conos/inbox/). When you run conos agent "do something",
the CLI writes a .task file into that directory. A systemd
path unit detects the new file and wakes the concierge.
The concierge reads the task, decides which agent should handle it,
and moves the file to that agent's inbox
(/srv/conos/agents/<name>/inbox/). Another systemd path
unit detects it and starts the agent's service. The agent reads the task,
does the work, and writes a response to its outbox.
There is no message bus, no RPC layer, no internal API. Agents communicate by writing files. Routing is file-move. Activation is systemd. The entire coordination layer is filesystem + init system.
Tiers
Tiers control privilege, not intelligence. Every agent runs the same kind of LLM — tiers determine what the OS allows it to do.
| Tier | Can Task | Hardening |
|---|---|---|
| Officer | Any agent | NoNewPrivileges, ProtectSystem=strict |
| Operator | Any agent | NoNewPrivileges, ProtectSystem=strict, ReadWritePaths scoped |
| Worker | Nobody | All of the above + BindReadOnlyPaths, PrivateTmp, ProtectHome=tmpfs |
Officers set policy (the strategist reviews system state daily and writes standing orders). Operators execute (the concierge routes tasks, the sysadmin commissions agents). Workers do a single job and write a response. They cannot task other agents — the kernel blocks writes to other inboxes via POSIX ACLs.
Skills & Roles
An agent's behavior is defined by two things: instructions and skills.
Instructions (AGENTS.md) define identity,
constraints, and decision-making rules. They tell the agent what
it is and what to consider when working.
Skills are step-by-step procedures for specific tasks.
They live in /etc/conos/roles/<role>/skills/*.md and are
injected into the agent's prompt at task time. Skills tell the agent
how to do things — exact commands, verification steps, and
checklists.
/etc/conos/
roles/
sysadmin/
skills/
commission-agent.md # 11-step procedure for creating an agent
evaluate-request.md # decision tree for evaluating requests
writing-contracts.md # how to write detective contracts
heartbeat-audit.md # interpret healthcheck results
agents/
concierge/
AGENTS.md # concierge identity and routing rules
sysadmin/
AGENTS.md # sysadmin identity and constraints
Agents reference roles in the config (roles = ["sysadmin"]).
At task time, skills from those roles are copied to the agent's workspace
and injected into the prompt. The agent is told: "If a skill exists for
what you are doing, use it. Do not improvise."
Skills and agent instructions are owned by root and set immutable
(chattr +i). An agent cannot modify its own instructions or
skill files — the kernel prevents it.
Configuration
Everything lives in conos.toml. The default config ships with
three agents: concierge, sysadmin, and strategist.
[system]
name = "my-conspiracy"
[dashboard]
enabled = true
port = 8080
[contracts]
brief_output = "/srv/conos/context/system-state.md"
[base]
runner = "picoclaw"
provider = "anthropic"
api_key_env = "CONOS_API_KEY" # env var holding your LLM provider key
[base.officer]
model = "claude-sonnet-4-6"
[base.operator]
model = "claude-sonnet-4-6"
The [base] section sets defaults. [base.officer] and
[base.operator] override per tier. Individual agents inherit from
their tier unless they specify their own model or provider.
Adding an Agent
Append to conos.toml:
[[agents]]
name = "researcher"
tier = "worker"
mode = "on-demand"
packages = ["curl", "jq"]
instructions = """
You are the Researcher. You gather information from
approved sources and summarize findings.
"""
Redeploy to provision the new agent:
conos config apply # if using conos
make deploy # if building from source
Bootstrap creates the Linux user (a-researcher), home directory,
inbox, outbox, workspace, systemd units, and ACLs. It installs any
declared packages and deploys skills from the agent's roles. The agent
exists because Linux says it does.
Agent Fields
| Field | Required | Description |
|---|---|---|
name | yes | Agent name. Becomes Linux user a-<name>. |
tier | yes | officer, operator, or worker. |
mode | yes | on-demand (inbox-triggered), cron, or continuous. |
instructions | yes | System prompt. Injected at task time. |
roles | no | List of roles. Loads skills from /etc/conos/roles/<role>/skills/. |
packages | no | Apt packages installed on bootstrap. Also installable at runtime. |
cron | no | Cron expression. Required if mode = "cron". |
runner | no | Override runtime binary (default: inherited from [base]). |
runner_args | no | Arguments for the runtime binary. |
Packages
Agents sometimes need tools — psql for database queries,
curl for HTTP, jq for JSON processing. Packages
declared in the config are installed at bootstrap:
[[agents]]
name = "data-analyst"
packages = ["postgresql-client", "jq"]
For runtime installation without restarting:
# Install now (ephemeral — lost on container restart)
conctl package install postgresql-client --agent data-analyst
# Install and persist to conos.toml
conctl package install postgresql-client --agent data-analyst --save
The --save flag writes the package to the config so it
survives restarts. Without it, the install is immediate but ephemeral.
Package installation goes through conctl package, not raw
apt-get. This validates the package name, logs the action to
the audit trail, and keeps apt-get out of agent sudoers.
Contracts
Contracts are YAML-defined checks that assert the system matches its intended state. They run on systemd timers (typically every 60 seconds) and report pass, fail, or warn for each check.
Prevention is the OS's job — chmod, nftables,
sudoers enforce boundaries at the kernel level. Detection is
the contract's job — contracts verify that those preventive controls
stayed in place. If an ACL was loosened, a file permission changed, or
a service stopped, the contract catches it.
There are three contract types:
- Detective — runs on a timer, detects drift. Most contracts are this type.
- Atomic — single-state assertion. Semantically identical to detective.
- Protocol — runs before a destructive action, enforces preconditions. See below.
The contracts
framework is standalone. It runs on any Linux system — no dependency on
the rest of ConspiracyOS.
Writing Contracts
A contract is a YAML file in /srv/conos/contracts/:
id: CON-SEC-001
description: Skill files must be owned by root
type: detective
tags: [schedule]
scope: global
checks:
- name: skills_root_owned
script:
inline: |
found=$(find /etc/conos/roles -name '*.md' ! -user root)
[ -z "$found" ]
on_fail: escalate
severity: critical
category: security
what: "Skill files not owned by root"
verify: "find /etc/conos/roles -name '*.md' ! -user root"
affects: ["/etc/conos/roles"]
Key fields:
| Field | Purpose |
|---|---|
id | Unique ID. Convention: CON-{SEC|SYS|AGENT}-NNN |
type | detective, atomic, or protocol |
tags | [schedule] to run on the healthcheck timer |
on_fail | Action on failure: alert, escalate, halt_agents, halt |
severity | critical, high, medium, low |
what | Human-readable finding (shown in reports) |
verify | Command to manually verify the issue |
affects | Paths or resources affected |
Check types: command (single command + exit code),
script (inline or file), regex_in_file,
path_exists, yaml_key/json_key/toml_key,
env_var, command_available. A check passes when
exit code is 0 or the assertion holds.
Protocol Contracts
Protocol contracts are precondition gates. They don't run on a timer — they run explicitly before a destructive action.
id: CON-PROTO-001
description: Preconditions before disabling Tailscale
type: protocol
trigger: "tailscale down"
checks:
- name: lan_ssh_reachable
command:
run: "ssh -o ConnectTimeout=5 -o BatchMode=yes nas 'echo ok'"
exit_code: 0
on_fail: halt
severity: critical
what: "LAN SSH unreachable — disabling Tailscale would lock out the host"
Run a protocol check before acting:
# Check preconditions (exit 0 = clear, exit 1 = blocked)
conctl protocol check CON-PROTO-001
# Override a specific check with a reason
conctl protocol check CON-PROTO-001 --exempt lan_ssh_reachable \
--reason "Physical console access confirmed"
Exemptions are logged to the audit trail. Detective contracts can later verify whether the exemption was justified.
Built-in Contracts
The default config ships with 15 contracts across three categories:
| ID | What it checks | On failure |
|---|---|---|
CON-AGENT-001 | Agent directories have correct permissions/ownership | alert |
CON-AGENT-002 | Agent instructions (AGENTS.md) are root-owned, read-only | alert |
CON-SEC-001 | Skill files owned by root | escalate |
CON-SEC-002 | Agent git repos have no unauthorized remotes | alert |
CON-SEC-003 | Exposed artifacts have valid manifests | alert |
CON-SEC-004 | Artifacts don't contain secrets | escalate |
CON-SEC-005 | Critical files have immutable bit set | escalate |
CON-SYS-001 | Disk free ≥ 15% | halt_agents |
CON-SYS-002 | Memory available ≥ 10% | halt_agents |
CON-SYS-003 | Load average ≤ 2x CPU cores | halt_agents |
CON-SYS-004 | No agent session exceeds 30 minutes | alert |
CON-SYS-005 | Audit log directory is writable | halt_agents |
CON-SYS-006 | /etc/conos/env is mode 600 root:root | alert |
CON-SYS-007 | auditd service is active | alert |
CON-SYS-008 | System state snapshot + change diff | alert |
Security Model
Three layers, each independent:
- Prevention — the OS enforces boundaries.
File permissions (
chmod 700), POSIX ACLs on inboxes,nftablesper-uid firewall rules, andsudoersallowlists. A compromised agent cannot bypass these because they are enforced by the kernel, not the application. - Immediate detection —
auditdwatches on critical directories generate events the instant a write is attempted. Combined with immutable files, the write fails but is always logged. - Periodic verification — detective contracts run every 60 seconds. They verify that preventive controls stayed in place and that system state matches intent. This is the safety net that catches anything the first two layers missed.
The design assumption is that every agent will eventually process adversarial input. A fully compromised agent should accomplish nothing beyond its intended capability.
Trust Boundaries
Trust is determined by provenance (file ownership), not content:
- User input (outer inbox) — trusted. The user has machine access.
- Agent-routed tasks — semi-trusted. The routing
agent may have been influenced by external content. Agents see
[trust:verified]or[trust:unverified]in the task header. - External data (email, URLs, APIs) — untrusted. Agents are instructed not to take consequential actions based on unverified input.
Provenance is checked via lstat() on the task file. Files
owned by root or a member of the trusted group are verified.
Agent-owned files are unverified. Symlinks are always treated as
unverified regardless of target ownership.
Immutable Files
Critical configuration files are set immutable with chattr +i
during bootstrap. Even root cannot modify them without first removing the
immutable flag — which requires CAP_LINUX_IMMUTABLE. No agent
has this capability.
Immutable files:
/etc/conos/conos.toml— config/etc/conos/env— API keys/etc/conos/roles/*/skills/*.md— skill files/etc/conos/agents/*/AGENTS.md— agent instructions/etc/conos/contracts/*.yaml— contract definitions/etc/sudoers.d/conos-*— sudoers rules/etc/systemd/system/conos-*— systemd units/usr/local/bin/conctl— the binary itself
Legitimate changes go through conctl (e.g.,
conctl config apply, conctl bootstrap), which
temporarily removes the immutable bit, makes the change, and re-applies it.
The sysadmin agent has no sudoers entry for chattr — it can
only go through the controlled conctl path.
Drivers
A driver connects an external interface to the conspiracy. It receives messages from a platform, forwards them as tasks via SSH, polls for responses, and pushes them back. Drivers are standalone Go binaries that run on any host with SSH access to the container.
Discord
Receives Discord messages, forwards to the concierge, posts responses back to the channel or DM.
cd conos/drivers/discord
export DISCORD_BOT_TOKEN="..."
export DISCORD_CHANNEL_ID="..." # omit for DM mode
export CONOS_SSH_HOST="your-instance"
go build . && ./discord
Supports slash commands: /status, /logs,
/responses, /clear (reset session history).
Artifact links are resolved to signed URLs. Secrets are redacted from
responses before posting.
OpenClaw
Receives webhooks from the OpenClaw Gateway, which bridges WhatsApp, Telegram, Slack, and other messaging platforms.
cd conos/drivers/openclaw
export OPENCLAW_HOOK_TOKEN="your-secret"
export OPENCLAW_GATEWAY_URL="http://your-gateway:18789"
export CONOS_SSH_HOST="your-instance"
go build . && ./openclaw
Configure the Gateway to POST to
http://<driver-host>:3847/webhook?token=<OPENCLAW_HOOK_TOKEN>.
Messages flow: user's app → Gateway → webhook → SSH task →
concierge → agent → response poll → Gateway API → user's app.
conos (host-side)
conos install # pull image, create env, start
conos start # boot the instance
conos stop [--force] # stop the instance
conos status # agent status overview
conos config apply # push config into running instance
conos agent list # list agents
conos agent <message> # task the concierge
conos agent task <name> <message> # task a specific agent
conos agent logs [-f] [-n N] [name] # tail audit log
conos agent kill <name> # stop a running agent
conctl (inside the container)
You rarely need these directly. They are what conos calls over SSH.
# Operations
conctl bootstrap # provision the conspiracy from config
conctl run <agent> # execute one task cycle for an agent
conctl status # agent status
conctl logs [-f] [-n N] [agent] # audit log
conctl task [--agent <name>] <msg> # drop a task into an inbox
conctl kill <agent> # stop agent systemd units
conctl clear-sessions [agent] # wipe PicoClaw session history
# Contracts
conctl healthcheck # evaluate all contracts
conctl doctor # system verification report
conctl brief # system state summary for agents
conctl manifest show # dump expected system state as YAML
# Protocol contracts
conctl protocol check <id> [--exempt <check> --reason <reason>]
conctl protocol list # list protocol contracts
# Package management
conctl package install <pkg> --agent <name> [--save]
conctl package remove <pkg> --agent <name> [--save]
conctl package list [--agent <name>]
# Artifacts
conctl artifact create|show|link|verify
conctl task-contract open|claim|update|show
make targets (from the container repo)
make image # build the container image
make deploy # build + restart
make run # start the container
make stop # stop the container
make status # agent health and last activity
make logs # tail audit log
make responses # latest response from each agent
make task MSG="..." # send a task to the concierge
Inspecting a Running System
SSH into the container or use docker exec:
docker exec -it conos bash
Useful things to look at:
# Config
cat /etc/conos/conos.toml
# Agent home directories
ls /srv/conos/agents/
# Inboxes (where tasks land)
ls /srv/conos/agents/*/inbox/
# Outboxes (where responses appear)
ls /srv/conos/agents/*/outbox/
# Who can write to an inbox
getfacl /srv/conos/agents/sysadmin/inbox/
# Audit log
journalctl -u "conos-*" --no-pager -n 50
# Contract results
conctl doctor
# System state brief (what agents see)
cat /srv/conos/context/system-state.md
# Who owns what
stat /srv/conos/agents/concierge/
# Check immutable flags
lsattr /etc/conos/conos.toml /usr/local/bin/conctl
Every agent is a Linux user. Every inbox is a directory. Every permission boundary is a file mode. There is no abstraction layer to debug — just the OS.
Troubleshooting
Agent not responding to tasks
# Is the inbox watcher running?
systemctl status conos-concierge.path
# Is the service failing?
journalctl -u conos-concierge.service -n 20
# Is there a task stuck in the inbox?
ls -la /srv/conos/agents/concierge/inbox/
# Is the agent's session oversized? Clear it.
conctl clear-sessions concierge
Contract keeps failing
# See the full report
conctl doctor
# Run the check manually using the verify command from the contract
find /etc/conos/roles -name '*.md' ! -user root
# Check if immutable bit was stripped
lsattr /etc/conos/conos.toml
Bootstrap fails
# Check the bootstrap log
journalctl -u conos-bootstrap.service --no-pager
# Re-run bootstrap manually
conctl bootstrap
# Verify expected state vs actual state
conctl doctor
"Permission denied" errors
# Check who owns the file
stat /path/to/file
# Check ACLs
getfacl /path/to/directory
# Check what the agent's sudoers allows
cat /etc/sudoers.d/conos-sysadmin