Skip to content

feat: Claude provider: allow permission_mode to be configured via config.toml #166

Description

@josephfung

Problem

The Claude provider hardcodes permissionMode to 'bypassPermissions' (claude-provider.ts L178), which translates to --dangerously-skip-permissions when spawning the Claude Code subprocess.

This prevents Zora from running inside Docker containers, because Claude Code refuses that flag when running as root:

--dangerously-skip-permissions cannot be used with root/sudo privileges for security reasons

Docker containers run as root by default, and Zora's own Dockerfile creates state directories under /root/.zora.

Why this matters

The repo already ships a Dockerfile (with CMD ["node", "dist/cli/daemon.js"]) and a docker-compose.yml with ZORA_BIND_HOST=0.0.0.0, so containerized deployment is a supported path. This hardcoded permission mode is the only thing blocking it from working with the Claude provider.

Containerized deployment is a natural extension of Zora's security model:

  • Defense-in-depth with Zora's policy engine: Zora's locked-by-default policy.toml enforces what the agent can do; the container boundary enforces what the process can reach. They complement each other naturally.
  • Clean separation from the host: Zora's Claude provider spawns subprocesses, writes to ~/.zora, and installs npm packages. A container keeps all of that isolated from the user's own dev environment and makes teardown trivial.
  • Reproducible setup: docker compose up gives anyone evaluating Zora an identical environment — no "install Node 20, install Claude CLI, run init, configure policy" sequence to get wrong.

Making permission_mode configurable is a small change that unlocks all of this without changing any defaults or security posture for existing users.

Proposed fix

Allow permission_mode to be set in config.toml per provider, falling back to the existing default:

[[providers]]
name = "claude"
type = "claude-sdk"
permission_mode = "acceptEdits"  # works as root in Docker

The code change is minimal:

  1. Add permission_mode? to ClaudeProviderConfig and ProviderConfig in types.ts
  2. Read config.permission_mode in the ClaudeProvider constructor before falling back to options.permissionMode then 'bypassPermissions'

Impact

  • Security policy: No change to Zora's security model. The permission mode is already a Claude Code concept — this just makes it configurable rather than hardcoded. Operators who don't set it get the existing 'bypassPermissions' default.
  • Provider routing: No impact. Only affects how the Claude subprocess is spawned.
  • Backward compatible: Existing configs without permission_mode behave identically.

Context

I'm running Zora in Docker to keep it isolated from my local dev environment — clean separation of Zora's dependencies, easy teardown with docker compose down -v, and reproducible setup if I share the config with others. Everything works (policy enforcement, audit logging, dashboard, SSE streaming) except this permission mode issue blocks the Claude provider from executing tasks.

Working patch in my fork: josephfung@5b68799

Happy to open a PR if this approach aligns with your design.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions