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

lockersm

v2.0.6

Published

Official Locker Secrets SDK for Node.js

Readme

Locker Secrets SDK for Node.js

The official Node.js SDK for Locker Passwords & Secrets Management. It uses the versioned locker.sdk protocol exposed by the Locker CLI and supports both ES modules and CommonJS.

Requirements

  • Node.js 22.20+ or Node.js 24 LTS
  • A Locker access key
  • A compatible Locker CLI binary

Installing or importing the npm package never performs a network download. An explicit absolute, regular, non-link CLI path through cliPath or LOCKER_CLI_PATH always bypasses managed updates. Bare and relative values are rejected instead of being searched through ambient PATH. Otherwise, the first SDK client use checks the signed release channel and periodically checks for a newer compatible release. You can also force a check:

npx lockersm-install

The SDK verifies signed release metadata, the CLI artifact's SHA-256 hash and Ed25519 signature, and its declared OS and architecture. Rollback, same-version replacement, signature, provenance, and integrity failures are rejected. Resolution order is:

  1. Constructor cliPath
  2. LOCKER_CLI_PATH
  3. The latest fully verified managed release

Managed binaries are reverified before execution, stored in a private per-user cache, and activated atomically only after verification succeeds. A transient network failure may use the last fully verified cached release; verification failures always fail closed. Explicit caller-owned CLI paths remain the caller's trust responsibility and skip managed-channel verification.

The legacy auto-downloaded locker_secret binary is never selected automatically because it has no protocol-v1 provenance. During migration it can only be selected explicitly; capability negotiation still rejects a binary that does not implement the required protocol.

Installation

npm install lockersm

Migrating from 1.x

Version 2 requires a supported Node.js LTS line (22.20+ or 24.x) and a Locker CLI that exposes protocol v1. Package installation no longer runs a postinstall downloader. Let the first SDK use resolve the latest signed CLI, run npx lockersm-install, or set cliPath / LOCKER_CLI_PATH to a trusted CLI. The legacy locker_secret binary is not auto-selected.

get and getSync now return a default only for NOT_FOUND; other failures raise typed errors. Use getRequired or getRequiredSync wherever a missing secret must stop application startup.

Configuration

Use the canonical Locker environment variables:

| Environment variable | Purpose | | -------------------------- | --------------------------------- | | LOCKER_ACCESS_KEY_ID | Project access key ID | | LOCKER_SECRET_ACCESS_KEY | Project secret access key | | LOCKER_API_BASE | Cloud or self-hosted API base URL | | LOCKER_CLI_PATH | Absolute caller-owned CLI path |

Migration lookup uses this fixed precedence: LOCKER_ACCESS_KEY_ID then ACCESS_KEY_ID, and LOCKER_SECRET_ACCESS_KEY then SECRET_ACCESS_KEY, then LOCKER_ACCESS_KEY_SECRET, then ACCESS_KEY_SECRET.

The SDK trims outer credential whitespace, requires the access key ID to be an exact UUIDv4, and requires a non-empty canonical standard-Base64 secret access key. Invalid local credentials fail as a non-retryable LockerAuthenticationError before the SDK resolves or launches a CLI binary.

import { Locker } from 'lockersm'

const locker = Locker.fromEnv()

An explicit configuration is also supported:

const locker = new Locker({
  accessKeyId: process.env.LOCKER_ACCESS_KEY_ID!,
  secretAccessKey: process.env.LOCKER_SECRET_ACCESS_KEY!,
  apiBase: 'https://api.locker.io/locker_secrets',
  cliPath: process.env.LOCKER_CLI_PATH,
  timeoutMs: 30_000,
})

Credentials, headers and secret values are serialized into one JSON request on the CLI's stdin. The child process receives only one argument: sdk.

Reading secrets

const value = await locker.get('DATABASE_PASSWORD', 'production')
const syncValue = locker.getSync('DATABASE_PASSWORD', 'production')

get and getSync return undefined or the supplied default only when Locker returns NOT_FOUND. Authentication, permission, network, storage and protocol errors are never converted into a default.

For configuration that must exist, use the fail-closed accessors:

const value = await locker.getRequired('DATABASE_PASSWORD', 'production')
const syncValue = locker.getRequiredSync('DATABASE_PASSWORD', 'production')

Retrieve the full typed resource or list resources:

const secret = await locker.retrieve('DATABASE_PASSWORD', 'production')
const secrets = await locker.list('production')
const environments = await locker.listEnvironments()

Every read operation also has the existing synchronous variant. Node.js inspection and Secret#toString() redact the value. Explicit Secret#toJSON() and SDK export methods include plaintext by design, so their results must be handled as sensitive data.

For large collections, use the bounded page APIs and continue until nextCursor is null:

let cursor: string | undefined
do {
  const page = await locker.listPage({ pageSize: 100, cursor }, 'production')
  for (const secret of page.items) {
    useSecret(secret)
  }
  cursor = page.nextCursor ?? undefined
} while (cursor !== undefined)

listEnvironmentsPage, listPageSync, and listEnvironmentsPageSync use the same opaque cursor contract.

Creating and updating

await locker.create({
  key: 'DATABASE_PASSWORD',
  value: 'secret value',
  environmentName: 'production',
  description: 'Primary database password',
})

await locker.modify('DATABASE_PASSWORD', 'production', {
  newKey: 'PRIMARY_DATABASE_PASSWORD',
  value: 'new value',
  environmentName: null, // clear the environment association
  description: '',
})

await locker.createEnvironment({
  name: 'production',
  externalUrl: 'https://example.com',
})

await locker.modifyEnvironment('production', {
  newName: 'production-eu',
  externalUrl: 'https://eu.example.com',
})

Cache, timeout and cancellation

Cache settings can be configured globally or per call. Per-call values, including false and 0, override the global defaults.

const controller = new AbortController()

const secrets = await locker.list(undefined, {
  fetch: true,
  restTime: 0,
  timeoutMs: 5_000,
  signal: controller.signal,
})

controller.abort()

The SDK bounds request, stdout and stderr sizes and terminates the CLI process tree when an asynchronous call times out or is cancelled. Synchronous calls support a bounded timeout and detect an already-aborted signal. A call uses one total timeout budget across capability negotiation and the vault operation; each subprocess receives only the remaining time. maxBufferBytes may lower the 20 MiB protocol response ceiling but cannot raise it.

The SDK does not automatically retry a vault RPC. Create and update are sent once because a lost response can leave the remote commit outcome unknown. Applications may inspect error.retryable and add bounded retry only around read-only operations.

restTime controls the CLI's encrypted, revision-aware vault cache; the SDK does not retain plaintext secret values. restTime: 0 disables offline reuse, and fetch: true requires a successful server refresh with no cache fallback. A transient outage may reuse only a still-fresh cache last validated successfully by the server. Authentication, authorization, TLS, integrity, malformed-response, and local-storage failures fail closed.

Error handling

Protocol errors retain the JSON-RPC code, Locker kind, retryability and request ID:

import {
  LockerAlreadyExistsError,
  LockerAuthenticationError,
  LockerConflictError,
  LockerNotFoundError,
  LockerRateLimitError,
  LockerTransportError,
} from 'lockersm'

try {
  await locker.create({
    key: 'PAYMENT_API_KEY',
    value: paymentApiKey,
  })
} catch (error) {
  if (error instanceof LockerAlreadyExistsError) {
    // PAYMENT_API_KEY already exists.
    // This is also a LockerConflictError.
  } else if (error instanceof LockerNotFoundError) {
    // Secret does not exist.
  } else if (error instanceof LockerAuthenticationError) {
    // Inspect error.kind without exposing either credential:
    // missing_credentials, invalid_access_key_id,
    // malformed_secret_access_key, invalid_secret_access_key, or unauthorized.
  } else if (error instanceof LockerRateLimitError && error.retryable) {
    // retryAfterSeconds is an optional validated 0..86400 hint.
  } else if (error instanceof LockerTransportError) {
    // The CLI process did not complete a valid protocol exchange.
  }
}

| Protocol code | JavaScript error | Canonical kind | | ------------: | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | -32700 | LockerProtocolError | parse_error | | -32600 | LockerProtocolError | invalid_request | | -32601 | LockerProtocolError | method_not_found | | -32602 | LockerProtocolError | invalid_params | | -32603 | LockerProtocolError | internal_protocol_error | | -32000 | LockerError and legacy subtypes | operation_error, request_rejected, response_too_large, cancelled | | -32001 | LockerAuthenticationError | missing_credentials, invalid_access_key_id, malformed_secret_access_key, invalid_secret_access_key, unauthorized | | -32003 | LockerPermissionError | forbidden; legacy permission_denied | | -32004 | LockerNotFoundError | secret_not_found, environment_not_found; legacy not-found aliases | | -32009 | LockerConflictError / LockerAlreadyExistsError | conflict, secret_already_exists, environment_already_exists | | -32022 | LockerValidationError | validation_error | | -32029 | LockerRateLimitError | rate_limited | | -32050 | LockerNetworkError | network_error, network_timeout; legacy http_error | | -32051 | LockerServerError | service_unavailable, internal_error; legacy server_error | | -32060 | LockerStorageError | database_error, file_error, path_error | | -32070 | LockerIntegrityError | integrity, transport-integrity, and data-integrity kinds |

Classification is numeric-first. Distinctive kinds from older CLI releases (duplicate_hash, *_already_exists, conflict, validation_error, and the integrity aliases) are also mapped when their legacy code is -32000. request_rejected, response_too_large, and cancelled have explicit subtypes but are never guessed to be conflicts. All -32000 errors and known authentication, permission, not-found, conflict, validation, storage, integrity, protocol, cancellation, and internal-server errors expose retryable === false. Only rate-limit, network, service-unavailable, or an unknown server-range code can preserve a true hint. The SDK never replays a vault RPC automatically.

The SDK opts into typed errors only after system.capabilities advertises the exact typed-v1 value in error_contracts; absent and unknown valid contracts remain compatible and are not sent in operation context. serverRequestId is a separately validated upstream correlation ID. It never replaces the local JSON-RPC requestId and is not included in default error text.

Error objects and logs never retain protocol bodies, raw CLI messages, or CLI output. Upgrade the CLI to receive the unambiguous numeric codes above.

Import and export

The compatibility import and export APIs remain available. File parsing and formatting happen inside the SDK; every vault operation still uses protocol v1.

await locker.import('.env.production')

await locker.export({
  outputFile: '.env.production',
  format: 'env',
  env: 'production',
})

Import accepts dotenv assignments and INI-style environment sections. Export supports txt, env and json.

Versioning and releases

The package follows Semantic Versioning. npm packages use MAJOR.MINOR.PATCH versions and source releases use matching vMAJOR.MINOR.PATCH tags.

Security

Report security issues through the Locker Bug Bounty program. Do not include access keys or plaintext secrets in issue trackers. Product help is available at support.locker.io.

Troubleshooting

  • missing_credentials: set both canonical credential environment variables.
  • invalid_access_key_id or malformed_secret_access_key: correct the local UUIDv4/Base64 credential format.
  • invalid_secret_access_key or unauthorized: replace the credential pair or verify that it has not been revoked.
  • Permission errors: verify the credential's project/environment scope.
  • LockerTransportError: check the API base, CA/proxy, timeout, and absolute CLI path; protocol bodies and CLI stderr are intentionally unavailable.
  • Managed install failure: check system time, access to the Locker Secrets download service, and private ownership below ~/.locker/sdk-cli/nodejs.
  • Protocol failure: upgrade the SDK and CLI together or remove an incompatible explicit LOCKER_CLI_PATH.
  • Unexpected stale reads: use fetch: true; do not loosen cache permissions.

License

Apache-2.0