{"slug": "your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk", "title": "Your coding agent can read your .env. Here's how I reduced the risk", "summary": "A developer documented a method for keeping secrets out of reach of coding agents by moving credential values from project .env files into 1Password and committing only op:// references in an op.env file, then launching apps via 'op run --env-file=op.env -- pnpm dev'. The approach limits casual discovery of secrets in readable files but, the developer cautions, does not protect against an agent that can run the op CLI, access the vault, or inspect a process that received the resolved values.", "body_md": "If a coding agent can read your project files, assume it can read your `.env` too.\n\nIgnore rules and editor settings can help, but they are not a security boundary. I moved secret values out of my project files and into 1Password, then used the CLI to provide them to the processes that need them.\n\nThat reduces one easy way for an agent to discover secrets. It does **not** make secrets safe from code the agent writes or commands it runs. I’ll cover that part too.\n\nBefore, my project had a file containing actual values:\n\n```\n# .env\nSTRIPE_API_KEY=replace-with-your-key\nSENTRY_TOKEN=replace-with-your-token\nAPI_URL=http://localhost:8787\n```\n\nNow, the shared secrets live in 1Password. The project contains references to them instead:\n\n```\n# op.env\nSTRIPE_API_KEY=op://My-Project/api-env/STRIPE_API_KEY\nSENTRY_TOKEN=op://My-Project/api-env/SENTRY_TOKEN\n```\n\nThen I start the app with:\n\n```\nop run --env-file=op.env -- pnpm dev\n```\n\n`op run` resolves the references and provides the values to the process it starts. It does not, by itself, write those resolved values to a file.\n\nAn agent that reads `op.env` sees references, not secret values. But if it can run `op`, access the vault, or inspect a process that received the secrets, that protection may not be enough.\n\nNot every environment variable is a secret. Go through your existing files one value at a time:\n\n`op.env`.`.env.local`.\nFor example:\n\n| File | Contents | Git | \n|---|---|---|\n| `.env.local` | Non-sensitive, machine-specific settings | Ignored | \n| `op.env` | References to shared secrets | Committed | \n\nMake sure `.env.local` is actually ignored. Add it to `.gitignore` if it isn’t already:\n\n```\n.env.local\n```\n\nThe `op://` references reveal metadata such as vault, item, and field names, so use names you’re comfortable exposing in the repository. Don’t point development references at a vault containing production credentials.\n\nThe desktop app and CLI are separate installations. Follow [1Password’s CLI installation guide](https://developer.1password.com/docs/cli/get-started/) for your platform.\n\n```\n# macOS\nbrew install --cask 1password-cli\n# Windows\nwinget install AgileBits.1Password.CLI\n```\n\nIn the 1Password desktop app, enable **Settings → Developer → Integrate with 1Password CLI**. Then open a new terminal and try:\n\n```\nop vault list\n```\n\nAuthenticate when prompted, and check that you can see the vault you intend to use. Keep the development vault scoped to development credentials.\n\n**Do this yourself, in a terminal or the 1Password app—not through the coding agent.** If the agent reads the old file while you migrate it, the secret has already entered the environment you’re trying to protect.\n\nCreate an item in 1Password and add each secret as a concealed field. Avoid passing secret values as command-line arguments: depending on the system and tooling, arguments can be exposed through process listings or monitoring.\n\nThen create `op.env` with one reference per secret. For example:\n\n```\nSTRIPE_API_KEY=op://My-Project/api-env/STRIPE_API_KEY\nSENTRY_TOKEN=op://My-Project/api-env/SENTRY_TOKEN\n```\n\nKeep the file format simple: one `NAME=op://...` reference per line. This check flags any non-comment, non-blank line that doesn’t match that format:\n\n```\ngrep -nEv '^(#|[A-Za-z_][A-Za-z0-9_]*=op://|$)' op.env\n```\n\nNo output means every line matches the expected shape. It does **not** prove the references point to the right fields, or that a secret wasn’t committed elsewhere.\n\nCheck the diff before committing, and rotate any credentials an agent may already have seen. Moving a credential into a vault does not undo an earlier exposure.\n\n`op run`\nFor most apps, start the process that needs the secrets through 1Password:\n\n```\nop run --env-file=op.env -- pnpm dev\n```\n\nThe secret values are now available to the app and its child processes. They are no longer just references—but they are still accessible to code running in that process.\n\nGive each service only the secrets it needs. If you run multiple services, use separate references or vault items rather than handing every service the same broad set of credentials.\n\nWrangler’s local variable-file behavior matters here. If `.dev.vars` exists, Wrangler may not use the process environment the way you expect. Wrangler 4.27 and later support `.env.local`; `CLOUDFLARE_INCLUDE_PROCESS_ENV=true` enables merging the process environment as well. Check the [Wrangler environment variable documentation](https://developers.cloudflare.com/workers/wrangler/system-environment-variables/) for the behavior in the version you use.\n\nIf you currently use `.dev.vars`:\n\n`.dev.vars`.\nBash or zsh:\n\n```\nif [ -f op.env ]; then\n  CLOUDFLARE_INCLUDE_PROCESS_ENV=true \\\n    op run --env-file=op.env -- wrangler dev\nelse\n  wrangler dev\nfi\n```\n\nPowerShell:\n\n``` php\nif (Test-Path op.env) {\n  $previous = $env:CLOUDFLARE_INCLUDE_PROCESS_ENV\n  try {\n    $env:CLOUDFLARE_INCLUDE_PROCESS_ENV = 'true'\n    op run --env-file=op.env -- wrangler dev\n  } finally {\n    $env:CLOUDFLARE_INCLUDE_PROCESS_ENV = $previous\n  }\n} else {\n  wrangler dev\n}\n```\n\nThe process-environment setting can expose more of the environment than you intended to the Worker. Set it only for the command that needs it, and test the behavior with your Wrangler version.\n\nAvoid using `op inject` to write resolved secrets into `.dev.vars` or another project file. That puts plaintext back on disk.\n\nFor CI, use a 1Password service account with read-only access to the specific vault it needs. Store its token as a GitHub Actions secret on a protected environment, not as a broadly available repository secret.\n\nThis example loads secrets as step outputs rather than exporting them into the whole job environment:\n\n```\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    environment: production\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Load secrets from 1Password\n        id: op\n        uses: 1password/load-secrets-action@v5\n        with:\n          export-env: false\n        env:\n          OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}\n          CLOUDFLARE_API_TOKEN: op://My-Project/ci/CLOUDFLARE_API_TOKEN\n          STRIPE_API_KEY: op://My-Project/api-env/STRIPE_API_KEY\n\n      - name: Deploy Worker\n        uses: cloudflare/wrangler-action@v4\n        env:\n          STRIPE_API_KEY: ${{ steps.op.outputs.STRIPE_API_KEY }}\n        with:\n          apiToken: ${{ steps.op.outputs.CLOUDFLARE_API_TOKEN }}\n          accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}\n          workingDirectory: workers/api\n          secrets: |\n            STRIPE_API_KEY\n```\n\nThe `secrets` input names the variables Wrangler should upload; the values come from the step’s environment. Keep the service-account token and loaded values away from steps that do not need them.\n\nBefore using this, check the actions’ current documentation. For stronger supply-chain protection, pin actions to reviewed commit SHAs rather than relying only on version tags. Never make secrets available to workflows running untrusted code, such as pull requests from forks.\n\nA vault keeps secret values out of files. It does not keep them out of processes.\n\nWhen `op run` starts your app, the app receives ordinary environment variables. Code in that process can read them. A malicious dependency or a change the agent made could send them over the network.\n\nOther processes running as your user may also be able to inspect process environments. For example, macOS has `ps eww <pid>` and Linux exposes process environment information under `/proc/<pid>/environ`, subject to system permissions and configuration.\n\nAnd if you start the agent from a shell that already has secrets exported, commands it runs may inherit those values.\n\nWhat helps:\n\nA command allowlist is not a complete defense. An allowed command such as `pnpm test` or `node` can execute code in the project.\n\nAgent settings can reduce accidental access, but they are not a replacement for keeping plaintext secrets out of the workspace.\n\nYou can add deny rules for secret files and the 1Password CLI in Claude Code’s settings. Check the current [Claude Code permissions documentation](https://docs.anthropic.com/en/docs/claude-code/settings) for the supported syntax and behavior.\n\nOrganizations on Copilot Business or Enterprise can configure [content exclusions](https://docs.github.com/en/copilot/how-tos/configure-content-exclusion/exclude-content-from-copilot). These can limit what Copilot accesses, but GitHub documents limitations for indirect access, such as a script reading an excluded file.\n\nVS Code also provides settings for edit approval, terminal approval, and agent sandboxing. These settings vary by version and agent mode, so verify them against the current [VS Code documentation](https://code.visualstudio.com/docs/copilot).\n\nA sandbox can restrict an agent’s commands, filesystem access, and network access. It does not necessarily protect a secret file that remains readable inside the workspace. And disabling inline suggestions for `.env` files is not the same as preventing an agent from reading them.\n\nKeep secret values out of files the agent can read. Use runtime injection to provide credentials only to the process that needs them, and assume that process can access those credentials.\n\nThe vault prompt is useful for protecting vault access. If you get an unexpected prompt, deny it. But a prompt cannot protect secrets already handed to a running process.\n\nThis setup reduces exposure; it does not eliminate it. Use narrow, disposable credentials, and review what the agent changed before you run it.\n\n*Commands and settings can change. Check the current 1Password CLI, Wrangler, GitHub Actions, and editor documentation before adopting them.*", "url": "https://wpnews.pro/news/your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk", "canonical_source": "https://dev.to/edwinluijten/your-coding-agent-can-read-your-env-heres-how-i-reduced-the-risk-d6e", "published_at": "2026-11-13 08:11:35+00:00", "updated_at": "2026-11-13 08:21:00.001211+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-safety"], "entities": ["1Password", "pnpm", "Stripe", "Sentry"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk", "markdown": "https://wpnews.pro/news/your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk.md", "text": "https://wpnews.pro/news/your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk.txt", "jsonld": "https://wpnews.pro/news/your-coding-agent-can-read-your-env-here-s-how-i-reduced-the-risk.jsonld"}}