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-in-layers/secrets

v1.0.1

Published

The official secrets handling library for Node-in-Layers systems

Readme

Node In Layers Secrets

Unit Tests Coverage Status

The official library for handling secrets with Node in Layers.

How to Use

1. Install Library

npm install @node-in-layers/secrets

2. Add To Configuration

// config.base.mts
import { CoreNamespace } from '@node-in-layers/core'
import {
  config as secretsConfig,
  SecretsNamespace,
} from '@node-in-layers/secrets'

export default async () => ({
  environment: 'base',
  systemName: 'your-system-name',
  [CoreNamespace.root]: {
    apps: [
      // If using the configuration approach (recommended for most applications) put the secrets configuration here.
      secretsConfig,
      // your domains
    ],
  },
  // Optional: see SecretsConfig — omit or use {} to use the default json file backend.
  [SecretsNamespace.Core]: {},
})

To use the default json file backend instead, omit secretServiceFactory (or pass [SecretsNamespace.Core]: {}).

Main Capabilities

  1. Ability to retrieve system-level secrets from a secrets manager through a unified interface.
  2. Ability to seamlessly replace secrets placeholders in a system's config via a secrets manager.

Domains

SecretsNamespace.Core (@node-in-layers/secrets)

Provides the basic capabilities of storing and retrieving secrets.

Core wires a single SecretsService-compatible backend from config and exposes the full string + JSON API (JSON is synthesized from strings when the backend omits JSON methods).

SecretsConfig

SecretsConfig (under [SecretsNamespace.Core] in system config) resolves a backend in one of two ways only:

  1. secretServiceFactory(ctx: CommonContext) => SecretsService | Promise<SecretsService>. Use during globals when full ServicesContext is not available yet (for example secretsService from @node-in-layers/aws).
  2. Default — if secretServiceFactory is omitted, the json file backend (secrets.{ENVIRONMENT}.json) is used.

The ordered step ids are exported as SECRETS_CONFIG_RESOLUTION from this package (for docs and tooling).

Interface Description

Any domain can provide the ability to retrieve and store secrets by providing a services layer that implements the SecretsService type. getStoredSecret and storeSecret are required. getStoredJsonSecret and storeSecretJson are optional on the implementation: if you omit them, this library automatically implements them by delegating to getStoredSecret / storeSecret with JSON.parse and JSON.stringify.

Provide your own getStoredJsonSecret / storeSecretJson only when the backend can handle JSON more efficiently or differently than string round-tripping.

NOTE: If a domain does not want to provide a storeSecret() function, then the system should throw an exception.

Types

import { JsonObj, JsonAble } from 'functional-models'

type GetSecretProps = Readonly<{
  /**
   * The key path to the stored secret.
   */
  key: string
  /**
   * Optional metadata useful for grabbing the secret.
   */
  readonly [s: string]: JsonAble
}>

type StoreSecretProps = Readonly<{
  /**
   * The key path to the secret.
   */
  key: string
  /**
   * The value to store.
   */
  value: string
  /**
   * Optional metadata useful for setting the secret correctly.
   */
  readonly [s: string]: JsonAble
}>

type StoreSecretJsonProps<T extends JsonObj> = Readonly<{
  /**
   * The key path to the secret.
   */
  key: string
  /**
   * The value to store.
   */
  value: JsonObj
  /**
   * Optional metadata useful for setting the secret correctly.
   */
  readonly [s: string]: JsonAble
}>

/**
 * What a secrets manager *implements*. JSON methods are optional; the library fills them in when missing.
 */
type SecretsService = Readonly<{
  getStoredSecret: (props: GetSecretProps) => Promise<string>
  storeSecret: (props: StoreSecretProps) => Promise<void>
  getStoredJsonSecret?: <T extends JsonObj = JsonObj>(
    props: GetSecretProps
  ) => Promise<T>
  storeSecretJson?: <T extends JsonObj = JsonObj>(
    props: StoreSecretJsonProps<T>
  ) => Promise<void>
}>

/**
 * Optional marker for backends that implement all four methods natively (e.g. optimized JSON storage).
 * Call sites still receive the same surface whether methods are native or library-wrapped.
 */
type FullSecretsService = Readonly<{
  getStoredSecret: (props: GetSecretProps) => Promise<string>
  getStoredJsonSecret: <T extends JsonObj = JsonObj>(
    props: GetSecretProps
  ) => Promise<T>
  storeSecret: (props: StoreSecretProps) => Promise<void>
  storeSecretJson: <T extends JsonObj = JsonObj>(
    props: StoreSecretJsonProps<T>
  ) => Promise<void>
}>

Example:

