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

mysterio

v3.2.0

Published

Get secrets from AWS secret manager and merge them with ENV configs

Readme

Mysterio

Unified configuration management for Node.js applications

Mysterio combines your local config files and AWS Secrets Manager into a single, easy-to-use configuration object. No more juggling multiple configuration sources or worrying about where your settings live - everything is merged intelligently at runtime.

Upgrading from v2.x?

See the Migration Guide for detailed instructions on upgrading from v2.x to v3.0.

Why Mysterio?

🎯 Single Source of Truth

Stop managing configs and secrets separately. Mysterio merges them into one unified configuration object, giving you a single place to access all your application settings.

🚀 The Simple Use Case: Environment Configs + Secrets

The most common pattern is simple:

  1. Environment config - Your non-sensitive settings per environment (e.g., config/production.json)
  2. AWS Secrets - Your sensitive data (passwords, API keys, tokens)

Mysterio merges them at runtime into one config object. That's it!

🔀 Smart Merging System (Optional Layers)

For more complex scenarios, Mysterio supports a layered merging strategy:

default.json -> env.json -> secrets (AWS) -> .mysteriorc
 (optional)     (required)     (required)    (optional)

Core (required):

  • env.json - Environment-specific configs (production.json, dev.json, etc.)
  • secrets - Secure values from AWS Secrets Manager

Optional layers:

  • default.json - Base configs shared across all environments
  • .mysteriorc - Local developer overrides (gitignored)

Each layer overrides values from the previous layers, allowing you to:

  • Define base configs that work everywhere (optional)
  • Set environment-specific settings (required)
  • Add secure secrets from AWS Secrets Manager (required)
  • Use local overrides for development (optional)

Simple Example (env + secrets only):

// config/production.json
{
  "database": {
    "host": "prod-db.example.com",
    "port": 5432
  },
  "apiUrl": "https://api.production.com"
}

// AWS Secrets Manager (myapp/production)
{
  "database": {
    "password": "super-secret-password",
    "username": "dbuser"
  },
  "apiKey": "secret-api-key"
}

// Final merged result:
{
  "database": {
    "host": "prod-db.example.com",  // from production.json
    "port": 5432,                    // from production.json
    "username": "dbuser",            // from AWS Secrets
    "password": "super-secret-password"  // from AWS Secrets
  },
  "apiUrl": "https://api.production.com",  // from production.json
  "apiKey": "secret-api-key"  // from AWS Secrets
}

Advanced Example (with all optional layers):

// config/default.json (optional - base for all environments)
{
  "database": {
    "port": 5432
  },
  "apiUrl": "http://localhost:3000"
}

// config/production.json (required - environment specific)
{
  "database": {
    "host": "prod-db.example.com"
  },
  "apiUrl": "https://api.production.com"
}

// AWS Secrets Manager (required - secrets)
{
  "database": {
    "password": "super-secret-password"
  }
}

// .mysteriorc (optional - local overrides, gitignored)
{
  "database": {
    "host": "127.0.0.1"
  }
}

// Final merged result with all layers:
{
  "database": {
    "host": "127.0.0.1",      // from .mysteriorc (highest priority)
    "port": 5432,              // from default.json
    "password": "super-secret-password"  // from AWS Secrets
  },
  "apiUrl": "https://api.production.com"  // from production.json
}

⚡ Runtime Loading

Configs and secrets are loaded at runtime, meaning:

  • No build-time baking - Change configs without rebuilding
  • Environment-aware - Automatically picks the right config based on NODE_ENV
  • Dynamic secrets - Rotate AWS secrets without redeploying
  • Fresh on every start - Always get the latest configuration

🔒 Security First

  • Secrets stay in AWS Secrets Manager (never committed to git)
  • Local overrides (.mysteriorc) can be gitignored for developer-specific settings
  • Supports AWS IAM roles and secure credential management

Installation

npm install mysterio

🚀 Quick Start

The simplest setup only requires environment configs and AWS secrets.

1. Create your config directory (minimal setup)

your-project/
├── config/
│   ├── production.json   # Production settings (required)
│   ├── dev.json          # Dev environment (optional)
│   └── local.json        # Local development (optional)
└── package.json

Optional files:

  • config/default.json - Base configs for all environments
  • .mysteriorc - Local developer overrides (gitignore this!)

2. Create your environment config

config/production.json:

{
  "appName": "MyApp",
  "port": 8080,
  "database": {
    "host": "prod-db.example.com",
    "port": 5432
  },
  "apiUrl": "https://api.production.com"
}

config/local.json (for local development):

{
  "appName": "MyApp",
  "port": 3000,
  "database": {
    "host": "localhost",
    "port": 5432
  },
  "apiUrl": "http://localhost:3000"
}

3. Set up AWS Secrets Manager

Create a secret in AWS Secrets Manager with the name pattern: {package-name}/{environment}

For example, if your package.json has "name": "myapp" and you're running in production:

  • Secret name: myapp/production

✨ Option A: Using the Key/value Tab (Recommended)

AWS Secrets Manager doesn't support JSON validation in the plaintext editor, which can lead to invalid JSON being saved. Instead, it's much more convenient to use the Key/value tab when creating or editing secrets. Mysterio can automatically unflatten these key-value pairs into nested objects:

In AWS Console (Key/value tab):

database.password = super-secret-password
database.username = dbuser
apiKey = your-secret-api-key

In your code (with unflatten enabled):

const config = await mysterio.getMerged(['default', 'env', 'secrets', 'rc'], true)
// Secrets are automatically unflattened to:
// {
//   database: {
//     password: 'super-secret-password',
//     username: 'dbuser'
//   },
//   apiKey: 'your-secret-api-key'
// }

📝 Option B: Using JSON (Plaintext Tab)

You can still use traditional JSON format in the plaintext editor:

Secret value:

{
  "database": {
    "password": "super-secret-password",
    "username": "dbuser"
  },
  "apiKey": "your-secret-api-key"
}

4. Use in your application

import { Mysterio } from 'mysterio'

const mysterio = new Mysterio()
const config = await mysterio.getMerged()

console.log(config)
// {
//   appName: 'MyApp',
//   port: 8080,
//   database: {
//     host: 'prod-db.example.com',
//     port: 5432,
//     username: 'dbuser',
//     password: 'super-secret-password'
//   },
//   apiKey: 'your-secret-api-key'
// }

// Use your config
const db = await connectToDatabase(config.database)

API

new Mysterio(options)

Creates a new Mysterio instance.

Options:

  • configDirPath (string): Path to config directory. Default: ./config
  • localRcPath (string): Path to local RC file. Default: ./.mysteriorc
  • secretName (string): Custom AWS secret name. Default: {package-name}/{NODE_ENV || 'local'}
  • env (string): Override environment name instead of using NODE_ENV. Affects both config file selection and secret name generation
  • awsParams (object): AWS SDK parameters for Secrets Manager client
  • client (function): Custom client function for fetching secrets

Example:

const mysterio = new Mysterio({
  configDirPath: './my-configs',
  localRcPath: './.myapprc',
  secretName: 'custom-secret-name',
  env: 'staging',  // Use staging configs regardless of NODE_ENV
  awsParams: {
    region: 'us-west-2'
  }
})

mysterio.getMerged(mergingOrder, unflattenSecrets)

Returns the merged configuration object.

Parameters:

  • mergingOrder (array): Custom merge order. Default: ['default', 'env', 'secrets', 'rc']
  • unflattenSecrets (boolean): Whether to unflatten dotted secret keys into nested objects. Default: false

Returns: Promise - Merged configuration object

Example:

// Default order with flat secrets
const config = await mysterio.getMerged()

// With unflattened secrets (recommended for AWS Key/value tab)
const config = await mysterio.getMerged(['default', 'env', 'secrets', 'rc'], true)

// Custom order - secrets override everything
const config = await mysterio.getMerged(['default', 'env', 'rc', 'secrets'])

