Skip to content

Feature: User-configurable shell guard rules via config.json + /guard menu command #229

Description

@yablokolabs

Feature: User-configurable shell guard rules via config.json + /guard menu command

Problem

When running atomic-agent via Telegram (or any non-interactive surface), every shell command that doesn't match a built-in guard rule triggers an approval prompt. This creates a painful experience for automated/remote workflows:

Approval requested
tool: os.shell.run
kind: shell command
reason: no shell guard rule matched; approval required
preview: cargo run -p quantumclaw-app --example logistics_dwave_shadow_compare > /tmp/router_benchmark.txt 2>&1

The only current options are:

  1. Press s during TUI to grant the category for the session (not available on Telegram)
  2. Raise approvalLevel to 5 (approves everything — too permissive)
  3. Edit source code to add custom rules (not practical for users)

Proposed Solution

Two complementary pieces:

1. Config-based rules (config.json)

Add a shell.guard section to config.json for persistent, version-controllable rules:

{
  "shell": {
    "guard": [
      {
        "pattern": "cargo run *",
        "action": "allow"
      },
      {
        "pattern": "cargo build *",
        "action": "allow"
      },
      {
        "pattern": "npm test *",
        "action": "allow"
      }
    ]
  }
}

2. /guard menu command (TUI + Telegram)

For quick, interactive management without editing JSON:

Command Description
/guard Show current user-defined rules
/guard add <pattern> [allow|block|ask] Add a new rule
/guard remove <pattern> Remove a rule
/guard clear Clear all user rules
/guard on|off Temporarily enable/disable all user rules

The menu entry fits naturally in the Setup group alongside /mouse, /theme, /analytics:

Setup
  ├── Theme…
  ├── Mouse…
  ├── Analytics
  ├── Guard…          ← new
  ├── Enable or disable a skill…
  └── Create, cancel or run a task…

Rule format

Each rule has:

  • pattern — glob-style pattern matching the full command string (e.g., cargo run *, git status)
  • action — one of "allow" | "block" | "approval_required"
  • cwd (optional) — restrict rule to a specific working directory

Matching behavior

User-defined rules are evaluated after built-in hardline/dangerous rules but before the default approval_required fallback:

hardline → dangerous → trusted → safe-allow → [user rules] → approval_required

This ensures:

  • Catastrophic commands (rm -rf /, mkfs, etc.) are always blocked regardless of user rules
  • User rules cannot override safety hardlines
  • Users can pre-allow patterns they trust without raising the global approval level

Implementation sketch

Config layer

  1. Add shell.guard to UserConfigFile and AtomicAgentConfig interfaces in config-schema.ts
  2. Create rules-user-config.ts in src/tools/os/shell-command-guard/ implementing the Rule interface
  3. Inject the user rule layer into guard-engine.ts between safeAllowRule and the default fallback
  4. Bump USER_CONFIG_VERSION to 43 and add migration in parseUserConfigFile

Menu command

  1. Add menu node in menu-registry.ts under setup group with /guard slash
  2. Add dispatch handler in slash-command-handler.ts for /guard subcommands
  3. Wire persistence through the orchestrator to read/write shell.guard in config.json

Tests

  1. Pattern matching tests (glob, cwd scoping, metachar rejection)
  2. Pipeline placement tests (hardline override protection)
  3. Config round-trip tests (parse → serialize → parse)
  4. Slash command dispatch tests

Use cases

  • Telegram automation: Pre-allow project-specific commands (cargo run, npm test) without raising global approval level
  • Quick toggles: /guard off to temporarily disable all user rules during debugging
  • CI/CD pipelines: Allow specific build/test commands in trusted directories
  • Shared environments: Per-project guard rules that don't affect other projects

Security considerations

  • User rules sit below hardline rules in the pipeline — they cannot unblock blocked commands
  • block action in user rules adds a deny layer before the default approval
  • Optional cwd scoping limits rules to specific directories
  • No pattern can match shell metacharacters (same as safeAllowRule)
  • /guard on|off is a session toggle only — config.json rules persist independently

Alternatives considered

  • Per-project .atomic-agent/guard.json: Could be a future extension, but config.json keeps it centralized
  • Session-only grants: Already exist via [s] key, but don't persist across restarts
  • Raising approvalLevel: Too broad — the whole point is fine-grained control

Would love maintainer feedback on:

  1. Is shell.guard the right config key name?
  2. Should cwd scoping be in v1 or deferred?
  3. Any concerns about the rule pipeline placement?
  4. Preference for /guard as a standalone command vs subcommand of /privacy?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions