<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>dotsecenv | Blog</title><description>Secure secrets management CLI that encrypts environment variables at rest using GPG, making them safe to commit to version control.</description><link>https://dotsecenv.com/</link><language>en</language><item><title>Why your .env secrets shouldn&apos;t be plaintext on disk</title><link>https://dotsecenv.com/blog/why-dotsecenv/</link><guid isPermaLink="true">https://dotsecenv.com/blog/why-dotsecenv/</guid><description>The Trivy supply chain attack swept 50+ filesystem locations for plaintext credentials. It&apos;s time to encrypt .env secrets at rest.

</description><pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;On March 19, 2026, a threat actor known as &lt;a href=&quot;https://arcticwolf.com/resources/blog/teampcp-supply-chain-attack-campaign-targets-trivy-checkmarx-kics-and-litellm-potential-downstream-impact-to-additional-projects/&quot;&gt;TeamPCP&lt;/a&gt; compromised Aqua Security’s Trivy vulnerability scanner in a supply chain attack (&lt;a href=&quot;https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23&quot;&gt;CVE-2026-33634&lt;/a&gt;). The malicious code swept &lt;strong&gt;50+ filesystem locations&lt;/strong&gt; on CI/CD runners, harvesting SSH keys, AWS/GCP/Azure credentials, &lt;code dir=&quot;auto&quot;&gt;.env&lt;/code&gt; files, database passwords, Docker configs, and Kubernetes tokens. It then &lt;a href=&quot;https://www.microsoft.com/en-us/security/blog/2026/03/24/detecting-investigating-defending-against-trivy-supply-chain-compromise/&quot;&gt;dumped &lt;code dir=&quot;auto&quot;&gt;Runner.Worker&lt;/code&gt; process memory&lt;/a&gt; to extract additional secrets.&lt;/p&gt;
&lt;p&gt;The damage didn’t stop there. Credentials stolen from CI/CD pipelines cascaded downstream — within days, TeamPCP used harvested PyPI tokens to &lt;a href=&quot;https://www.bleepingcomputer.com/news/security/popular-litellm-pypi-package-compromised-in-teampcp-supply-chain-attack/&quot;&gt;backdoor LiteLLM&lt;/a&gt;, an AI gateway library with &lt;a href=&quot;https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/&quot;&gt;~3.4 million daily downloads&lt;/a&gt;. The compromised versions deployed a &lt;a href=&quot;https://www.kaspersky.com/blog/critical-supply-chain-attack-trivy-litellm-checkmarx-teampcp/55510/&quot;&gt;three-stage payload&lt;/a&gt;: credential harvesting, Kubernetes lateral movement, and a persistent backdoor for remote code execution. Other projects were also targeted in the same campaign.&lt;/p&gt;
&lt;p&gt;This wasn’t an isolated pattern. In August 2024, Palo Alto’s Unit 42 documented an &lt;a href=&quot;https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/&quot;&gt;extortion campaign&lt;/a&gt; that found exposed &lt;code dir=&quot;auto&quot;&gt;.env&lt;/code&gt; files on 110,000+ domains and stole 90,000+ environment variables. GitHub’s own data shows &lt;a href=&quot;https://resources.github.com/enterprise/understanding-secret-leak-exposure/&quot;&gt;39 million secrets&lt;/a&gt; were committed to public repositories in 2024 alone.&lt;/p&gt;
&lt;p&gt;The pattern is consistent: &lt;strong&gt;plaintext secrets on disk are a high-value, low-effort target.&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-gap-in-existing-tools&quot;&gt;The gap in existing tools&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Several tools address parts of this problem, and they’re all good at what they do:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://getsops.io/&quot;&gt;SOPS&lt;/a&gt;&lt;/strong&gt; encrypts config files in-place and supports cloud KMS backends (AWS, GCP, Azure). It’s excellent for production config management. But it has no shell integration for developer workflows, and multi-user key sharing requires manual &lt;code dir=&quot;auto&quot;&gt;.sops.yaml&lt;/code&gt; configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://direnv.net/&quot;&gt;direnv&lt;/a&gt;&lt;/strong&gt; provides the shell integration developers want - environment variables load automatically when you &lt;code dir=&quot;auto&quot;&gt;cd&lt;/code&gt; into a project. But it provides zero encryption. Your secrets are still plaintext in &lt;code dir=&quot;auto&quot;&gt;.envrc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.vaultproject.io/&quot;&gt;HashiCorp Vault&lt;/a&gt;, &lt;a href=&quot;https://www.doppler.com/&quot;&gt;Doppler&lt;/a&gt;, &lt;a href=&quot;https://infisical.com/&quot;&gt;Infisical&lt;/a&gt;&lt;/strong&gt; are powerful platforms with dynamic secrets, rotation, and web dashboards. They’re the right choice for production. But they require server infrastructure or SaaS accounts, overhead that doesn’t make sense when a developer just needs to share an API key with a teammate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/AGWA/git-crypt&quot;&gt;git-crypt&lt;/a&gt;&lt;/strong&gt; encrypts files transparently in git, but operates at the file level with no per-secret granularity and no sharing workflow.&lt;/p&gt;
&lt;p&gt;The gap: no tool combined &lt;strong&gt;encryption at rest&lt;/strong&gt; + &lt;strong&gt;shell auto-loading&lt;/strong&gt; + &lt;strong&gt;simple multi-user sharing&lt;/strong&gt; without requiring a server or cloud account.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-dotsecenv-works&quot;&gt;How dotsecenv works&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/dotsecenv/dotsecenv&quot;&gt;dotsecenv&lt;/a&gt; is a Go CLI that encrypts environment secrets at rest using hybrid encryption: AES-256-GCM for data, GPG for key exchange. Secrets are stored in a JSONL vault file that’s safe to commit to git.
However, that is purely optional - you don’t actually have to commit the vault.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Store a secret (encrypted in the vault)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;my-api-key&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dotsecenv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;secret&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;store&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;API_KEY&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Retrieve it (decrypted on-demand via GPG)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;dotsecenv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;secret&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;API_KEY&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Share with a teammate (their GPG public key)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;dotsecenv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;secret&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;share&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;API_KEY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;teammate@company.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A shell plugin for zsh, bash, and fish decrypts and loads secrets when you enter a directory - like direnv, but your secrets stay encrypted on disk:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# .secenv file (safe to commit)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;API_KEY&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;dotsecenv}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;dotsecenv}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# cd into the project directory, secrets load automatically&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;my-project/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$API_KEY&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# decrypted on-demand&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;why-gpg&quot;&gt;Why GPG?&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;GPG over &lt;a href=&quot;https://github.com/FiloSottile/age&quot;&gt;age&lt;/a&gt; was a tradeoff - age is simpler, but I needed signatures for tamper detection and audit trails. Signatures enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Verifying who created each secret&lt;/strong&gt;: every vault entry is signed by the originator’s key&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tamper detection&lt;/strong&gt;: &lt;code dir=&quot;auto&quot;&gt;dotsecenv validate&lt;/code&gt; checks cryptographic integrity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-repudiation&lt;/strong&gt;: audit trails with provable authorship&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus most developers already have a GPG key from &lt;a href=&quot;https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits&quot;&gt;GitHub&lt;/a&gt; or &lt;a href=&quot;https://docs.gitlab.com/user/project/repository/signed_commits/gpg/&quot;&gt;GitLab&lt;/a&gt; commit signing. dotsecenv builds on that existing infrastructure.
If, however, you don’t have a GPG key, you can generate one with &lt;a href=&quot;https://dotsecenv.com/reference/#identity-create&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;dotsecenv identity create&lt;/code&gt;&lt;/a&gt;, without needing to get into the GPG nitty-gritty!&lt;/p&gt;
&lt;p&gt;Algorithm defaults align with &lt;a href=&quot;https://dotsecenv.com/concepts/compliance/&quot;&gt;FIPS 186-5&lt;/a&gt; (Ed25519, ECDSA P-384, RSA-3072+), and the tool refuses to operate with weaker algorithms.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-dotsecenv-does-not-protect-against&quot;&gt;What dotsecenv does NOT protect against&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I want to be upfront about the boundaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Process memory&lt;/strong&gt;: once secrets are loaded as environment variables, they’re readable via &lt;code dir=&quot;auto&quot;&gt;/proc&lt;/code&gt; or memory dumps. The Trivy attack also dumped &lt;code dir=&quot;auto&quot;&gt;Runner.Worker&lt;/code&gt; process memory; encryption at rest doesn’t help there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compromised GPG private key&lt;/strong&gt;: if your private key is stolen and unprotected, your secrets are exposed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Root/admin access&lt;/strong&gt;: a privileged attacker can read anything.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Production runtime&lt;/strong&gt;: dotsecenv targets the development lifecycle (SDLC), not production secret injection. For production, use Vault, AWS Secrets Manager, or similar.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;dotsecenv is &lt;strong&gt;defense-in-depth&lt;/strong&gt; - it eliminates the plaintext-on-disk attack surface. If the Trivy attacker’s disk sweep had found GPG ciphertext instead of plaintext &lt;code dir=&quot;auto&quot;&gt;.env&lt;/code&gt; files, that component of the attack would have yielded nothing usable, or at the very least made the exfiltration much harder to effect. The LiteLLM cascade — where a single set of stolen plaintext credentials led to the compromise of a library with millions of daily downloads — illustrates exactly why encryption at rest matters at every stage of the supply chain.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;current-state&quot;&gt;Current state&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;dotsecenv is functional and I use it daily. It has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comprehensive &lt;a href=&quot;https://github.com/dotsecenv/dotsecenv/actions&quot;&gt;E2E tests&lt;/a&gt;, including &lt;a href=&quot;https://dotsecenv.com/concepts/security-model/#hermetic-testing&quot;&gt;hermetic tests&lt;/a&gt; that run with network disabled to prove that all secret handling happens locally&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dotsecenv/dotsecenv#security-features&quot;&gt;SLSA Build Level 3&lt;/a&gt; release provenance&lt;/li&gt;
&lt;li&gt;Packages for macOS (Homebrew), Debian/Ubuntu, Fedora/RHEL, and Arch Linux&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://dotsecenv.com/guides/github-action/&quot;&gt;GitHub Action&lt;/a&gt; for CI/CD integration&lt;/li&gt;
&lt;li&gt;A &lt;a href=&quot;https://dotsecenv.com/guides/terraform-credentials-helper/&quot;&gt;Terraform credentials helper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are rough edges. Windows support is WIP. The vault format is v2 and may evolve; I will generally aim to provide migration tooling for any breaking changes, but it may not always be viable. I’m looking for feedback from developers who deal with secrets in their daily workflow; if you try it out and have feedback, please &lt;a href=&quot;https://github.com/dotsecenv/dotsecenv/issues/new&quot;&gt;open an issue on GitHub&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Thank you! 🙏&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;further-reading-on-the-trivyteampcp-campaign&quot;&gt;Further reading on the Trivy/TeamPCP campaign&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/&quot;&gt;Aqua Security — Trivy Supply Chain Attack: What You Need to Know&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/security/blog/2026/03/24/detecting-investigating-defending-against-trivy-supply-chain-compromise/&quot;&gt;Microsoft Security — Detecting, Investigating, and Defending Against the Trivy Compromise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.crowdstrike.com/en-us/blog/from-scanner-to-stealer-inside-the-trivy-action-supply-chain-compromise/&quot;&gt;CrowdStrike — From Scanner to Stealer: Inside the trivy-action Compromise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reversinglabs.com/blog/teampcp-supply-chain-attack-spreads&quot;&gt;ReversingLabs — Inside the TeamPCP Cascading Supply Chain Attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/&quot;&gt;Datadog Security Labs — LiteLLM Compromised on PyPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.paloaltonetworks.com/blog/cloud-security/trivy-supply-chain-attack/&quot;&gt;Palo Alto Networks — When Security Scanners Become the Weapon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Next steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source code&lt;/strong&gt;: &lt;a href=&quot;https://github.com/dotsecenv/dotsecenv&quot;&gt;github.com/dotsecenv/dotsecenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;: &lt;a href=&quot;https://dotsecenv.com/getting-started/&quot;&gt;dotsecenv.com/getting-started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comparison&lt;/strong&gt;: &lt;a href=&quot;https://dotsecenv.com/concepts/comparison/&quot;&gt;dotsecenv.com/concepts/comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href=&quot;https://dotsecenv.com&quot;&gt;dotsecenv.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>security</category><category>encryption</category><category>launch</category></item></channel></rss>