2.7 KiB
allowlist
(no submodules)
Purpose
The allow-list module encapsulates the per-plugin scoping rule that
defines the package's security boundary: a plugin can only read secrets
that its manifest's secrets[] block declared, no matter what
Bun.secrets happens to hold under the host's service namespace. Every
get / require call routes through assertAllowed BEFORE any
keystore round-trip, so a leaked plugin cannot probe key existence by
name even when the OS daemon is reachable.
The interface is intentionally minimal — a frozen ordered names array
plus an O(1) has probe and the throwing assertAllowed guard — so the
adapter can compose it without re-implementing the membership semantics.
Features
- Frozen
namesarray preserving manifest declaration order; safe to return directly from the adapter'slist(). has(name)boolean probe for branchy callers (e.g. selective writes).assertAllowed(name)guard that throwsSECRETS_BUN.NOT_ALLOWEDwithcontext.namepopulated for the audit log.- Silent de-duplication so a manifest that imports the same secret twice does not crash at construction.
Usage: Basic
import { createAllowlist } from "@suaveplan/api-secrets-bun/allowlist";
const al = createAllowlist(["STRIPE_SECRET_KEY", "JWT_SIGNING_KEY"]);
al.assertAllowed("STRIPE_SECRET_KEY"); // no-op
al.assertAllowed("AWS_SECRET_KEY"); // throws SECRETS_BUN.NOT_ALLOWED
Usage: Advanced
Compose the allow-list with the adapter to gate set as well as get:
import { createAllowlist } from "@suaveplan/api-secrets-bun/allowlist";
const al = createAllowlist(manifest.secrets.map((s) => s.name));
function writeIfAllowed(name: string, value: string): void {
al.assertAllowed(name);
return Bun.secrets.set({ service: "my-plugin", name, value });
}
API Reference
createAllowlist(names: readonly string[]): Allowlist— build a frozen allow-list from a manifest-derived name list.Allowlist.names: readonly string[]— frozen ordered view.Allowlist.has(name: string): boolean— membership probe.Allowlist.assertAllowed(name: string): void— throwsSecretsBunErrorwith codeSECRETS_BUN.NOT_ALLOWEDon miss.
Implementation Notes
The allow-list owns its Set<string> once at construction so per-call
lookup is O(1). The names array is frozen at the top level only; the
strings inside are primitives and already immutable. assertAllowed
populates context.name so the audit trail can route a denied lookup
back to the offending plugin without surfacing the value (there is no
value yet — the call is rejected before keystore consultation).