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

@simpleworkjs/bao-conf

v1.0.1

Published

OpenBao/Vault KV-v2 secrets overlay for @simpleworkjs/conf — deep-merge boot secrets from OpenBao over the sync-loaded config object.

Readme

@simpleworkjs/bao-conf

OpenBao / HashiCorp Vault KV-v2 secrets overlay for @simpleworkjs/conf.

npm version Tests License: MIT

View Full Documentation

@simpleworkjs/conf loads its config object synchronously at require() time (base.js<env>.jssecrets.jsapp_* env) and exposes no async hook. @simpleworkjs/bao-conf performs the complementary async step: at boot, after require('@simpleworkjs/conf') returns, call init({ path, conf }) to fetch secret/data/<path>/conf from OpenBao and deep-merge it over the live conf object in place. Because the merge mutates the same reference every consumer already holds, code that reads conf.ldap.bindPassword at call time picks up the OpenBao value automatically.

Table of Contents

Installation

npm install --save @simpleworkjs/bao-conf @simpleworkjs/conf

Requires Node.js >= 18 (uses the global fetch).

Quick Start

const conf = require('@simpleworkjs/conf');
const baoConf = require('@simpleworkjs/bao-conf');

// After @simpleworkjs/conf has loaded, overlay secrets from OpenBao.
// Fetches secret/data/sso-manager/conf and deep-merges it into `conf`.
await baoConf.init({ path: 'sso-manager', conf });

console.log(conf.ldap.bindPassword);   // now the OpenBao value
console.log(conf.oidc.clientSecret);   // now the OpenBao value

init() is fail-soft: if OpenBao is unreachable or the path is absent, it logs a warning, leaves conf untouched, and resolves — so a missing overlay never crashes boot. Make sure your file-loaded config is a safe fallback.

The Boot-Order Constraint

This is the one subtlety that matters. Some code captures a secret at require() time rather than reading it at call time. The canonical example is an OIDC client built during require('../models'):

// models/index.js — runs at require time
const oidcClient = createOidcClient({ clientSecret: conf.oidc.clientSecret });

init() mutates conf after it returns — so any value already captured into a closure will not see the overlay. The fix is to ensure init() resolves before the capturing require() runs. Wrap your bin/www so the fetch happens first:

const conf = require('@simpleworkjs/conf');
const baoConf = require('@simpleworkjs/bao-conf');

baoConf.init({ path: 'proxy', conf }).then(() => {
  const app = require('../app');   // models + createOidcClient now see merged conf
  const server = http.createServer(app);
  server.listen(port);
}).catch(err => { console.error('boot failed:', err); process.exit(1); });

If your bin/www already requires ../models as an explicit line (e.g. jump-host), gate that line:

const conf = require('@simpleworkjs/conf');
require('@simpleworkjs/bao-conf').init({ path: 'jump-host', conf }).then(() => {
  require('../models');            // createOidcClient sees merged conf.oidc
  const app = require('../app');
  server.listen(webPort);
  sshServer.start();
});

Values read at call time (e.g. conf.ldap.bindPassword inside a lookup function) need no special handling — they see the overlay whenever it has resolved.

API Reference

init({ path, conf, addr?, token? }) → Promise<conf>

Fetch secret/data/<path>/conf and deep-merge it over conf in place. Fail-soft on error/404, and fail-soft on a missing VAULT_TOKEN (standalone Docker, bare metal, CI images with no OpenBao sidecar): warns and leaves conf untouched so boot continues from the file-loaded config. Throws only if path/conf are omitted. The explicit get/set/request helpers still throw on a missing token — they are intentional operations against OpenBao, not a boot-time overlay.

get(path, opts?) → Promise<object|null>

Read a KV-v2 secret at secret/data/<path>. Returns the inner data object, or null if absent / on error (fail-soft).

set(path, data, opts?) → Promise<object>

Write a KV-v2 secret at secret/data/<path> (wrapped as { data } per KV-v2). Throws on a non-2xx response. Used by bootstraps that write generated creds into OpenBao.

request(method, vaultPath, body?, opts?) → Promise<Response>

Low-level OpenBao API request below /v1/. Returns the raw fetch Response. Used by application-side brokers that mint scoped tokens or write policies (auth/token/create/<role>, sys/policies/acl/<name>, …).

configure({ addr?, token? }) → { addr, token }

Resolve and cache the OpenBao connection config from options or env. Called implicitly by init/get/set/request; exported for explicit setup. Throws if no token is available.

Environment Variables

| Variable | Description | Default | |---|---|---| | VAULT_ADDR | OpenBao API URL | http://openbao:8200 | | VAULT_TOKEN | OpenBao token (scoped — no root fallback) | required |

opts.addr / opts.token override the env on a per-call basis.

Examples

Bootstrap writing generated creds into OpenBao

const baoConf = require('@simpleworkjs/bao-conf');
// BOOTSTRAP_VAULT_TOKEN has write policy on secret/proxy/conf etc.
await baoConf.set('proxy/conf', { oauth: { clientId, clientSecret } });

A token/policy broker (server-side, scoped tokens)

const baoConf = require('@simpleworkjs/bao-conf');
// Create a per-user policy, then mint a scoped token through a role.
await baoConf.request('PUT', `sys/policies/acl/user-${uid}`, {
  policy: `path "secret/users/${uid}/*" { capabilities = ["create","read","update","delete","list"] }`
});
const res = await baoConf.request('POST', 'auth/token/create/sso-broker', { policy: `user-${uid}` });
const { auth } = await res.json();
// auth.client_token is the per-user token — inject it as X-Vault-Token on proxied requests.

Best Practices

  • Mint scoped per-app tokens in your setup/orchestration and pass them via VAULT_TOKEN. Never propagate the OpenBao root token to application containers.
  • Call init() before any require-time capture of an overlaid secret (see The Boot-Order Constraint).
  • Keep file-loaded config as a safe fallbackinit() is fail-soft by design; make sure the app can still boot (degraded) if OpenBao is unavailable.
  • Use fail-soft for boot config, fail-loud for writesinit/get resolve on error; set throws. Don't wrap set in a swallow-catch during bootstrap.
  • Deep-merge, don't replaceinit deep-merges, so partial overlays (just the secrets) work without re-stating the whole config in OpenBao.

Development

npm install
npm test              # mocha
npm run test:coverage # c8 mocha

Contributions welcome — see the GitHub repository.