Skip to content

Add per-model directory scoping to restrict allowed roots #504

Description

@Hrovatin

Create issue about this in main dreb repo and tag aebrer user for feedback. Make issue as follows: First state main request (scope some models to specific dirs, give exmaple of models that store data). Then provide different options we explored so far for solutions and list their pros/cons, what is needed to get them up and running


Summary

As a user who mixes trusted and untrusted providers, I want to scope some models to specific directories (e.g. only the current project or an explicit allow-list) while other models remain global. Typical example: allow Muse Spark (and any other provider that stores/trains on inputs) only inside the current project, but let Anthropic/OpenAI/Codex models roam as today. This is a privacy/data-boundary feature, not just a read-only toggle.

cc @aebrer — would love your take on the preferred direction before anyone builds it.

Current Behavior

  • No per-model filesystem scoping exists. enabledModels / --models only limits which models are selectable for cycling.
  • context.trustedFolders / context.autoLoadNested only control lazy-loading of AGENTS.md / CLAUDE.md, not tool access.
  • sensitiveFilePaths, secretOutputPatterns, forbiddenCommands are global filters for all models.
  • --tools / --no-tools can remove tool classes globally, but not per-model.
  • An extension can intercept tool_call (ctx.model + ctx.cwd) and return {block: true} — this was verified against examples/extensions/protected-paths.ts / tool-override.ts and core/extensions/types.ts:329 (ctx.model). That is the best available today, but is still JS-level filtering.

Proposed Behavior

Provide a supported way to enforce "this model may only see/edit inside these roots (default: session cwd)" while still letting it do useful work inside them. No specific implementation is prescribed here — this issue is to capture the requirement and the explored solution space and get maintainer feedback on which path to pursue. Any accepted solution should make the untrusted model unable to read/edit outside its allowed roots via dreb-executed tools, and should make the choice of design explicit for bash (the only fundamentally non-containable tool without OS help).

Options Explored So Far — Pros / Cons / Setup

1. Extension with tool_call interception + realpath containment (available today)

How: dreb.on("tool_call", (event, ctx) => { if (restrictedModel(ctx.model)) check insideRoots(ctx.cwd + extraRoots, event.input.path) else allow }) for read/write/edit/grep/find/ls/git/tmp_read/search (clamp searchDir), validate subagent child cwd, block web_search/web_fetch for restricted models. Register in global ~/.dreb/agent/settings.json extensions so subagent children inherit it. bash filtered via regex deny-list.
Pros: Airtight for all dreb-executed tools (dreb resolves paths itself, realpath defeats symlink escapes); per-model via ctx.model.provider/id; no dreb fork needed; already matches existing examples.
Cons: bash filtering is heuristic — bypassable via python -c, base64, ${HOME} indirection, env -i, etc. Same OS user, so --no-extensions disables it. Allow-list maintenance for bash is fragile.
Setup: Drop a ~40-line extension to ~/.dreb/agent/extensions/sandbox-scope.ts and add it to global extensions; no build change.

2. --tools allow-list

How: dreb --tools read,write,edit,grep,find,ls,git,search (from cli/args.ts:229). search/skill/tasks_update are always active.
Pros: Removes whole tool classes without code; simple for pure editing/review work; no bash/network if omitted.
Cons: Global, not per-model; losing bash means no builds/tests/installs/git commit (only read-only git tool remains); still needs path containment for the remaining file tools if roaming is a concern.
Setup: Launch flag, no files.

3. OS-level sandbox (true kernel enforcement)

How: Run the untrusted model session in a separate mount/network namespace: Linux bwrap/firejail/docker run -v $PWD:/work:rw --read-only --network none, macOS separate low-privilege user / sandbox-exec / VM. dreb already supports delegating via ReadOperations/BashOperations and examples/extensions/ssh.ts (/mnt/sandbox${cwd} pattern).
Pros: Only literal "never" for bash — kernel enforces open()/exec() regardless of command obfuscation; network can be cut.
Cons: Heavier setup, platform-specific, spawns/containers needed per session; file ownership/permission mapping.
Setup: Wrapper script or SSH/container delegate + dreb remote execution config.

4. Hypothetical core feature (maintainer decision needed)

How: First-class modelScope in ~/.dreb/agent/settings.json or models.json, e.g. { "muse/spark": { "allowedRoots": ["${cwd}", "~/shared"], "allowedTools": [...], "bash": {"mode":"allowlist"}, "network": false } }, enforced in AgentSession like askMode (not in extension JS) and optionally spawning bash through bwrap/sandbox-exec. Complement: decompose bash into typed tools (npm/cargo/make) so work does not need a free-form shell.
Pros: Per-model, cannot be bypassed with --no-extensions, discoverable settings UI, correct place for policy.
Cons: Requires dreb code change and design choice on bash backend.
Setup: npm run build after implementation; user only edits settings.

Relation to GitHub Copilot in VS Code: Similar in spirit to VS Code's security.workspace.trust + Copilot's per-workspace Tools picker / MCP allow-list (see microsoft/vscode#272320 per-workspace tool config and #315459 per-agent skill scoping), but dreb's proposal adds per-model enforcement in core and kernel-level bash isolation vs. Copilot's confirmation dialogs — i.e. dreb would prove the model cannot escape even with python -c or base64 obfuscation.

Acceptance Criteria

  • Per-model directory scoping use case (e.g. Muse Spark and other data-storing providers) is documented.
  • The explored options above are captured with pros/cons and setup steps.
  • @aebrer is tagged and feedback on preferred direction is requested (no implementation is assumed accepted by filing this issue).

Context

  • Explored in a local dreb session (2026-09-04) with @aebrer/dreb at karin branch. Research verified: allTools including git/tmp_read (core/tools/index.ts:255), --tools/--no-tools (cli/args.ts:108), ctx.model (core/extensions/types.ts:329), extension examples protected-paths.ts / tool-override.ts.
  • Related docs: packages/coding-agent/docs/settings.md (context.trustedFolders), packages/coding-agent/docs/models.md.

Technical Notes

  • Enforcement for file tools belongs in core/agent-session.ts (_installAgentToolHooks / setActiveToolsByName) if made core, mirroring askMode. Per-model identity is model.provider + "/" + model.id.
  • realpath containment (native realpath as used for trustedFolders) is needed to defeat symlink escapes.
  • bash without OS help can only be filtered heuristically; true containment needs bwrap/sandbox-exec/container via BashOperations/ReadOperations remote execution.
  • Subagent children are fresh dreb --mode json processes — they inherit global-settings extensions but not -e flags; any core solution must scope child cwd as well.
  • Even perfect directory containment still sends allowed-dir content to the provider — data-storing concern also needs network: false or local-model choice / redaction.
  • Similar in spirit to VS Code's workspace trust + Copilot's per-workspace tool picker / MCP allow-list, but adds per-model enforcement in core and kernel-level bash isolation (vs confirmation dialogs).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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