🔒

15-Security

🔒

Security is mandatory, not a feature. This page is the canonical threat model and the binding ruleset for the entire system.

1. Trust boundaries

flowchart LR
	User(["User (token bearer)"]) --- API["Laravel API"]
	API --- Workers["Horizon workers"]
	Workers --- Providers["AI providers (egress)"]
	Workers --- WS["Workspace FS (sandboxed)"]
	Workers --- Git["Remote Git host (egress)"]
	API --- DB[("PostgreSQL")]
	API --- Redis[("Redis")]
	Workers --- S3[("Object store")]

The only ingress is the Laravel API. All other components are internal.

2. Threats and mitigations

ThreatMitigation
Stolen API tokenSanctum tokens are scoped (abilities), expire, and are revocable. Login bumps tokens_version. APNs tokens are revoked on logout.
Token used from new IPOptional per-user IP allowlist and login alerts.
Malicious prompt (prompt injection)Tool surface is the only way to act. Tools enforce allowlist/blocklist. Provider output is never executed as shell.
Path traversalWorkspaceService::resolve() rejects .. and absolute paths; only resolves under workspace.root/{project_slug}/.
Disk exhaustionPer-project disk quota; soft warn at 80%, hard refuse at 100%. Old snapshot tarballs garbage-collected after N days.
Forkbomb / runaway commandSubprocess wall-clock + CPU time limits; cgroup memory limits in production; was_killed=true flagged.
Secret exfiltration via printingSecretRedactionProcessor filters known secret patterns from any output before logging or persisting.
Secret leakage in docs/GitGenerated docs and committed content pass through the redaction processor. Pre-commit hook in the run branch refuses files containing OPENAI_API_KEY= / sk-.
Arbitrary repo URL fetchHostname allowlist + scheme allowlist. No credentials in URLs; deploy keys / GitHub App.
Force-push / history rewriteHard-blocked in GitService.
Cross-project contaminationWorkspace lock per project + per-project FS root + per-project RAG project_id filter on every retrieval.
Replay of old events to clientsSSE includes Last-Event-ID; server validates the requester still owns the run.
Privilege escalation via settingssettings:write is a separate ability; secret fields require step-up auth (re-enter password).
SQL injectionEloquent / parameterized queries everywhere; raw SQL only in migration bootstrap and audited.
Mass assignmentModels use $guarded = ['id'] • explicit fillable lists in form requests.

3. Command execution rules

  • Allowlist governs which commands can run. Anything not in the list is blocked.
  • Blocklist is a kill-switch layer: even if accidentally added to the allowlist, blocklist matches refuse the command. Blocklist always wins.
  • Destructive markers force a pre_command snapshot.
  • Commands run in the project workspace dir, with a clean env (PATH, LANG, HOME) plus an injected AGENT_RUN_ID.
  • Per-command timeout from agent_settings.max_command_time.
  • Killed commands set was_killed=true.

Hard-blocked commands

  • rm -rf /, rm -rf ~, rm -rf ../*, fork bombs.
  • mkfs, dd if=, parted, fdisk.
  • chmod /, chown /, chmod -R 777 /, recursive ownership changes on system paths.
  • cat .env, printenv, env (reading env wholesale).
  • git push, git push --force, git push --tags.
  • shutdown, reboot, systemctl.
  • curl http*, wget http* (unless claude_code.allow_web=true).
  • npm publish, composer publish, pip publish, pip install -e file://.
  • Anything containing a redirect to /etc/, /usr/, /var/, /root/.

4. File system rules

  • Workspaces live under workspace.root (e.g. /var/lib/agent-workspaces/{project_slug}) owned by the app's service user.
  • The service user has no sudo, no access to system paths.
  • File operations are restricted to symlink-resolved paths under the workspace root. Symlinks pointing outside are rejected.
  • .env, .git/config, and other sensitive files cannot be read by read_file (returns file_blocked event).
  • Max file size for read/write controlled by limits.max_file_size.

5. API security

  • All endpoints require Sanctum token unless explicitly public (auth/token only).
  • CORS: deny by default; allowlist for the iPhone app and the web console domain.
  • HSTS, secure cookies, CSP for the web console.
  • Rate limits per group (see 14 — API Endpoints).
  • Request body size cap: 1 MB (32 MB for file upload endpoints, when introduced).
  • Output is filtered by API resources; secret fields are never exposed.
  • Idempotency keys for run-start endpoints (header Idempotency-Key).

6. Audit logging

All critical actions append a row to agent_events (run-scoped) or a separate audit_log table (system-scoped, not introduced in v1, but planned). Critical actions include:

  • Settings change (with key, before/after redacted).
  • Provider enabled/disabled.
  • Token created/revoked.
  • Snapshot created/restored.
  • Run approved/rejected.
  • Push attempt (allowed or blocked).

7. Role-based abilities

  • v1 ships a single-owner model with per-token abilities (above).
  • v2 will introduce a role layer: owner (everything), developer (start/control runs, commit, no push, no settings write), viewer (read only).
  • Sanctum abilities are checked in form requests via $request->user()->tokenCan('git:push').

8. Backup & recovery

  • PostgreSQL: managed daily snapshots, point-in-time recovery 7 days minimum.
  • Snapshot tarballs in S3 versioned for 30 days.
  • Workspaces are reproducible (re-clone), so they are not backed up.
  • Disaster recovery target: RPO 24 h, RTO 4 h.

9. Secrets management

  • Secrets live in agent_settings (encrypted) or environment variables that are mounted at boot only.
  • Rotation procedure documented per provider (OpenAI, Anthropic) in docs/SECURITY.md.
  • Compromise drill: revoke at provider → update setting → emit setting_changed event → restart workers.

10. Compliance posture (informative)

  • v1 is intended for single-owner / small-team use; not certified for SOC 2 / GDPR audits yet.
  • Logs and events containing user prompts are considered sensitive and stored within the same tenancy as the project workspace.
  • A future hardening track will introduce CMK encryption for agent_events.payload_json and per-tenant KMS.