npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-env-resolver-dotenvx

v2.0.0

Published

Dotenvx and OS Keychain resolvers for node-env-resolver (encrypted .env files, keychain:// secret references)

Readme

node-env-resolver-dotenvx

Dotenvx and OS Keychain integration for node-env-resolver. Load encrypted .env files and resolve secrets from OS credential stores.

npm version

Install

npm install node-env-resolver-dotenvx

@dotenvx/dotenvx is included as a package dependency.

Why

Local .env files are plaintext — any malicious dependency, editor extension, or AI tool that can read your workspace gets your secrets. This package helps by:

  • Encrypting secrets at rest (dotenvx-encrypted .env files)
  • Supporting decryption keys from code/env/.env.keys for dotenvx files
  • Resolving keychain:// references from macOS Keychain, Linux secret-tool, or Windows Credential Manager

This follows the pattern from the dotenvx keychain blog post.

Quick start

One-line convenience functions

import { resolveDotenvx } from 'node-env-resolver-dotenvx';

const config = await resolveDotenvx(
  { path: '.env.local.secrets' },
  { DATABASE_URL: postgres(), API_KEY: string() },
);

With resolveAsync() (combining sources)

import { resolveAsync } from 'node-env-resolver';
import { processEnv } from 'node-env-resolver/resolvers';
import { dotenvx } from 'node-env-resolver-dotenvx';

const config = await resolveAsync({
  resolvers: [
    [processEnv(), { PORT: 3000 }],
    [dotenvx('.env.local.secrets'), { DATABASE_URL: postgres() }],
  ],
});

The keychain pattern (plain config + encrypted secrets)

import { resolveAsync } from 'node-env-resolver';
import { processEnv } from 'node-env-resolver/resolvers';
import { dotenvxKeychain } from 'node-env-resolver-dotenvx';
import { createKeychainHandler } from 'node-env-resolver-dotenvx/handlers';

const config = await resolveAsync({
  resolvers: [
    [processEnv(), { PORT: 3000 }],
    [dotenvxKeychain(), { DATABASE_URL: postgres() }],
  ],
  references: {
    handlers: { keychain: createKeychainHandler() },
  },
});

This loads:

  1. .env.local (plain config, feature flags, non-secrets)
  2. .env.local.secrets (encrypted, decrypted via dotenvx with key from privateKey, env, or .env.keys)

Any schema value set to keychain://<name> resolves from your OS credential store.

Note: createKeychainHandler() resolves keychain:// references in config values. It does not automatically fetch and inject the dotenvx decryption key for dotenvx()/dotenvxKeychain().

Features

  • Load encrypted .env files via @dotenvx/dotenvx
  • dotenvxKeychain() resolver for the plain + encrypted pattern
  • keychain:// reference handler (macOS Keychain, Linux secret-tool, Windows Credential Manager)
  • One-line convenience functions (resolveDotenvx, safeResolveDotenvx)
  • Full TypeScript support
  • Sync and async loading

Resolvers

dotenvx(options?)

Load an encrypted .env file and decrypt it.

import { dotenvx } from 'node-env-resolver-dotenvx';

// Default path
const resolver = dotenvx();

// Custom path
const resolver = dotenvx('.env.local.secrets');

// With options
const resolver = dotenvx({
  path: '.env.local.secrets',
  overload: true,            // Override existing env vars (default: false)
  privateKey: 'abc123...',   // Decryption key (or set DOTENV_PRIVATE_KEY env var)
  envKeysFile: '../../.env.keys', // Custom keys file path
});

The decryption key can come from:

  1. privateKey option
  2. DOTENV_PRIVATE_KEY (or DOTENV_PRIVATE_KEY_*) environment variable
  3. .env.keys file (default or custom path via envKeysFile)

dotenvxKeychain(options?)

Load plain .env.local then encrypted .env.local.secrets. Secrets override plain values by default.

import { dotenvxKeychain } from 'node-env-resolver-dotenvx';

// Default: .env.local + .env.local.secrets
const resolver = dotenvxKeychain();

// Custom paths
const resolver = dotenvxKeychain({
  plainPath: '.env',
  secretsPath: '.env.encrypted',
  overload: true,
  privateKey: 'abc123...',
});

Keychain Reference Handler

Use keychain://<name> in your .env files to resolve values from OS credential stores:

# .env
DATABASE_URL=keychain://prod/database-url
API_KEY=keychain://prod/api-key
import { resolveAsync } from 'node-env-resolver';
import { processEnv } from 'node-env-resolver/resolvers';
import { createKeychainHandler } from 'node-env-resolver-dotenvx/handlers';

const config = await resolveAsync({
  resolvers: [[processEnv(), schema]],
  references: {
    handlers: { keychain: createKeychainHandler() },
  },
});

Platform support

| Platform | Tool | Command | |----------|------|---------| | macOS | Keychain Access | security find-generic-password | | Linux | libsecret | secret-tool lookup | | Windows | Credential Manager | Get-StoredCredential (PowerShell) |

Storing secrets

macOS:

security add-generic-password -a "prod/database-url" -s "node-env-resolver" -w

Linux:

secret-tool store --label="node-env-resolver" service "node-env-resolver" account "prod/database-url"

Windows (PowerShell):

cmdkey /generic:"node-env-resolver\prod\database-url" /user:"prod/database-url" /pass

Handler options

const handler = createKeychainHandler({
  servicePrefix: 'myapp',          // Keychain service name (default: 'node-env-resolver')
  platform: 'macos',               // Force platform: 'macos' | 'linux' | 'windows' | 'auto'
});

Pre-configured handler

import { keychainHandler } from 'node-env-resolver-dotenvx/handlers';

// Auto-detects platform
const config = await resolveAsync({
  resolvers: [[processEnv(), schema]],
  references: {
    handlers: { keychain: keychainHandler },
  },
});

Convenience functions

resolveDotenvx(dotenvxOptions, schema, resolveOptions?)

import { resolveDotenvx } from 'node-env-resolver-dotenvx';

const config = await resolveDotenvx(
  { path: '.env.local.secrets' },
  { DATABASE_URL: postgres(), API_KEY: string() },
);

safeResolveDotenvx(dotenvxOptions, schema, resolveOptions?)

Non-throwing version:

import { safeResolveDotenvx } from 'node-env-resolver-dotenvx';

const result = await safeResolveDotenvx(
  { path: '.env.local.secrets' },
  { DATABASE_URL: string() },
);

if (result.success) {
  console.log(result.data.DATABASE_URL);
} else {
  console.error(result.error);
}

Encryption workflow

# 1. Create your secrets file
cat > .env.local.secrets <<EOF
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret-key-123
EOF

# 2. Encrypt it with dotenvx
npx dotenvx encrypt -f .env.local.secrets

# 3. Store the decryption key in your OS keychain (macOS)
# The key is in .env.keys after encryption
security add-generic-password -a "LOCAL_SECRETS_DOTENVX_KEY" -s "node-env-resolver" -w

# 4. Delete the plaintext key file (keep a backup!)
rm .env.keys

# 5. Add to .gitignore
echo ".env.local\n.env.local.secrets\n.env.keys" >> .gitignore

Then at app startup (manual key lookup + dotenvx):

import { resolveDotenvx } from 'node-env-resolver-dotenvx';
import { createKeychainHandler } from 'node-env-resolver-dotenvx/handlers';
import { postgres } from 'node-env-resolver/validators';

const keychain = createKeychainHandler();
const keyRef = keychain.resolveSync('keychain://LOCAL_SECRETS_DOTENVX_KEY');

const config = await resolveDotenvx(
  { path: '.env.local.secrets', privateKey: keyRef.value },
  { DATABASE_URL: postgres() },
);

Configuration

dotenvx options

interface DotenvxOptions {
  path?: string;        // Path to encrypted .env file (default: '.env')
  overload?: boolean;   // Override existing env vars (default: false)
  privateKey?: string;  // Decryption key (or set DOTENV_PRIVATE_KEY env var)
  envKeysFile?: string; // Custom path to .env.keys file
}

dotenvxKeychain options

interface DotenvxKeychainOptions {
  plainPath?: string;    // Path to plain env file (default: '.env.local')
  secretsPath?: string;  // Path to encrypted secrets (default: '.env.local.secrets')
  overload?: boolean;    // Secrets override plain values (default: true)
  privateKey?: string;  // Decryption key
  envKeysFile?: string;  // Custom path to .env.keys file
}

KeychainHandler options

interface KeychainHandlerOptions {
  servicePrefix?: string;  // Keychain service name (default: 'node-env-resolver')
  platform?: 'macos' | 'linux' | 'windows' | 'auto'; // Platform (default: 'auto')
}

Troubleshooting

dotenvx: failed to load

  • Ensure .env.keys is present or DOTENV_PRIVATE_KEY env var is set
  • Check the file path is correct

Failed to read from macOS Keychain

  • Run: security add-generic-password -a "<account>" -s "node-env-resolver" -w
  • Verify: security find-generic-password -a "<account>" -s "node-env-resolver" -w

Failed to read from Linux keyring

  • Install libsecret: sudo apt install libsecret-1-0
  • Install secret-tool: sudo apt install libsecret-tools
  • Store: secret-tool store --label="node-env-resolver" service "node-env-resolver" account "<key-name>"

Invalid keychain reference

  • Format must be keychain://<key-name> (e.g., keychain://prod/database-url)

License

MIT