fleet-rlm is a single-operator, bring-your-own-key (BYOK) backend with no authentication surface of its own. The design keeps secrets out of every API response, binds the service to loopback by default, runs all model-generated code inside a Daytona Sandbox, and bounds or validates everything that crosses the host boundary.
Report vulnerabilities by email to contact@qredence.ai, following the process in the repository’s SECURITY.md. Do not open public issues for vulnerabilities.
Threat model
The assumed attacker is whoever or whatever can reach an exposed Fleet process or feed content into it: a malicious URL fetched by a Tool, a crafted filename in an upload, or model-generated code that tries to touch the host. The defender boundary is the host process plus your own Daytona account. Multi-tenant isolation is out of scope by design: one process, one operator, one Workspace namespace. If you expose Fleet beyond loopback, put your own auth layer in front of it. See Deployment for the reverse-proxy pattern.Secrets and error hygiene
config/fleet.tomlprofiles name environment variables. Fleet resolves only the variables the selected profile explicitly references. Unreferenced ambient variables never reach provider clients, and ambient selectors (FLEET_CONFIG_PROFILE,FLEET_RUN_ENVIRONMENT) are ignored.- Fleet sanitizes public API errors. Run preparation, startup, and provider failures return closed public messages, never raw exception text, stack traces, or credentials. Typed validation errors carry fixed public strings; unknown failures collapse to generic
503responses such as “Attachment storage is unavailable”. - Tool event views are fail-closed allowlists. Fleet projects only declared bounded metadata onto SSE, traces, and the pi-tui timeline; structural values are capped at 256 characters, and a Tool without a declared view exposes no arguments or results at all.
No-auth local API and bind safety
The local API accepts noAuthorization header and no synthetic identity headers. Fleet installs one deterministic process-local scope with fixed user and workspace ids, and the settings API rejects non-loopback clients.
Because there is no auth, binding matters. Every launcher rejects a non-loopback bind host unless you pass --allow-non-loopback-bind deliberately. Fleet raises the error at bind time, which prevents casually exposing Sessions, Workspace operations, and BYOK model execution on a network interface.
Filesystem and upload protections
- Attachment filenames are sanitized before storage: Fleet rejects path-shaped input (
/,\,..) before basename extraction, requires names to match a 255-character allowlist, and refuses hidden dotfiles. Fleet caps uploads at 10 MiB and rejects empty or negative sizes. - Session Workspace paths must be relative POSIX paths with no
\, NUL, or relative components. A reserved.fleetsegment is refused, and paths are bounded at 8 segments, 255 bytes per segment, and 1,024 bytes total. Validation is lexical, without filesystem normalization, so no symlink or canonicalization trick can widen a path. - Mutating Workspace Tools (
delete_workspace_path,edit_workspace_text, and the project-scoped pairs) target regular files and empty directories only, never follow symlinks, and fail closed on FIFOs and other non-regular nodes. Optionalexpected_sha256preconditions guard writes, edits, and deletes against clobbering content that changed since it was read. The compare-and-mutate happens inside one mounted Workspace Agent operation with inode revalidation, which closes the cross-sandbox time-of-check/time-of-use (TOCTOU) window.
URL fetch SSRF defenses
The public-URL source Tool stays behind the host with layered bounds against server-side request forgery (SSRF):- Before any bytes move, Fleet resolves the host and requires every resolved address to be globally routable, rejecting loopback, private, and link-local targets.
- Fleet caps redirects at 3 and re-canonicalizes the URL on each hop.
- Fleet bounds fetches with a 10-second timeout, 64 KiB read chunks, an allowlist of text media types, and per-response byte caps.
- The session source cache is bounded at 64 entries and 64 MiB total, and the HTTP client does not inherit ambient proxy settings.
Memory and Skill injection guardrails
Each Workspace Memory record inmemory/MEMORIES.md gets one addressable id. Duplicates fail closed, and remember is idempotent for the same record, so a model cannot corrupt the log by replaying appends. Each Turn receives only a bounded 4 KiB memory digest.
Bundled Skills can supply the model instructions and manifest-declared UTF-8 resources through load_skill and read_skill_resource only. Skills can never register executable Tools, so a Skill document is not a code-execution vector. See the agent model for the Skill catalog.
Sandbox and dependency containment
Alldspy.RLM-generated code executes inside a Daytona Sandbox interpreter, not on the host. The host only wraps bounded Tools and observes the interpreter boundary.
On the dependency side, pyproject.toml pins litellm>=1.87.0 above releases affected by known CVEs, floors aiohttp and urllib3 at patched releases, and make check-security runs pip-audit plus bandit -lll. Application code reaches LLMs only through dspy.LM, never through litellm directly.
What fleet-rlm does not do
- No user authentication or authorization model anywhere in the API. Identity is the single local scope, and remote access is explicitly not provided.
- Secrets live in environment variables only. There is no secret store and no encrypted config. The settings API can edit non-secret policy but can never read back or inject a referenced secret value.
- No multi-tenant network defense. Everything above assumes loopback plus an operator-provided auth layer if the bind is widened.