Most managers only implement the string APIs. Do not implement getStoredJsonSecret / storeSecretJson unless the backend needs native JSON behavior—the library supplies those by wrapping getStoredSecret / storeSecret with JSON.parse / JSON.stringify, so callers always use the full API and never add their own JSON fallbacks.

// your-system/src/yourDomain/services.ts

import type {
  GetSecretProps,
  StoreSecretProps,
  SecretsService,
} from '@node-in-layers/secrets'
import { ServicesContext } from '@node-in-layers/core'
import { MyDomainServices } from './types.ts'

export type MyDomainServices = SecretsService & {
  // whatever else
}

export const create = (context: ServicesContext): MyDomainServices => {
  const getStoredSecret = async (props: GetSecretProps): Promise<string> => {
    // read stored secret using props.key (and optional metadata).
    return 'stored-secret'
  }

  const storeSecret = async (props: StoreSecretProps): Promise<void> => {
    throw new Error('Not implemented')
  }

  return {
    getStoredSecret,
    storeSecret,
  }
}

SecretsNamespace.Config (@node-in-layers/secrets/config)

This domain provides the most common capability of loading secrets at runtime, by replacing structured secret placeholders, via a secrets manager. Instead of calling stored secrets each time they're used, the path to secrets can be configured in the config, and then at load time, all secrets are pulled down and set in the config. These secrets can then be seamlessly used throughout the application.

How To Use

  1. Define and create a placeholder for your secret inside your configuration file.
// config.base.mts
import { CoreNamespace } from '@node-in-layers/core'
import {
  config as secretsConfig,
  SecretsNamespace,
} from '@node-in-layers/secrets'

export default async () => ({
  environment: 'base',
  systemName: 'your-system-name',
  [CoreNamespace.root]: {
    apps: [
      // Place this close to the top. Above anything that needs a
      // clean config with secrets replaced.
      secretsConfig,
      // your domains
    ],
  },
  [SecretsNamespace.Core]: {},
  yourDomain: {
    // This entire structure will collapse down to a string. Example: 'mySecretKey: "the stored secret"'
    mySecretKey: {
      type: 'nil-secret',
      format: 'string',
      key: '/your-system-name/dev/my-secret-key',
    },
  },
  anotherDomain: {
    nested: {
      // This structure is replaced with a json object. Example: anotherSecretKey: {}
      anotherSecretKey: {
        type: 'nil-secret',
        format: 'json',
        key: '/your-system-name/dev/another-secret-as-a-json',
      },
    },
  },
})

Another Example Using AWS Secrets Config Replacement

// config.base.mts
import { CoreNamespace } from '@node-in-layers/core'
import {
  config as secretsConfig,
  SecretsNamespace,
} from '@node-in-layers/secrets'
import { secretsService, AwsNamespace, AwsService } from '@node-in-layers/aws'

export default async () => ({
  environment: 'base',
  systemName: 'your-system-name',
  [AwsNamespace.root]: {
    awsClientProps: { region: 'us-east-1' },
    services: [AwsService.secretsManager, AwsService.ssm],
  },
  [CoreNamespace.root]: {
    apps: [
      secretsConfig,
      // your domains
    ],
  },
  [SecretsNamespace.Core]: {
    secretServiceFactory: secretsService,
  },
  yourDomain: {
    // This entire structure will collapse down to a string. Example: 'mySecretKey: "the stored secret"'
    mySecretKey: {
      awsService: 'secretsManager',
      type: 'nil-secret',
      format: 'string',
      key: '/your-system-name/dev/my-secret-key',
    },
  },
  anotherDomain: {
    nested: {
      // A property that is stored in parameterStore (not a secret, but rides on the backbone)
      anAwsProperty: {
        awsService: 'parameterStore',
        type: 'nil-secret',
        format: 'json',
        key: '/your-system-name/dev/not-a-secret-a-config',
      },
    },
  },
})
import { JsonAble } from 'functional-models'

enum SecretFormat {
  String = 'string',
  Json = 'json',
}

type StructuredSecretEntry = Readonly<{
  /**
   * This tells the system that this is a node in layer secret that should be replaced.
   */
  type: 'nil-secret'
  // The format for replacing the SecretEntry. If string, it sets a string value. If json, it uses the JSON secret path (resolved via the same automatic JSON wrapping as `getStoredJsonSecret` when the manager only implements string APIs).
  format?: SecretFormat
  // The key path to the secret.
  key: string
  // Optional information specific passed into the storage system.
  readonly [data: string]: JsonAble
}>

SecretsNamespace.Json (@node-in-layers/secrets/json)

The default file-based backend: secrets are read from JSON or JSON5 files under the working directory, using the same SecretsService contract as other backends.

File Pathing

These files are automatically found at the base of the system directory (working directory) using the environment name. secrets.{ENVIRONMENT}.json secrets.{ENVIRONMENT}.json5