
Tene
Your .env is not a secret. Tene hides it from AI agents.
4 followers
Your .env is not a secret. Tene hides it from AI agents.
4 followers
Every AI coding agent — Claude Code, Cursor, Windsurf — reads your entire project directory, includingenv files. Your API keys and database passwords are sent to AI models as plaintext. Tene is an open-source CLI that encrypts secrets locally and injects them as environment variables at runtime. AI agents can use your secrets without ever seeing them.




Quick journey log for anyone who’s in the middle of figuring out AI-safe secret management. Before I landed on what tene does now, I spent about a month cycling through approaches that all seemed reasonable until I actually tested them against Claude Code, Cursor, and Windsurf.
Pattern 1: Gitignored .env (the default)
The thing everyone does. .env.example committed, real .env ignored, keys pasted in for local dev.
Why it failed: AI coding agents read .env as project context on session start, regardless of .gitignore. I watched Claude Code confidently cite my real STRIPE_SECRET_KEY value in response to “what env vars does this project use?” — and then happily paste it into generated code. The gitignore protects GitHub. It does nothing to protect the model context window.
Pattern 2: Shell exports + sourced scripts
Moved everything to ~/.zshrc.d/project-secrets.sh with exports. Idea: the agent opens the project but the env vars are only in my shell, not in any project file.
Why it failed: Two separate leaks.
Agent spawns subshells for tool calls (npm, git, test runners). Those subshells inherit exports, and the agent reads stdout/stderr including env-dumping errors (“missing STRIPE_SECRET_KEY” messages that contain the first few chars from logs).
Shell history persistence. Typing “export STRIPE_KEY=sk_…” a single time leaves it in .zsh_history forever. The agent grep-searches history when debugging.
Pattern 3: dotenvx encrypted .env
Found dotenvx, thought this was it — encrypt the file at rest, decrypt only at runtime. .env.vault in the repo, private key in .env.keys.
Why it failed: The decryption happens in-process before the app starts. From the agent’s perspective, process.env.STRIPE_KEY still returns plaintext during the running session. If the agent can spawn the process (which it often does for debugging), it sees the plaintext env. Encryption at rest ≠ encryption at runtime, and the runtime is where the agent operates.
Solutions: What actually worked
The piece I was missing: secrets shouldn’t exist in any env var the agent can observe, unless the command doing the observing is specifically the one I want to run with that secret.
That collapses to a very narrow pattern:
Encrypted local vault (XChaCha20-Poly1305 + Argon2id)
No plaintext file anywhere on disk
Env vars injected only for the duration of an explicit “tene run – <cmd>” invocation
Process exits → env vars gone, vault stays sealed
When an agent is left running in the project without tene run, there are literally no env vars for it to discover. When I need to run tests with real keys, “tene run – npm test” works and cleans up after itself.
The meta-lesson
Every pattern before this one was trying to hide the plaintext somewhere the agent wouldn’t look. That’s a losing game against tools that aggressively index filesystem context.
The only approach that scales is to not have plaintext env material anywhere persistent, and only materialize it inside a specific process the agent hasn’t been invited into.
Happy to dig deeper into any of these failure modes if folks have hit something I missed. Discussion welcome — this squad is the place for it.
tene (MIT, Go single binary, no server): https://github.com/tomo-kay/tene
Project: https://tene.sh
MCP RCE: when an agent's tools become your attack surface
Summary
An MCP server is a local process spawned by your agent host. It runs as you, it sees what you see, and the protocol is a transport, not a sandbox.
Four leak channels are open by default: filesystem read, env vars, outbound network, and prompt context. The first two are the cheap-to-close ones.
"Trusted source" is not a permission model. Supply-chain compromise is real, and MCP servers sit on top of npm/PyPI dependency trees you have not audited.
Stop storing secrets in .env files. Stop exporting them in your shell rc. Inject them only into the specific child process that needs them, at runtime — tene run -- <cmd> is the one-liner version.
Hardened MCP is a stack: runtime secret injection, env zeroing per-server, container or chroot for high-blast-radius servers, egress firewall, pinned versions. Two layers cover most of the realistic risk; five layers are appropriate at the team level.