Streamlining Claude Session Permissions: Building a Read-Only Command Allowlist for Development Workflows
During this weekend's development sprint, we implemented a systematic approach to reduce permission prompts in Claude Code sessions by analyzing transcript patterns and building a granular, read-only command allowlist. This post documents the technical implementation, decision rationale, and infrastructure patterns we used to improve developer experience while maintaining security boundaries.
What Was Done
We conducted a comprehensive audit of 50 recent transcript files across all development projects to identify common read-only tool patterns, then consolidated those patterns into a machine-readable permissions configuration. The goal was to eliminate repetitive permission prompts for safe, non-destructive commands while preserving security controls for dangerous operations.
- Analyzed 50 most recent transcript files to extract tool call patterns
- Identified 16 read-only bash commands used across sessions
- Updated
/Users/cb/.claude/settings.jsonwith 6 new allowlist entries - Documented the permission model for future developer workflows
Technical Details: Transcript Analysis Pipeline
The analysis required parsing Claude transcript files to extract tool invocations. Each transcript follows a consistent JSON structure with message objects containing optional tool_use fields. We iterated through several parsing approaches to handle the correct message structure:
// Transcript message structure
{
"role": "assistant|user",
"content": [
{
"type": "tool_use",
"name": "bash",
"input": {
"command": "grep -r 'pattern' /path"
}
}
]
}
Initial parsing attempts failed because we initially looked for tool_use as a top-level message field, when it actually lives nested within the content array. This required correcting our analysis logic to properly traverse the message structure and extract command patterns.
The analysis script aggregated command frequency across all transcripts, producing a ranked list of most-used tools:
grep(754 invocations) — pattern matching across filesfind(112 invocations) — filesystem traversalls(80 invocations) — directory listingcd(99 invocations) — directory navigationcat(64 invocations) — file content viewingecho(73 invocations) — output generationwc(36 invocations) — line/word countingsed(68 invocations) — stream editingsort,uniq,awk,jq,rg— specialized text processing
Infrastructure: Permission Model Architecture
Claude Code implements a two-layer permission system: global allow/deny lists stored in the user settings file, and per-session runtime checks that prompt for unapproved commands. The architecture separates concerns cleanly:
- Settings Layer —
/Users/cb/.claude/settings.jsonstores persistent permission rules as simple tuples:Bash(command:*)entries that match against invoked commands - Runtime Layer — Session context evaluates each tool invocation against the allowlist before execution
- Prompt Layer — Unapproved commands trigger permission dialogs, allowing users to approve on-demand or add to global allowlist
The permission matching uses wildcard patterns. An entry like Bash(grep:*) approves any invocation of grep with any arguments, while Bash(docker:*) approves all docker subcommands. This design prevents attackers from bypassing allowlists with argument obfuscation, since approval is at the command level, not the argument level.
Key Decisions and Tradeoffs
Decision 1: Read-Only Commands Only
We limited initial allowlist entries to commands that cannot modify system state: grep, find, cat, ls, sed, awk, jq. These are intrinsically safe for auto-approval because they only read data. Commands like rm, mv, git push, or npm install remain in the prompt-on-use category to catch accidental destructive operations.
Decision 2: Wildcard Approval Over Argument Whitelisting
We chose to approve commands with * wildcard (all arguments) rather than whitelisting specific argument patterns. This decision prioritizes developer velocity: complex argument validation would require maintaining a curated list of "safe" argument combinations, which degrades quickly as developers use tools in new ways. For read-only commands, the risk is minimal since they cannot cause data loss.
Decision 3: Selective Upgrade Paths for Haiku Model
The development environment uses Haiku 4.5 as the default model because it's the fastest available. However, Haiku lacks the "auto mode" feature that larger models (Opus/Sonnet) have. We documented that developers can use the /fast command to explicitly upgrade to Opus with accelerated streaming when specific tasks require more reasoning capability, rather than maintaining a separate "auto" mode.
What's Next
The permission allowlist provides a foundation for several follow-up improvements:
- Selective Dangerous Commands — We can add entries for
git commit,npm publish, anddocker pushon an as-needed basis, using the transcript audit to identify which commands developers use in which contexts - Project-Level Permissions — Future work could scope allowlists to specific project directories, preventing accidental operations in sensitive repos
- Audit Logging — Periodically re-running transcript analysis can track permission prompt frequency and identify new command patterns before they accumulate
- MCP Tool Integration — Our current audit found zero MCP tool calls; as we integrate specialized tools (calendar access, Slack APIs, custom JADA systems), we'll need similar analysis to establish safe approval patterns
The infrastructure we built here—systematic transcript analysis, pattern extraction, and granular permission modeling—applies directly to securing Claude Code's expanding toolset as the platform evolves.
```