Default .env to 0600 but allow overriding the mode#258
Conversation
Reworks the closed jamesyc#252 per maintainer feedback: instead of unconditionally forcing 0600 (which breaks Docker/multi-user setups that need group access to .env), default to 0600 and let power users pick a different mode via the TCAPSULE_ENV_FILE_MODE env var (octal, e.g. 640). Invalid or out-of-range values fall back to 0600. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the ability to configure the file permissions of the generated .env file using the TCAPSULE_ENV_FILE_MODE environment variable, defaulting to 0o600. It includes corresponding documentation updates and unit tests. The review feedback correctly identifies that parsing octal values with int(raw, 8) will fail and fallback to the default if standard Python octal prefixes like 0o are used. It is recommended to strip these prefixes before parsing and add a test case to verify this behavior.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| raw = environ.get(ENV_FILE_MODE_ENV, "").strip() | ||
| if not raw: | ||
| return DEFAULT_ENV_FILE_MODE | ||
| try: | ||
| mode = int(raw, 8) |
There was a problem hiding this comment.
The int(raw, 8) call will raise a ValueError if the user specifies the octal mode with a standard Python octal prefix like 0o or 0O (e.g., 0o640). This causes it to silently fall back to the default 0o600 mode, which defeats the user's override. Stripping the 0o or 0O prefix if present makes the override more robust and user-friendly.
| raw = environ.get(ENV_FILE_MODE_ENV, "").strip() | |
| if not raw: | |
| return DEFAULT_ENV_FILE_MODE | |
| try: | |
| mode = int(raw, 8) | |
| raw = environ.get(ENV_FILE_MODE_ENV, "").strip() | |
| if not raw: | |
| return DEFAULT_ENV_FILE_MODE | |
| if raw.lower().startswith("0o"): | |
| raw = raw[2:] | |
| try: | |
| mode = int(raw, 8) |
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "640"}), 0o640) | ||
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "0640"}), 0o640) |
There was a problem hiding this comment.
Add a test case to verify that env_file_target_mode correctly handles the standard Python octal prefix 0o (e.g., 0o640).
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "640"}), 0o640) | |
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "0640"}), 0o640) | |
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "640"}), 0o640) | |
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "0640"}), 0o640) | |
| self.assertEqual(env_file_target_mode({"TCAPSULE_ENV_FILE_MODE": "0o640"}), 0o640) |
What
Writes
.envwith owner-only (0600) permissions by default, but lets power users override the mode via aTCAPSULE_ENV_FILE_MODEenv var (octal, e.g.640).Why
This reworks the closed #252. Your feedback there was that unconditionally forcing
0600breaks legitimate workflows — a Docker container or multi-user host where another user needs access to.env. This keeps0600for the vast majority of cases (the file holdsTC_PASSWORD) while giving power users a clean way to choose a different mode, instead of forcing it or leaving it to chance.Changes
env_file_target_mode(env)readsTCAPSULE_ENV_FILE_MODEas an octal mode; unset/invalid/out-of-range falls back to0600.write_env_fileapplies that mode to the temp file before the atomic rename (soos.replacedelivers it, with no post-renamechmod).Tests
env_file_target_mode: default,640/0640overrides, invalid and out-of-range fallback.write_env_fileyields0600by default and0640when the override is set.I dropped the doctor "loose permissions" warning from the original #252 — with the mode now intentionally configurable, a warning would just nag the power users this is meant to serve.
🤖 Generated with Claude Code