Ryu runs untrusted LLM-driven agents that can execute code, run shell commands, spawn subprocesses, and make network calls. This document states our trust model honestly, names the boundaries we actually enforce, and is explicit about which defenses are real containment versus best-effort accident prevention.
Report security issues privately to security@ryuhq.com. Do not open a public issue for a
suspected vulnerability.
Please include:
- The affected file or mechanism (e.g.
tool_exec/deno_backend.rs, the SSRF guard, env-scrub). - A minimal reproduction or proof of concept.
- The impact: what boundary is crossed and what an adversary gains.
We aim to acknowledge a report within a few business days and to keep you updated as we triage and fix. Please give us reasonable time to ship a fix before any public disclosure.
- The LLM / agent is untrusted. Assume it is adversarial: it will try to run destructive commands, exfiltrate secrets, reach internal/metadata endpoints, escape the sandbox, and probe around pattern-based filters.
- The operator and the Core process are trusted. Core makes privileged calls on the agent's behalf; the agent never holds the keys directly.
- Containment is the OS and the sandbox process boundary, not in-process heuristics. A pattern matcher running inside the same trust domain as the thing it inspects is not a wall. A determined model can rephrase around it.
These are the three places where Ryu actually contains an adversarial agent:
- Deny-by-default code-exec sandbox. The always-on Deno backend spawns
deno runwith zero--allow-*flags plus--no-prompt: no network, no filesystem, no env, and any attempt fails instead of prompting (apps/core/src/tool_exec/deno_backend.rs). The optional wasmtime/WASI sandbox (featuresandbox-wasmtime) gives the same deny-by-default posture (apps/core/src/sandbox/mod.rs). RYU_TOKEN+ loopback-default bind + fail-closed remote/mesh auth. Core binds127.0.0.1:7980by default. If the mesh is enabled or the bind is non-loopback and no strongRYU_TOKENis set, Core refuses to start (enforce_remote_authinapps/core/src/server/mod.rs).--bind=0.0.0.0cannot bypass it.- Per-agent tool allowlists.
allowed_toolsscopes which tools an agent may call, enforced in-process for least privilege.
The command scanner, output redaction/DLP, the firewall, and env-scrub reduce blast radius and stop honest mistakes. They are not containment: a determined adversarial model can probe around pattern-based heuristics. Treat them as seat belts, not as the wall.
In scope (please report):
- Escape from declared isolation: sandbox or process escape from the Deno/WASI backend.
- Tool-allowlist bypass: invoking a tool an agent was not granted.
- Credential exfiltration via env-scrub bugs.
- Auth bypass or fail-open in remote/mesh binding (reachable off-box without a valid
RYU_TOKEN). - SSRF guard bypass: reaching loopback, private, link-local, or cloud-metadata targets.
- DLP/redaction bypass that leaks a real secret through an output path that should redact it.
Out of scope (by design, not a containment boundary):
- Heuristic-pattern bypass of the command scanner. It is accident prevention, not a wall.
- Prompt injection without a chained boundary violation.
- Third-party skill or plugin malice. Operator review before install is the boundary.
- Social-engineering the operator into approving an action. The operator is trusted.
- Run Core as a non-root user.
- Set a strong, random
RYU_TOKENbefore exposing Core beyond loopback. - Never bind
0.0.0.0without auth. - Lock down per-agent tool and channel allowlists to the minimum each agent needs.
- Review third-party skills and plugins before installing them.
- Keep
RYU_ALLOW_GATEWAY_FALLBACKunset in production (default-deny when the gateway is unreachable). - Set
RYU_EXEC_APPROVAL_MODE=manualfor untrusted workloads.