Luxe

Security

The permission model, secrets, and what goes out over the wire.

  • Permissive by default + always-on guardrails: allows almost everything, but denies .env reads and catastrophic shell commands (rm -rf /, mkfs, dd of=/dev/…, fork bombs) and asks before working outside the project dir or force-pushing. Real isolation via documented containers.
  • Allow / ask / deny policy keyed by tool with glob patterns and default/strict/read-only/ yolo modes — set via ~/.config/luxe/permissions.json, a tighten-only project .luxe/permissions.json, or --permission-mode/--read-only/--yolo/--trust-project. In the TUI an ask shows a prompt with four answers — once, this session, always (written to the global permissions.json, so it survives a restart), or reject — and an “always” for a path outside the project approves that directory, not just the one file. Headless/loop auto-allow but still enforce every deny.
  • Secrets: 0600 auth file (written atomically, under a cross-process lock, never clobbering what it doesn’t understand), env vars, $ENV/!command resolution; keys held as secrecy::SecretString (zeroized on drop) with redacted Debug/Serialize, and secret-shaped strings scrubbed from tool output, transcripts, and logs. OAuth tokens refresh once under that same lock, so concurrent turns can’t lose a rotating refresh token.
  • Terms-respecting sign-in: subscription OAuth only where the provider permits third-party clients (ChatGPT Codex, GitHub Copilot, OpenRouter). Anthropic and Google are API-key only — their terms forbid harness use of a subscription, and the wizard says so rather than shipping a flow that gets your account actioned.
  • SSRF-guarded web_fetch + screenshot mode=url (blocks localhost/private/metadata/file://, pins DNS). No telemetry, no phone-home — only optional outbound calls are the LLM provider, the web_* tools, and the models.dev metadata refresh.
  • Rust with unsafe_code = "forbid", a lean dependency tree, and a cargo-deny + --locked CI supply-chain gate.