HTTP Security
RoboCo can front its API with a fastapi-guard security layer — IP and rate controls, a signature WAF, security headers, cloud-provider and honeypot checks, an emergency kill switch, and three RoboCo-specific content validators — for when you expose the panel/API beyond a trusted LAN.
The layer is off by default. When ROBOCO_GUARD_ENABLED is unset, create_app never mounts the middleware and the request path is byte-for-byte unchanged; the per-route guard decorators are harmless no-ops (roboco/security.py). A personal NAS deploy needs none of this — it's built for cloud/public hosting.
What it does
Armed, the middleware sits outermost and applies, per route:
- Rate limiting & size caps — a baseline throttle plus tighter per-endpoint limits on sensitive routes (provider-key writes, CEO release ops, agent verbs).
- A signature WAF — SQLi/XSS/path-traversal/etc. detection on request bodies, headers, params, and path.
- Security headers — HSTS, CSP,
X-Frame-Options,nosniff, referrer and permissions policies on every response. - Cloud-provider & honeypot checks — block datacenter egress on the highest-value routes; form-trap honeypots on human-facing POSTs.
- Three RoboCo custom validators the stock WAF cannot cover — prompt-injection, secret-exfil, and internal-SSRF scanning on the prompt-facing and agent-content surfaces.
- An emergency lockdown (
ROBOCO_GUARD_EMERGENCY) — a flip-on-without-redeploy switch that blocks every non-whitelisted IP during an active attack.
Behind nginx, guard trusts the single proxy hop for the real client IP, excludes WebSocket upgrades and health/docs paths, and keeps RoboCo's own CORS.
- The internal agent mesh is exempt from WAF/IP-ban entirely. Agents reach the orchestrator directly over the docker bridge, HMAC-authenticated — guard's WAF/IP-ban/rate-limit exists for the external attack surface arriving through nginx, not for already-authenticated internal traffic. Without this exemption, guard IP-banned agent containers the moment a single journal or note body tripped a signature, wedging every subsequent verb that container tried. The exemption is scoped to loopback plus docker's own bridge address-pool range — never a broader LAN/CGNAT range that could also cover a real external client hitting nginx.
- A host-proxied chain (e.g. Tailscale Serve terminating in front of nginx) resolves to the real client, not a whitelisted hop. Guard's own logic peels a single fixed proxy hop, which is correct for a direct nginx client but wrong for one more hop in front of it — that shape used to resolve to a whitelisted gateway IP, silently going inert on the whole surface behind that hop.
ROBOCO_GUARD_TRUSTED_CHAIN_PEERSnames the extra hop(s) to peel (a single exact IP per entry, never a CIDR range — a range would readmit every sibling container's real address into the trusted-hop set). Left unset, only a loopback rightmost hop ever peels; a host-proxied chain behind an unconfigured docker-bridge gateway is logged once so the gap isn't silent.
The orchestrator API itself is not reachable from outside the host either way — see how the orchestrator is kept off the open network for that separate, always-on layer. Guard is what stands between nginx's forwarded traffic and the app once a request does arrive.
Passive first, then enforce
Guard has two enforcement postures:
ROBOCO_GUARD_PASSIVE_MODE | Behaviour |
|---|---|
true (calibrate) | Log-only. Detections are logged but never block — legit and malicious requests pass through. Safe to arm on live traffic to surface false positives first. |
false (enforce) | Detections block the request. |
RoboCo's request bodies are code, SQL, diffs, file paths, HTML, and URLs — task specs, agent notes and commits, RAG queries, git bodies, chat. A stock WAF reads that legitimate traffic as attacks. RoboCo ships a calibration (excluded_detection_body_fields in build_security_config, derived from the real request models) that excludes those free-text fields from WAF scanning so active mode does not false-positive — while the WAF stays active on every structured (id/enum/slug/branch) field and the prompt-injection / secret-exfil / SSRF validators keep firing regardless. Those two content validators are themselves calibrated against RoboCo's own vocabulary — documenting a placeholder secret in a doc-shaped string, or writing neutral engineering prose about a security control, no longer trips them. If you're building your own deployment from a config default rather than the tracked NAS compose below, arm passive first, watch the logs for any straggler false positive, then flip to active.
The NAS composes (docker-compose.yml / docker-compose.yaml) now default to active enforcement (ROBOCO_GUARD_PASSIVE_MODE=false) — passive-mode calibration on that deploy reviewed clean, so a matching request is genuinely blocked, not just logged. ROBOCO_GUARD_FAIL_SECURE=false on that same compose keeps a guard-internal bug from ever 500'ing your own deploy. The registry compose (docker-compose.registry.yml) still omits the guard trio entirely, so a fresh third-party deploy starts from the safe config default (guard off) and calibrates on its own schedule before enforcing. A bare roboco/config.py default is also off. See The three compose files in Deployment for what each compose is for.
Active enforcement means a real false positive now genuinely blocks instead of just logging. The two triggers that used to catch legitimate RoboCo traffic — a documented placeholder secret in a doc-shaped string, and neutral engineering prose about a security control — are the ones the RoboCo-vocabulary calibration above fixed. If you still hit a straggler on unusual content, the instant rollback is env-only: set ROBOCO_GUARD_PASSIVE_MODE=true and restart.
Scanner honeytrap & auto-ban
Automated scanners probe every host for well-known soft spots (/.env, /wp-login.php, /phpmyadmin, /.git/config, …). RoboCo turns those probes against the scanner, in two layers matched to where the traffic actually lands — behind nginx only /api, /ws, /health, and /ready reach the orchestrator, so guard can only see probes on those paths:
- Guard adaptive ban (the
/apisurface). The guardthreat_ban_configcarriesrecon/sensitive_file/cms_probingcategories. A scanner probing those fingerprints on an/apipath is detected on the URL-path scan, and repeated probes from one IP trip a per-IP auto-ban (redis-backed, 24h). This only bans in active mode (passive logs the recon hit) and requires redis (the 24h ban exceeds the in-memory cap). - nginx edge-drop (the classic root paths). The classic scanner paths never reach the orchestrator, so nginx drops them at the edge with
444(closes the connection, returns nothing) before they touch the panel. It's anchored to known scanner fingerprints —/.well-knownand every real panel/API route are untouched — and is always on, independent ofROBOCO_GUARD_ENABLED.
Fail-secure
ROBOCO_GUARD_FAIL_SECURE decides what happens if a security check itself errors: true (default) fails closed — block the request — which is the right default for public hosting. The NAS compose overrides it to false so a guard-internal bug can never 500 your own deploy, even now that the same compose enforces (rather than just logs) everything else.
Enable it
1ROBOCO_GUARD_ENABLED=true2ROBOCO_GUARD_PASSIVE_MODE=false # default on the NAS composes: enforce. true to log-only and calibrate first3ROBOCO_GUARD_FAIL_SECURE=true # false on a personal/NAS deploy4# ROBOCO_GUARD_EMERGENCY=true # attack lockdown kill switch5# ROBOCO_GUARD_EMERGENCY_WHITELIST=1.2.3.4 # extra allowed IPs during lockdown6# ROBOCO_GUARD_TELEMETRY_ENABLED=true # + guard_agent_api_key + guard_project_id7# ROBOCO_GUARD_TRUSTED_CHAIN_PEERS=172.18.0.1 # exact IP(s) of a host proxy hop in front of nginx (e.g. Tailscale Serve's docker gateway); never a CIDR rangeGuard's enforce_https check is hardcoded off — TLS termination sits at nginx, in front of the orchestrator, so the app layer never needs to redirect or reject on scheme. There's no flag for this; it isn't derived from ROBOCO_ENVIRONMENT or any other setting.
See the environment reference for the full flag list.
Telemetry (optional, off)
ROBOCO_GUARD_TELEMETRY_ENABLED reports security events/metrics to a guard-core platform via guard-agent. It is off by default and no data leaves the box while off; flip it on and set ROBOCO_GUARD_AGENT_API_KEY + ROBOCO_GUARD_PROJECT_ID to enable.
Next
→ Environment reference for every flag · Deployment for where to set them · back to Optional subsystems.