TriOnyx¶
track what agents see, not just what they do
A security-first agent runtime that tracks information flow between isolated LLM agents. Taint tracking, sensitivity labels, and bandwidth-constrained communication — built on Elixir/OTP for a single operator running their own agents.
The lethal trifecta¶
The term comes from Simon Willison's "The Lethal Trifecta" — the observation that AI agents become critically dangerous when they combine access to private data, exposure to untrusted content, and the ability to communicate externally. TriOnyx was built to address this directly.
Other agent runtimes sandbox capability — restrict filesystem, disable shell, limit network. This misses the point. The real danger isn't any single property of an agent. It's the combination of three.
Architecture¶
graph TB
subgraph triggers["External Triggers"]
WH[Webhooks]
CH[Chat]
CR[Cron]
EM[Email]
end
subgraph gateway["Elixir/OTP Gateway"]
direction TB
LC[Agent Lifecycle]
TT[Taint & Sensitivity Tracking]
MV[Message Validation]
RS[Risk Scoring]
CM[Credential Management]
BQ[BCP Approval Queue]
end
subgraph agents["Agent Containers"]
direction LR
subgraph agentA["Agent A"]
PA[Python + Claude SDK]
FA[FUSE Driver]
end
subgraph agentB["Agent B"]
PB[Python + Claude SDK]
FB[FUSE Driver]
end
end
CON[Connector<br/>Matrix / Slack]
triggers --> gateway
gateway --> agents
gateway <--> CON
style triggers fill:#1c2536,stroke:#30363d,color:#8b949e
style gateway fill:#0f2d1a,stroke:#238636,color:#3fb950
style agents fill:#1a1e24,stroke:#30363d,color:#c9d1d9
style agentA fill:#161b22,stroke:#21262d,color:#c9d1d9
style agentB fill:#161b22,stroke:#21262d,color:#c9d1d9
style CON fill:#1c2536,stroke:#1f6feb,color:#58a6ff
Agents — Python + Claude SDK inside Docker. Communicate with the gateway over JSON Lines.
FUSE — Go driver enforcing per-file read/write policies. Logs all access.
Connector — Bridges the gateway to Matrix, Slack, or email.
Features¶
Isolated containers
Each agent runs in its own Docker container with a per-agent FUSE filesystem, network rules, and no shared state.
Taint tracking
Biba integrity model tracks what each agent has been exposed to. Untrusted web data, user uploads, and API responses all carry taint.
Sensitivity labels
Bell-LaPadula confidentiality tracks access to secrets, credentials, and private data. The two axes are independent.
Information flow enforcement
The gateway intercepts all inter-agent messages and blocks flows that violate integrity or confidentiality constraints.
Bandwidth-Constrained Protocol
Tainted agents communicate with clean agents through structured, bandwidth-limited, human-approvable message formats.
FUSE filesystem
Custom Go driver enforces per-file read/write policies inside each container. O(1) path-trie lookups, structured access logging.
Browser sessions
Agents can get a headless Chromium browser with persistent login sessions from the host. No credential sharing.
Plugin system
Reusable agent extensions (news aggregation, bookmarks, diary) installable from git repos with FUSE-enforced access.
Auditable everything
Structured logs for file access, tool calls, message routing, and policy violations. Queryable audit API.
New here?
Start with the Getting Started guide for a complete walkthrough, or read the Comparison with OpenClaw to understand the security model.
Quick start¶
# Gateway image (Elixir/OTP)
docker build -f gateway.Dockerfile -t tri-onyx-gateway:latest .
# Agent runtime image (Python + FUSE sandbox)
docker build -f agent.Dockerfile -t tri-onyx-agent:latest .
# Connector image (Python, for Matrix chat bridge)
docker build -f connector.Dockerfile -t connector:latest .
The agent image requires a pre-built FUSE driver binary at fuse/tri-onyx-fs.
See the FUSE Driver spec for build instructions.
Or run the gateway standalone:
# Elixir gateway tests
docker run --rm -v $(pwd):/app -w /app \
tri-onyx-gateway:latest mix test
# Go FUSE driver tests
docker run --rm --device /dev/fuse --cap-add SYS_ADMIN \
--security-opt apparmor=unconfined \
-v $(pwd)/fuse:/src -w /src golang:1.22 \
bash -c "apt-get update -qq && \
apt-get install -y -qq fuse3 2>/dev/null && \
go test ./..."
# Python connector tests
docker run --rm -v $(pwd)/connector:/app -w /app \
connector:latest uv run pytest