Skip to content

feat(claude-code): add managed_settings input for policy delivery via /etc/claude-code#863

Open
morganl-ant wants to merge 2 commits intocoder:mainfrom
morganl-ant:anthropic/managed-settings
Open

feat(claude-code): add managed_settings input for policy delivery via /etc/claude-code#863
morganl-ant wants to merge 2 commits intocoder:mainfrom
morganl-ant:anthropic/managed-settings

Conversation

@morganl-ant
Copy link
Copy Markdown

Problem

The module configures Claude Code's permission posture by reaching around the permission system rather than through it:

  • scripts/install.sh writes bypassPermissionsModeAccepted, autoModeAccepted, and primaryApiKey directly into the user-writable ~/.claude.json. Any process in the workspace can read the API key or flip the acceptance flags back.
  • scripts/start.sh adds --dangerously-skip-permissions to every task launch, even when the template author set an explicit permission_mode. The README has to carry a security warning telling people the module bypasses permission checks.
  • permission_mode, allowed_tools, and disallowed_tools each plumb through a different ad-hoc path (CLI flag, coder subcommand) instead of a single policy surface.

Change

Add a managed_settings input that renders to /etc/claude-code/managed-settings.d/10-coder.json. Claude Code reads that drop-in directory at startup with the highest configuration precedence (above ~/.claude/settings.json and project settings), so template authors get an admin-controlled policy file that users inside the workspace cannot override. The mechanism is a local file read with no API call, so it works identically for the Anthropic API, AWS Bedrock, Google Vertex AI, and AI Bridge / AI Gateway.

managed_settings = {
  permissions = {
    defaultMode                  = "acceptEdits"
    disableBypassPermissionsMode = "disable"
    deny                         = ["Bash(curl:*)", "WebFetch"]
  }
}

Supporting changes:

  • install.sh writes the policy file (root-owned, 0644) and stops writing bypassPermissionsModeAccepted, autoModeAccepted, and primaryApiKey into ~/.claude.json. The API key is already exported via coder_env as CLAUDE_API_KEY; duplicating it on disk is unnecessary. hasCompletedOnboarding stays because there is no env-var alternative for it.
  • start.sh only adds --dangerously-skip-permissions for tasks when no explicit permission_mode is set (same fix as fix(claude-code): don't pass --dangerously-skip-permissions in auto mode #846; included here so this PR is self-contained, happy to drop if fix(claude-code): don't pass --dangerously-skip-permissions in auto mode #846 lands first).
  • permission_mode, allowed_tools, and disallowed_tools are marked deprecated and shimmed into managed_settings.permissions for one release when managed_settings is not provided.
  • README security warning rewritten to point at the policy mechanism instead of telling people the module is unsafe by design.

Relationship to #861

#861 strips this module to install-and-configure and removes permission_mode / allowed_tools / disallowed_tools outright. managed_settings is the natural replacement for those: it is install-time (survives the start.sh removal), it covers everything the dropped variables did plus hooks, env, model, apiKeyHelper, and the rest of the settings schema, and it does not require the module to know anything about how Claude is launched. If #861 lands first I will rebase this on top and drop the deprecation shim and the start.sh hunk.

Validation

  • terraform fmt / terraform validate clean
  • New tests: claude-managed-settings-written, claude-managed-settings-legacy-shim, claude-no-policy-keys-in-claudejson, plus an assertion in claude-auto-permission-mode that --dangerously-skip-permissions is absent when a mode is set
  • Manually verified /etc/claude-code/managed-settings.d/*.json precedence in the Claude Code CLI source

Closes #818. Relates to #284, #846, #861.

Disclosure: I work at Anthropic on the Claude Code team. Happy to adjust scope or split this further if that is easier to review.

@morganl-ant morganl-ant marked this pull request as ready for review April 22, 2026 20:57
@matifali matifali added the version:minor Add to PRs requiring a minor version upgrade label Apr 23, 2026
@matifali
Copy link
Copy Markdown
Member

This would need rebasing after #861 is merged. Thanks for your contribution.

… /etc/claude-code

Re-authored on top of the post-coder#861 install-only module.

Adds a managed_settings variable that the install script writes to
/etc/claude-code/managed-settings.d/10-coder.json. Claude Code reads
this drop-in directory at startup with the highest configuration
precedence, so template authors get an admin-controlled policy file
that users inside the workspace cannot override. The mechanism is a
local file read with no API call, so it works identically for the
Anthropic API, AWS Bedrock, Google Vertex AI, and AI Gateway.

Compared to the original PR against v4.x, this drops the deprecation
shim for permission_mode/allowed_tools/disallowed_tools (those vars
are gone in v5) and the start.sh changes (start.sh is gone). The
~/.claude.json policy-key removal is also dropped from this PR scope
since the surrounding configure_standalone_mode logic changed
substantially in coder#861; can revisit separately if wanted.
@morganl-ant morganl-ant force-pushed the anthropic/managed-settings branch from 913224b to 4b83931 Compare April 27, 2026 23:26
@morganl-ant
Copy link
Copy Markdown
Author

Rebased onto post-#861 main. Scope is now smaller: just the managed_settings input writing /etc/claude-code/managed-settings.d/10-coder.json. The deprecation shim and ~/.claude.json key removal from the original PR are no longer needed since #861 dropped those vars and writes.

…ude.json

These flags pre-accept the permission-mode confirmation dialogs in a
user-writable file. Permission posture is the managed_settings input's
job (delivered via /etc/claude-code/managed-settings.d/, root-owned,
not user-overridable). Onboarding-bypass keys (hasCompletedOnboarding,
hasAcknowledgedCostThreshold, project trust) stay since there is no
managed-settings equivalent for those.
Copy link
Copy Markdown
Member

@matifali matifali left a comment

Choose a reason for hiding this comment

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

I like the flexibility this PR adds. I think this supercedes #866 and covers the use case.

Comment on lines -161 to -162
.autoModeAccepted = true |
.bypassPermissionsModeAccepted = true |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why did we remove this? AFAIK, these were needed for skipping the welcome wizard.

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

Labels

version:minor Add to PRs requiring a minor version upgrade

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-code: Add 'auto' and 'dontAsk' to permission_mode validation

2 participants