Files
2026-05-27 23:29:53 +01:00

3.6 KiB

@suaveplan/api-secrets-env — Environment-variable secrets adapter

Package: @suaveplan/api-secrets-env Status: PROPOSED Type: new-package Stage: 4 (Base adapters) — see umbrella openspec/changes/api-plugin-foundation/

Why

The simplest secrets source: process.env (or Bun.env). Suitable for local development, single-host deployments, container orchestrators that inject secrets as env vars, and anywhere the operator already has tooling around env-var injection.

Per design doc, plugins declare their secrets in plugin.json under manifest.secrets[] (each entry has name, required: boolean, description). The installer's fail-fast contract (§4) is: any required secret missing at install or start time → refuse to install / refuse to start. api-secrets-env implements that contract by reading from env at construction and at validateAgainstManifest(manifest) time.

Stage 4 invariant: ships direct AND worker-proxy surfaces; both pass runSecretsConformance (§5.2a, §13.4).

What changes

Net-new package depending on @suaveplan/api-secrets (contract), @suaveplan/api-types, @suaveplan/api-worker-rpc, @suaveplan/error, @suaveplan/data-masking (for redaction in logs), zod.

  • ADDED: createEnvSecrets(opts) — direct-surface factory returning a SecretsLike. Reads via Bun.env first then falls back to process.env.
  • ADDED: createEnvSecretsWorkerProxy(port, opts) — proxy-surface factory; the worker NEVER touches host env directly — every get() round-trips through the proxy so secret materialisation stays in the main process.
  • ADDED: EnvSecretsOptions — Zod schema (prefix?: string (default ""), caseSensitive?: boolean (default true), fallbackSource?: "process" | "bun" | "both" (default "both")).
  • ADDED: validateAgainstManifest(manifest) — given a plugin manifest's secrets[], checks every required: true entry is present; missing → throws SECRETS_ENV_REQUIRED_MISSING listing every missing name.
  • ADDED: SECRETS_ENV_* error codes (SECRETS_ENV_REQUIRED_MISSING, SECRETS_ENV_INVALID_OPTIONS).
  • ADDED: Log redaction integration — secret VALUES never appear in error messages or telemetry; only NAMES.

Out of scope

  • Watched/reactive secrets — env vars are read once at construction; rotation requires plugin reload (delegated to host hot reload §8).
  • Encrypted secrets at rest — api-secrets-vault Stage 8.
  • Per-tenant secrets — env-based source is host-global by definition.

Source mapping

Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.

Requirement Design doc section
Manifest secrets installer fail-fast §3 (manifest schema), §4 (lifecycle FSM)
Worker-proxy variant required §5.2a
Conformance both surfaces §13.4
Value redaction §12.2

Verification approach

  • src/factory/create-env-secrets.test.ts — get(name) returns env value; missing → undefined; redaction never leaks values.
  • src/factory/create-env-secrets.spec-conformance.test.ts — runSecretsConformance("env-direct", ...).
  • src/proxy/create-env-secrets-worker-proxy.test.ts — proxy unit tests; worker side has no env access.
  • src/proxy/create-env-secrets-worker-proxy.spec-conformance.test.ts — runSecretsConformance("env-worker-proxy", ...).
  • src/manifest-validation/validate-against-manifest.test.ts — required missing → throw with named list; optional missing → silent.
  • src/redaction/redaction.test.ts — error messages and metrics never contain raw secret values.

Migration impact

None — additive only.