Concept
Secret Injection: How It Works
Secret injection is the process of delivering credentials to your application at the moment it needs them — without storing those credentials in source code, configuration files, or build artifacts. The goal is simple: your application gets the secrets it needs, and nothing else on the system can access them.
What Is Secret Injection?
Every application needs credentials to connect to databases, call APIs, sign tokens, and decrypt data. The question is how those credentials get from wherever they are stored into the running process.
Secret injection is the act of populating your application's environment with these values at startup time. Instead of reading credentials from a file on disk, the application receives them through its process environment — the same process.env interface your code already uses, but populated from a secure source rather than a plaintext file.
// Whether secrets come from a .env file or runtime injection,
// your code reads them the same way
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.STRIPE_SECRET_KEY;
const jwtSecret = process.env.JWT_SECRET;The difference is in how those values got into process.env. A .env file requires plaintext on disk. Runtime injection does not.
Methods of Secret Injection
There are four common approaches to getting secrets into a running application. Each has different security properties and operational tradeoffs:
| Method | How It Works | Tradeoffs |
|---|---|---|
| Environment variables (.env file) | A plaintext file is read at startup by a library like dotenv. Values are parsed and set on process.env. | Simple to set up. Insecure — plaintext on disk, easy to commit, no access control. |
| File mounting | Secrets are written to a file (often in a tmpfs or volume mount) and the application reads the file at startup. | Used by Kubernetes Secrets and Docker Swarm. The file still exists on the file system, even if it’s in memory-backed storage. |
| Sidecar / init container | A separate process fetches secrets from a vault and either writes them to a shared volume or exposes them via a local API. | Common in Kubernetes with Vault Agent. Adds infrastructure complexity. The sidecar itself needs credentials to authenticate. |
| Runtime CLI injection | A CLI tool fetches secrets from encrypted storage and spawns your application as a child process with secrets set in the environment. | No file on disk. No sidecar. Secrets exist only in the child process’s memory. Requires the CLI to be installed. |
The Problem with File-Based Approaches
Whether it's a .env file, a mounted Kubernetes Secret, or a file written by a sidecar — any approach that puts secrets in a file shares the same fundamental weaknesses:
- 1.Secrets persist on disk. Even after your application reads them, the file remains. Any process with file system access can read it — including malicious dependencies, debugging tools, and AI coding agents that index your project directory.
- 2.Stale files accumulate. Old
.env.backup,.env.production, and.env.oldfiles pile up over time. Each one is an unencrypted copy of your credentials that may not match your gitignore rules. - 3.Permission management is coarse. File permissions operate at the user/group/other level. You cannot restrict access to individual secrets within a file, and you cannot audit which process read which value.
- 4.Rotation requires file updates. When a secret changes, every file that contains it must be updated. In a team environment, this means notifying every developer to pull a new file or restart their sidecar.
Zero-Disk Injection with secr
secr run takes a different approach. Instead of writing secrets to a file, it fetches them from encrypted storage, sets them as environment variables, and spawns your application as a child process. The secrets exist only in the child process's memory — never on the file system.
Here is what happens when you run secr run -- npm start:
- The CLI reads your
.secr.jsonconfig to determine the project, organization, and environment. - It authenticates with the secr API using your stored session token.
- It fetches the encrypted secrets for the target environment.
- Secrets are decrypted in memory on the client side.
- The CLI spawns
npm startas a child process with the decrypted values set as environment variables. - Your application reads
process.envas usual. No file is written. No dotenv library is needed.
# That's it. One command replaces your entire .env workflow.
secr run -- npm start
# Loaded 18 secrets → server running on :3000Works with Every Language and Framework
Because secr run injects secrets as standard environment variables, it works with any language, runtime, or framework that reads from the process environment. No SDK or library integration required.
Node.js
# Express, Fastify, Next.js, Hono — any Node.js application
secr run -- node server.js
secr run -- npm start
secr run -- next dev
secr run -- tsx watch src/index.tsPython
# Django, Flask, FastAPI — any Python application
secr run -- python app.py
secr run -- uvicorn main:app --reload
secr run -- gunicorn wsgi:application
secr run -- python manage.py runserverGo
# Any Go application that reads os.Getenv()
secr run -- go run main.go
secr run -- ./myapp
secr run -- air # live reloadRuby, PHP, Rust, and everything else
# Ruby / Rails
secr run -- rails server
secr run -- bundle exec puma
# PHP / Laravel
secr run -- php artisan serve
# Rust
secr run -- cargo run
# Any command that reads environment variables
secr run -- your-command-hereYou can also specify the environment explicitly:
# Development (default)
secr run -- npm start
# Staging
secr run --env staging -- npm test
# Production
secr run --env production -- node dist/server.jsInjection in CI/CD
The same injection pattern works in continuous integration and deployment pipelines. secr provides purpose-built integrations for the most common platforms:
GitHub Actions
The secr composite action pulls secrets and exports them as environment variables for the duration of the job. Values are automatically masked in logs.
Vercel
The @secr/vercel package runs before next build and writes secrets to .env.local where the framework expects them. No manual env var configuration in the Vercel dashboard.
Netlify
The @secr/netlify-plugin injects secrets into process.env during the onPreBuild phase. Your build step receives all the credentials it needs.
Docker
Use secr run as your Docker CMD or ENTRYPOINT to inject secrets at container startup. Never bake credentials into an image layer.
steps:
- uses: actions/checkout@v4
- name: Inject secrets
uses: secr-dev/secr-action@v1
with:
token: ${{ secrets.SECR_TOKEN }}
org: my-org
project: my-app
environment: production
- run: npm ci
- run: npm run build
- run: npm testStart injecting secrets securely
npm i -g @secr/cli
secr login
secr init
secr run -- npm start