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

@idriszade/secrets-sops

v0.2.6

Published

Pipeline-kit SOPS CLI-backed SecretsResolver — spawns sops binary via node:child_process

Readme

@idriszade/secrets-sops

T3 SOPS-backed SecretsResolver for pipeline-kit. Decrypts git-stored encrypted files via the sops CLI binary and exposes secrets as a SecretsResolver with caching, invalidation, and concurrency deduplication.

ADR: VIII-5 (reference-adapter trio, sops leg).

Quick start

import { createSopsSecretsResolver } from '@idriszade/secrets-sops';

const secrets = createSopsSecretsResolver('./secrets.enc.json');

const result = await secrets.resolve('db_password');
if (result.ok) {
  console.log(result.value); // 'hunter2'
} else {
  console.error(result.error); // SecretsError
}

The file at ./secrets.enc.json must be decryptable by sops using the keys available in the current environment (age, PGP, AWS KMS, GCP KMS, Azure Key Vault, etc.).

Options

const secrets = createSopsSecretsResolver('./secrets.enc.json', {
  binaryPath: '/usr/local/bin/sops', // default: 'sops' (PATH lookup)
  timeoutMs: 15_000,                 // default: 30_000ms
});

| Option | Type | Default | Description | |--------------|----------|------------|------------------------------------------------| | binaryPath | string | 'sops' | Path to the sops binary. Falls back to PATH. | | timeoutMs | number | 30000 | Milliseconds before the sops subprocess is treated as hung. | | spawn | typeof spawn | (built-in) | Override for testing via dependency injection. |

Caching and invalidation

The full decoded JSON object is cached after the first resolve() call. Subsequent calls to resolve() for any key are served from cache without re-spawning sops.

Call invalidate(name) to bust the cache. The next resolve() call will re-spawn sops -d to re-read the file. This is the correct pattern for secret rotation — update the encrypted file, then call invalidate.

secrets.invalidate('db_password'); // clears cache; next resolve re-decrypts

Stats

const s = secrets.stats('db_password');
// { reads: 3, currentVersion: '1' }

currentVersion is a stringified counter incremented by each invalidate() call. It is undefined until the first invalidation.

Error mapping

| Situation | code | |----------------------------------|---------------------| | sops non-zero exit | secret_unavailable | | sops binary not found (ENOENT) | secret_unavailable | | sops output is not valid JSON | secret_unavailable | | sops timed out | secret_unavailable | | Key missing or not a string | secret_not_found |

Lambda / serverless caveat

Requires the sops binary on PATH. AWS Lambda, Vercel Functions, and other serverless runtimes do not include sops by default. Either:

  1. Bundle sops as a Lambda Layer or container image layer (sops ships as a single statically linked Go binary — easy to layer).
  2. Use a different adapter (e.g., @idriszade/secrets-oidc) for workload-identity-based secret retrieval on serverless platforms.

Cold-start note: sops subprocess spawn adds approximately 50–200ms cold-start latency on the first secret access per instance. Subsequent calls within the same instance lifetime are served from cache at near-zero cost. For high-frequency Lambda invocations, prefer an adapter that does not require subprocess spawn.

Why no JS wrapper dependency?

The sops Go binary is the upstream source of truth. JS wrappers (e.g., @figedi/sops) lag behind binary releases and add abstraction over a stable CLI interface. Spawning directly via node:child_process keeps the dependency surface minimal and the adapter version-independent of sops's release cadence. The spawn injection point (options.spawn) makes the adapter fully testable without a real sops binary.

Concurrency

If multiple resolve() calls are made while a sops subprocess is in-flight, they share the pending promise — only one subprocess is spawned. All callers receive the same result when the subprocess completes.