Designing a Sandbox and Permission Policy for a Fintech Monorepo
You're setting up Claude Code for a 40-engineer fintech company working
in one large monorepo that contains: application code, Terraform for
production infrastructure, a secrets/ directory holding
locally-decrypted credentials for local dev, and a scripts/deploy/
directory that can push to production. Engineers should be able to use
Claude Code productively for day-to-day feature work without constant
prompting, but production access needs a hard human checkpoint no
individual engineer can quietly relax.
- Design the sandbox configuration: what should sandboxed commands be able to read and write by default, and what needs an explicit deny?
- Design the permission rules: what goes in
allow, what goes indeny, and what goes inask? Be specific about which of these needs to be enforced at the managed-settings level rather than left to project settings. - A team lead argues the
secrets/deny rule doesn't need to be in managed settings because "it's already in the project's.claude/settings.json, and nobody would remove it." Explain concretely why this reasoning is insufficient for this threat model.
1. Sandbox configuration:
Enable the built-in Bash sandbox in auto-allow mode so routine local
work (tests, builds, linting) doesn't generate constant prompts, but
layer explicit protections on top of the defaults rather than trusting
them as-is. The sandbox's default read behavior — read access to
essentially the whole machine except a short denied list — is not
acceptable for this threat model as-is, because it would let a
sandboxed command read secrets/ and any host credentials by default.
Add sandbox.filesystem.denyRead (or the dedicated
sandbox.credentials.files deny entries) for secrets/ and any host
credential paths engineers might have (~/.aws/credentials,
~/.ssh/), and keep the default write scope (working directory plus
session temp) rather than widening it — there's no reason a routine
task needs write access outside the checkout. Because the Bash sandbox
does not constrain MCP servers or hooks, and this org has real
production access to protect, consider the sandbox runtime or a dev
container for any workflow that also uses MCP servers with production
access, since those run entirely outside the Bash-only sandbox boundary
regardless of how tightly sandbox.filesystem is configured.
2. Permission rules:
allow: narrow, specific entries for routine, low-risk commands engineers run constantly —Bash(npm run test *),Bash(npm run lint), and similar. This is what removes the prompt fatigue that would otherwise push engineers toward broader allow rules or bypass modes.deny:Read(./secrets/**)and equivalent rules for any credential directory, applied unconditionally regardless of permission mode. This is the one guarantee that has to hold no matter what mode any individual engineer happens to be running that day.ask: a rule that forces a prompt for anything touchingscripts/deploy/or the Terraform directory, even in auto mode — something like an ask rule scoped to those paths, or to the specific deploy commands. This is the "hard human checkpoint" the scenario asks for: an explicit ask rule forces a prompt even when auto mode's classifier would otherwise judge the action safe, which is exactly the property needed here.- The
secrets/deny rule and the deploy-path ask rule both need to live in managed settings withallowManagedPermissionRulesOnly: trueset, so no engineer's local or project-level settings edit can remove or relax them. The routineallowrules for tests and lint can safely stay in project-level.claude/settings.jsonsince relaxing those carries little risk and engineers benefit from being able to extend them for their own workflow.
3. Why "it's already in project settings" is insufficient:
A rule in .claude/settings.json is enforced by convention, not by
guarantee — any engineer with write access to the repository (which,
in a 40-person monorepo, is most of the team) can edit that file in a
branch, or a well-meaning engineer chasing a false-positive prompt
could narrow the deny rule "temporarily" and forget to revert it.
Nothing stops that change from merging like any other PR unless a
review process specifically catches it, and even then, an engineer's
own .claude/settings.local.json can add competing rules for their
own sessions without touching the shared file at all. Managed settings
close this gap specifically: with allowManagedPermissionRulesOnly: true, permission rules from project and local settings stop having
any effect at all, and only the managed-settings deny rule is
consulted — there is no supported way for an individual engineer,
however well-intentioned or however much repository access they have,
to work around it. For a rule protecting locally-decrypted production
credentials, "nobody would remove it" is a social expectation; a
managed-settings lock is the actual technical guarantee, and this
threat model — a fintech company with real financial and infrastructure
exposure — is exactly the kind of environment where that distinction
matters enough to be worth the administrative overhead of managed
settings.
Share this question