# `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 `names` array preserving manifest declaration order; safe to return directly from the adapter's `list()`. - `has(name)` boolean probe for branchy callers (e.g. selective writes). - `assertAllowed(name)` guard that throws `SECRETS_BUN.NOT_ALLOWED` with `context.name` populated for the audit log. - Silent de-duplication so a manifest that imports the same secret twice does not crash at construction. ## Usage: Basic ```ts 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`: ```ts 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` — throws `SecretsBunError` with code `SECRETS_BUN.NOT_ALLOWED` on miss. ## Implementation Notes The allow-list owns its `Set` 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).