Threat Model
Framework: STRIDE
Last updated: April 2026
Scope: Civitas runtime v0.3+
Components in scope
| Component |
Role |
AgentProcess |
Core agent unit — runs handle(), manages mailbox and state |
Supervisor |
Fault tolerance — restart policy, escalation chain |
MessageBus |
Name-based routing — delivers messages between agents |
InProcessTransport |
In-process asyncio queue transport |
ZMQTransport |
Multi-process transport (XSUB/XPUB proxy) |
NATSTransport |
Distributed transport (JetStream) |
HTTPGateway |
Edge process — translates HTTP to Civitas messages (v0.4+) |
StateStore |
Agent state persistence (SQLite, in-memory) |
Plugin system |
LLM providers, tools, MCP clients |
EvalAgent |
Corrective observability — monitors and corrects agent behaviour |
STRIDE analysis
AgentProcess
| Threat |
Category |
Risk |
Mitigation |
| Agent code executes arbitrary system calls — supply chain attack via a malicious dependency or compromised agent class |
E Elevation of Privilege |
High |
Sandbox agent processes (M4.2); audit third-party agent code before deployment |
| Infinite message loop between agents saturates mailboxes and exhausts CPU |
D Denial of Service |
Medium |
Bounded mailboxes (asyncio.Queue(maxsize=N)) drop messages at capacity; EvalAgent can halt runaway agents |
emit_eval() target name is controlled by calling agent — could redirect eval events to wrong evaluator |
S Spoofing |
Low |
Default target is "eval_agent"; only override in trusted orchestrator code |
Agent state in StateStore restored on restart — poisoned state persists across crashes |
T Tampering |
Medium |
Encrypt SQLite state at rest (M4.2); validate state schema on on_start() |
| No built-in audit of which agent sent which message — repudiation possible |
R Repudiation |
Medium |
OTEL spans include sender, recipient, trace_id on every message — use OTLP exporter for durable audit trail |
Supervisor
| Threat |
Category |
Risk |
Mitigation |
| Crash-loop attack — compromised child crashes repeatedly to exhaust restart budget and force parent shutdown |
D Denial of Service |
Medium |
Sliding-window restart limits (max_restarts + restart_window); escalation chain terminates the supervisor tree rather than looping forever |
| Restart of a compromised agent re-instantiates malicious code |
E Elevation of Privilege |
High |
Immutable agent class references loaded at startup; no dynamic code loading at restart |
| Escalation chain propagates fault upward — a single bad agent can take down the entire tree |
D Denial of Service |
Low |
Deliberate design choice (OTP model); scope supervisors narrowly to limit blast radius |
MessageBus
| Threat |
Category |
Risk |
Mitigation |
Malicious agent sends messages with a spoofed sender field |
S Spoofing |
High |
sender is set by the bus at route time for internal messages; message signing (M4.2) will cryptographically bind sender to the sending process |
_agency.* system message namespace accessible to any agent that constructs the right type |
S Spoofing / E Elevation of Privilege |
High |
_agency.* validation enforced on all routes in MessageBus.route() — only the bus itself may send system messages |
Glob-pattern broadcast() reaches unintended agents |
I Information Disclosure |
Low |
Broadcast patterns are caller-controlled; only use from trusted orchestrator agents |
| Message payload contains sensitive data logged at DEBUG level |
I Information Disclosure |
Medium |
Disable logging.DEBUG in production; configure OTLP exporter to strip payload attributes |
Transport layer
InProcessTransport
| Threat |
Category |
Risk |
Mitigation |
| Shared asyncio event loop — one agent blocking the loop delays all others |
D Denial of Service |
Medium |
Avoid blocking calls in handle(); use asyncio.to_thread() for CPU-bound work |
ZMQTransport
| Threat |
Category |
Risk |
Mitigation |
| Any local process can connect to the XPUB/XSUB proxy and inject messages |
S Spoofing / T Tampering |
High |
Bind proxy to 127.0.0.1 (not 0.0.0.0); enable ZMQ CURVE authentication (M4.2) |
| Messages transmitted over IPC/TCP in cleartext (msgpack, not encrypted) |
I Information Disclosure |
High |
Enable ZMQ CURVE encryption (M4.2); or terminate TLS at a sidecar (Envoy) |
| No message authentication — MITM can modify in-flight messages |
T Tampering |
High |
Message signing (M4.2) |
NATSTransport
| Threat |
Category |
Risk |
Mitigation |
| NATS without TLS transmits all messages in cleartext |
I Information Disclosure |
High |
Always configure NATS with TLS in production (tls: {cert, key, ca} in NATS server config) |
| NATS JetStream subjects are predictable — any authenticated NATS client can subscribe |
I Information Disclosure |
Medium |
Use NATS authorization (accounts + users) to isolate Civitas subjects from other workloads |
| No built-in agent identity validation on NATS subject routing |
S Spoofing |
Medium |
NATS credential file per Worker process (M4.2); sender field validated at application layer |
HTTPGateway (v0.4+)
| Threat |
Category |
Risk |
Mitigation |
| No request authentication by default — any client can send messages to any agent |
S Spoofing / E Elevation of Privilege |
High |
Deploy API key or JWT middleware on all production routes |
HTTP request body forwarded as message.payload without sanitisation |
T Tampering |
Medium |
Validate with @contract (Pydantic); agents must not trust message.payload without validation |
| Unbounded request rate exhausts agent mailboxes |
D Denial of Service |
High |
Add RateLimiter(GenServer) middleware; configure request_timeout |
| Plaintext HTTP/1.1 exposes request bodies |
I Information Disclosure |
High |
Always configure TLS cert/key; HTTP/2 requires TLS (ALPN) |
| Path traversal via unsanitised path parameters |
T Tampering |
Low |
@route path parameters are string-typed; agents must validate before using as filesystem paths |
StateStore
| Threat |
Category |
Risk |
Mitigation |
| SQLite state file stored in plaintext on disk |
I Information Disclosure |
High |
Encrypt at-rest via filesystem encryption or SQLite encryption extension (M4.2); avoid storing secrets in state |
| SQLite file writable by any process with filesystem access |
T Tampering |
High |
Restrict file permissions (chmod 600); run agents as a dedicated OS user |
| In-memory store is lost on process restart — agent assumes wrong initial state |
T Tampering |
Low |
AgentProcess restores state from StateStore before on_start() — in-memory store is intentionally ephemeral |
Plugin system
| Threat |
Category |
Risk |
Mitigation |
| LLM provider API keys in topology YAML committed to source control |
I Information Disclosure |
Critical |
Always use !ENV VAR_NAME in YAML; never hardcode keys; gitleaks pre-commit hook blocks accidental commits |
Malicious Python package in additional_dependencies exfiltrates keys at import time |
S Spoofing / I Information Disclosure |
High |
Pin all dependency versions; enable Dependabot + pip-audit in CI |
MCP server connected via connect_mcp() executes arbitrary subprocess commands |
E Elevation of Privilege |
High |
Only connect MCP servers from trusted sources; validate command and args at config parse time |
Tool execute() calls are unrestricted — a tool can make arbitrary network calls |
I Information Disclosure / E Elevation of Privilege |
Medium |
Sandbox tool execution (M4.2); review tool schemas before registration |
EvalAgent
| Threat |
Category |
Risk |
Mitigation |
EvalAgent itself is compromised — sends false halt signals to healthy agents |
D Denial of Service |
Medium |
Run EvalAgent under its own supervisor subtree; log all correction signals via OTEL |
| Rate limit bypass — attacker sends eval events faster than the window resets |
D Denial of Service |
Low |
Rate limiter tracks timestamps server-side; no client-controlled state |
civitas.eval.halt message forged by a non-eval agent |
D Denial of Service |
Medium |
Message signing (M4.2) will bind message type to sender identity |
Risk summary
| Risk level |
Count |
Primary mitigation |
| Critical |
1 |
LLM API keys in YAML — use !ENV + gitleaks |
| High |
10 |
Message signing, transport TLS/CURVE, gateway auth (M4.2) |
| Medium |
8 |
Mailbox bounds, OTEL audit, NATS isolation, state encryption |
| Low |
5 |
Broadcast scope, path params, in-memory state |
The majority of High findings are addressed by M4.2 Security Hardening (mTLS, message signing, credential isolation, sandboxing).
Out of scope
- Threats to the NATS server itself (covered by NATS documentation)
- Threats to the LLM provider APIs (covered by provider security policies)
- Social engineering or insider threats
- Physical access to the host machine