// Partial config - only default and env
const config = await mysterio.getMerged(['default', 'env'])

Individual Config Methods

mysterio.getDefaultConfig()

Returns the default config from config/default.json

mysterio.getEnvConfigs()

Returns environment-specific config based on NODE_ENV (e.g., config/production.json)

mysterio.getRcConfigs()

Returns local RC file config from .mysteriorc

mysterio.getSecrets(unflatten)

Returns secrets from AWS Secrets Manager

Parameters:

  • unflatten (boolean): Whether to unflatten dotted keys into nested objects. Default: false

Example:

// Get secrets as flat key-value pairs
const secrets = await mysterio.getSecrets(false)
// { 'database.password': 'secret', 'database.username': 'user' }

// Get secrets with dotted keys unflattened into nested objects
const secrets = await mysterio.getSecrets(true)
// { database: { password: 'secret', username: 'user' } }

Configuration Sources

Required Sources

Environment Config ({env}.json)

Environment-specific configuration based on NODE_ENV (or the env constructor option):

  • NODE_ENV=production -> config/production.json
  • NODE_ENV=development -> config/development.json
  • NODE_ENV=test -> config/test.json
  • Not set -> config/local.json
  • env: 'staging' in constructor -> config/staging.json (overrides NODE_ENV)

This is where your non-sensitive settings live (ports, hosts, feature flags, etc.)

Secrets (AWS Secrets Manager)

Secure secrets stored in AWS, automatically fetched based on:

  • Package name from package.json
  • Current environment (NODE_ENV or the env constructor option)
  • Pattern: {package-name}/{environment}

This is where your sensitive data lives (passwords, API keys, tokens, etc.)

Optional Sources

Default Config (default.json) - OPTIONAL

Base configuration that applies to all environments. Use this to avoid duplicating common settings across environment files.

If you don't need shared base configs, you can skip this file entirely.

RC File (.mysteriorc) - OPTIONAL

Local developer overrides (should be gitignored). Perfect for:

  • Database connections to local instances
  • API endpoints pointing to localhost
  • Debug flags
  • Developer-specific settings

Most projects don't need this - only use it if developers need local overrides.

Advanced Usage

🔓 Unflattening Secrets (Key/Value Tab Support)

AWS Secrets Manager's Key/value tab is more convenient than the plaintext JSON editor because it doesn't require JSON validation and is easier to edit. However, it stores secrets as flat key-value pairs. Mysterio can automatically unflatten these dotted keys into nested objects.

✨ Why use the Key/value tab?

  • ⚠️ AWS Secrets Manager doesn't support JSON validation in the plaintext editor
  • 💥 Invalid JSON can be accidentally saved, breaking your application
  • 🎯 The Key/value tab provides a better UI for managing individual secrets
  • ⚡ Easier to add, remove, or update individual values

How it works:

When you enable unflattening, Mysterio converts dotted keys like database.password into nested objects:

// AWS Secrets (Key/value tab):
// database.host = prod-db.example.com
// database.port = 5432
// database.credentials.password = secret123
// database.credentials.username = dbuser
// apiKey = my-api-key

// With unflatten enabled:
const config = await mysterio.getMerged(['default', 'env', 'secrets'], true)

console.log(config)
// {
//   database: {
//     host: 'prod-db.example.com',
//     port: 5432,
//     credentials: {
//       password: 'secret123',
//       username: 'dbuser'
//     }
//   },
//   apiKey: 'my-api-key'
// }

🔀 Deep merging with unflattened secrets:

Unflattened secrets merge deeply with your config files:

// config/production.json
{
  "database": {
    "host": "prod-db.example.com",
    "maxConnections": 100
  }
}

// AWS Secrets (Key/value tab):
// database.password = secret123
// database.username = dbuser

// With unflatten enabled:
const config = await mysterio.getMerged(['env', 'secrets'], true)

