Inject secrets from 1Password into your app at runtime using dotenvx encrypted env files.
No secrets ever touch your shell history, CI environment variables, or unencrypted .env files.
| Package | Version | Description |
|---|---|---|
envlock-next |
Next.js plugin and envlock CLI |
|
envlock-core |
Framework-agnostic CLI and shared utilities |
Use envlock-next for Next.js projects. Use envlock-core directly for any other Node.js project.
envlock injects secrets in two phases so they never appear in shell history, CI environment variables, or unencrypted files:
op runphase — envlock re-invokes itself wrapped insideop run --environment <id>. The 1Password CLI resolves your secrets and injectsDOTENV_PRIVATE_KEY_<ENV>(e.g.DOTENV_PRIVATE_KEY_PRODUCTION) into the child process environment, then hands control back.dotenvxphase — the re-invoked process detectsDOTENV_PRIVATE_KEY_<ENV>already set, skipsop run, and calls thedotenvxJS API in-process to decrypt the encrypted.env.*file. The target command is then spawned with secrets in its environment.
In CI, set DOTENV_PRIVATE_KEY_<ENV> directly (e.g. a GitHub Actions secret). envlock detects it and skips the op run phase entirely.
npm install envlock-nextUpdate your package.json scripts to use the envlock CLI:
{ "dev": "envlock dev", "build": "envlock build", "start": "envlock start" }Add withEnvlock to your next.config.js (or next.config.ts):
import { withEnvlock } from "envlock-next";
export default withEnvlock(nextConfig, {
onePasswordEnvId: "your-1password-env-id",
});Or set the ENVLOCK_OP_ENV_ID environment variable instead in a root level .env.
envlock dev # next dev with .env.development secrets
envlock build # next build with .env.production secrets
envlock start # next start with .env.production secrets
envlock run <cmd> # run any command with secrets injectedEnvironment flags:
envlock dev --staging # use .env.staging
envlock build --production # use .env.production (default for build)Auto port switching:
If the default port (3000) is in use, envlock dev automatically finds the next free port and logs a notice:
[envlock] Warning: Port 3000 in use, switching to 3001
Debug output:
envlock dev --debugnpm install envlock-core# Run any command with secrets injected
envlock node server.js
envlock python app.py --port 4000
# Named commands from envlock.config.ts
envlock dev
envlock start --production
# Explicit run subcommand (bypasses config)
envlock run node migrate.jsConfig file (envlock.config.ts):
export default {
onePasswordEnvId: "your-1password-env-id",
commands: {
dev: "node server.js --watch",
start: "node server.js",
},
};import { findFreePort, runWithSecrets, log } from "envlock-core";
// Find a free port starting from preferred
const port = await findFreePort(3000); // returns 3000, or 3001 if taken, etc.
// Run a command with secrets injected
runWithSecrets({
envFile: ".env.development",
environment: "development",
onePasswordEnvId: "your-env-id",
command: "node",
args: ["server.js"],
});Prerequisites: Node 18+, pnpm 10+
# Install dependencies
pnpm install
# Build all packages (core first, then next)
pnpm build
# Run all tests
pnpm testPackages are in packages/. Each has its own tsup build and vitest test suite.
Note for contributors:
envlock-nextbundlesenvlock-coreinto its CLI binary vianoExternal. Always build core before next (pnpm buildhandles this with--workspace-concurrency=1).
MIT — Benjamin Davies