Skip to content

Add global-level permissions from user config#2230

Merged
dgageot merged 1 commit intodocker:mainfrom
trungutt:feat/global-permissions
Mar 25, 2026
Merged

Add global-level permissions from user config#2230
dgageot merged 1 commit intodocker:mainfrom
trungutt:feat/global-permissions

Conversation

@trungutt
Copy link
Contributor

@trungutt trungutt commented Mar 24, 2026

Summary

Closes #52

Adds a permissions field to the user config (~/.config/cagent/config.yaml) so users can define permission patterns that apply across all sessions and agents as user-wide defaults.

Configuration

# ~/.config/cagent/config.yaml
version: v1
settings:
  permissions:
    allow:
      - "read_*"
      - "list_directory"
      - "shell:cmd=git*"
      - "shell:cmd=ls*"
      - "mcp:github:*"
    deny:
      - "shell:cmd=rm*"
      - "shell:cmd=sudo*"
    ask:
      - "shell:cmd=docker*"

Same pattern syntax as agent-level permissions — globs, argument matching (shell:cmd=git*), and MCP-qualified names (mcp:server:tool).

How it works

Global patterns are merged into the team's checker before the runtime is created. The runtime has no concept of "global" — it sees a single pre-merged checker.

Before

  Tool call arrives
         │
         ▼
  ┌──────────────┐
  │  YOLO mode?  │──── yes ──────────────────┐
  └──────┬───────┘                           │
         │ no                                │
         ▼                                   │
  ┌────────────────────┐                     │
  │  Session Checker   │                     │
  │  (in-session rules)│                     │
  └──────────┬─────────┘                     │
             │                               │
    ┌────────┼────────┐                      │
  Deny     Ask*    Allow                     │
    │        │        │                      │
    │        ▼        │                      │
    │  ┌─────────────────────┐               │
    │  │  Team Checker       │               │
    │  │  (from agent.yaml)  │               │
    │  └───────────┬─────────┘               │
    │              │                         │
    │     ┌────────┼────────┐                │
    │   Deny     Ask*    Allow               │
    │     │        │        │                │
    │     │        ▼        │                │
    │     │  ┌────────────┐ │                │
    │     │  │ Read-only? │ │                │
    │     │  └─────┬──────┘ │                │
    │     │   yes  │  no    │                │
    │     │    │   ▼        │                │
    │     │    │ Prompt     │                │
    │     │    │ user       │                │
    │     │    │  ┌──┴──┐   │                │
    │     │    │  Y    N    │                │
    ▼     ▼    ▼  ▼    ▼    ▼                ▼
          EXECUTE       DENY

After

  ~/.config/cagent/config.yaml     agent.yaml
  ┌──────────────────────┐    ┌──────────────────┐
  │ settings:            │    │ permissions:      │
  │   permissions:       │    │   allow: [...]    │
  │     allow: [...]     │    │   deny:  [...]    │
  │     deny:  [...]     │    └────────┬─────────┘
  └──────────┬───────────┘             │
             ▼                         ▼
      Global Checker            Team Checker
             │                         │
             └────────┐   ┌────────────┘
                      ▼   ▼
              permissions.Merge()
                      │
              team.SetPermissions()
                      │
                      ▼
             Runtime created with
             single merged checker

  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

  Tool call arrives
         │
         ▼
  ┌──────────────┐
  │  YOLO mode?  │──── yes ──────────────────┐
  └──────┬───────┘                           │
         │ no                                │
         ▼                                   │
  ┌────────────────────┐                     │
  │  Session Checker   │                     │
  │  (in-session rules)│                     │
  └──────────┬─────────┘                     │
             │                               │
    ┌────────┼────────┐                      │
  Deny     Ask*    Allow                     │
    │        │        │                      │
    │        ▼        │                      │
    │  ┌─────────────────────┐               │
    │  │  Merged Checker     │               │
    │  │  (team + global)    │               │
    │  └───────────┬─────────┘               │
    │              │                         │
    │     ┌────────┼────────┐                │
    │   Deny     Ask*    Allow               │
    │     │        │        │                │
    │     │        ▼        │                │
    │     │  ┌────────────┐ │                │
    │     │  │ Read-only? │ │                │
    │     │  └─────┬──────┘ │                │
    │     │   yes  │  no    │                │
    │     │    │   ▼        │                │
    │     │    │ Prompt     │                │
    │     │    │ user       │                │
    │     │    │  ┌──┴──┐   │                │
    │     │    │  Y    N    │                │
    ▼     ▼    ▼  ▼    ▼    ▼                ▼
          EXECUTE       DENY

* Ask = no matching pattern, fall through to next checker

The runtime evaluation is identical — the only difference is the checker now contains patterns from both sources.

@trungutt trungutt changed the title feat: add global-level permissions from user config Add global-level permissions from user config Mar 24, 2026
@trungutt
Copy link
Contributor Author

/review

// WithGlobalPermissions sets user-level permission patterns loaded from the
// user config (~/.config/cagent/config.yaml). These are evaluated after session
// and team permissions in the approval cascade, acting as user-wide defaults.
func WithGlobalPermissions(checker *permissions.Checker) Opt {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the runtime needs to know that global permissions exist, the runtime should just get a permissions checker that is already merged

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — reworked. Global patterns are now merged into the team's checker in run.go before the runtime is created (permissions.Merge() + team.SetPermissions()). The runtime has no concept of "global" and receives a single pre-merged checker.

Copy link

@docker-agent docker-agent bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assessment: 🟢 APPROVE

This PR correctly implements global-level permissions from user config. The code is well-structured with proper nil checks and comprehensive test coverage.

Key strengths:

  • Proper permission cascade: Session → Team → Global → ReadOnlyHint → Ask
  • Safe nil handling throughout (checks before creating options, checks before accessing fields)
  • Comprehensive test coverage (7 new tests covering deny, allow, cascade priority, YOLO override, and merging)
  • Clear documentation in comments

Verified:

  • No nil pointer dereferences (all accesses properly guarded)
  • Permission cascade logic is correct and matches documentation
  • Global permissions are properly integrated into the runtime via options pattern
  • Tests validate all priority scenarios

The implementation follows Go best practices and the project's existing patterns.

@trungutt trungutt force-pushed the feat/global-permissions branch from 733673b to f7d632b Compare March 24, 2026 19:50
Allow users to define permission patterns in ~/.config/cagent/config.yaml
that apply across all sessions and agents as user-wide defaults.

Global patterns are merged into the team's permission checker before the
runtime is created, so the runtime receives a single pre-merged set.
@trungutt trungutt force-pushed the feat/global-permissions branch from f7d632b to 1fe3f42 Compare March 24, 2026 20:01
@trungutt
Copy link
Contributor Author

/review

@docker-agent
Copy link

docker-agent bot commented Mar 24, 2026

Assessment: 🟢 APPROVE

No bugs found in the changed code. The implementation correctly:

  • Loads global permissions from user config
  • Merges them with team permissions using the new Merge() function
  • Handles nil checkers appropriately
  • Creates fresh team instances for each runtime/session

The permission merge logic is sound and follows Go best practices.

@trungutt trungutt marked this pull request as ready for review March 25, 2026 07:55
@trungutt trungutt requested a review from a team as a code owner March 25, 2026 07:55
@dgageot dgageot merged commit d6f7884 into docker:main Mar 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] - Add abort (or skip) option to the tool execution confirmation

3 participants