// Result - secrets merge into existing database object:
{
  "database": {
    "host": "prod-db.example.com",
    "maxConnections": 100,
    "password": "secret123",  // From secrets
    "username": "dbuser"       // From secrets
  }
}

⚠️ Key Conflicts:

When using the unflatten feature, be aware of how key conflicts are handled. If a parent key already exists as a non-object value, it cannot be converted to accept nested properties. The behavior follows JavaScript object property assignment order:

// AWS Secrets (Key/value tab):
// database = "connection-string"
// database.host = "localhost"

// With unflatten enabled:
const secrets = await mysterio.getSecrets(true)

// Result: The last value wins based on object key order
// If 'database' comes after 'database.host' in the object:
// { database: "connection-string" }

// If 'database.host' comes after 'database':
// { database: "connection-string" }
// Note: 'database.host' is ignored because 'database' is already a string

💡 Best Practice: Avoid conflicts by using consistent naming:

  • ✅ Use only nested keys: database.host, database.port, database.password
  • ❌ Don't mix: database (as value) and database.host (as nested key)

This ensures your secrets unflatten predictably into nested objects.

Custom Secret Client

You can provide your own secret fetching function:

const customClient = async (secretName) => {
  // Fetch from your own secret management system
  return {
    apiKey: 'custom-secret'
  }
}

const mysterio = new Mysterio({
  client: customClient
})

Debugging

Mysterio uses the debug module. Enable debug logs:

DEBUG=Mysterio:* node your-app.js

TypeScript Support

import { Mysterio } from 'mysterio'

interface MyConfig {
  database: {
    host: string
    port: number
    password: string
  }
  apiKey: string
}

const mysterio = new Mysterio()
const config = await mysterio.getMerged() as MyConfig

📚 Best Practices

1. Gitignore Sensitive Files

.mysteriorc
config/local.json

2. Use Environment Variables for Runtime Control

const mysterio = new Mysterio({
  configDirPath: process.env.CONFIG_PATH || './config'
})

3. Validate Your Config

const config = await mysterio.getMerged()

if (!config.database?.password) {
  throw new Error('Database password not configured!')
}

4. Organize Secrets by Environment

  • myapp/local - Local development secrets
  • myapp/dev - Development environment
  • myapp/staging - Staging environment
  • myapp/production - Production environment

🔀 How Merging Works

Mysterio uses lodash.merge for deep merging:

  1. Order matters: Later sources override earlier ones
  2. Deep merge: Nested objects are merged, not replaced
  3. Arrays are replaced: Arrays from later sources completely replace earlier ones

Example:

// default.json
{
  "features": ["feature1", "feature2"],
  "database": { "host": "localhost", "port": 5432 }
}

// production.json
{
  "features": ["feature3"],
  "database": { "host": "prod.db" }
}

// Merged result:
{
  "features": ["feature3"],  // Replaced (arrays don't merge)
  "database": {
    "host": "prod.db",       // Overridden
    "port": 5432             // Kept from default
  }
}

🌍 Environment Detection

The environment is determined by NODE_ENV:

# Uses config/production.json and myapp/production secrets
NODE_ENV=production node app.js

# Uses config/dev.json and myapp/dev secrets
NODE_ENV=dev node app.js

# Uses config/local.json and myapp/local secrets (default)
node app.js

Overriding Environment with env Option

You can override the environment detection by passing the env option to the constructor. This is useful when you want to load a specific environment's configuration regardless of the NODE_ENV value:

// Always use staging config, even if NODE_ENV is production
const mysterio = new Mysterio({
  env: 'staging'
})

// This will load:
// - config/staging.json (instead of using NODE_ENV)
// - myapp/staging secrets (instead of myapp/production)

Use cases:

  • Testing: Load production configs in a test environment without changing NODE_ENV
  • Multi-environment apps: Run multiple instances with different configs in the same process
  • Config validation: Test different environment configs without modifying environment variables
  • CI/CD pipelines: Explicitly specify which environment to use regardless of system settings

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Apache-2.0

Author

Amit Tal

Repository

https://github.com/amitosdev/mysterio