# `@suaveplan/api-secrets` — abstract Secrets contract **Package**: `@suaveplan/api-secrets` **Status**: PROPOSED **Type**: new-package **Stage**: 1 (Contracts) — see umbrella `openspec/changes/api-plugin-foundation/` ## Why Every plugin needs to read secrets — Stripe keys, JWT signing keys, OAuth client secrets, database passwords. Different deployments want different sources: `process.env` / `Bun.env` (dev + simple prod), `Bun.secrets` (OS keystore), `@suaveplan/vault` (the genesis vault), HashiCorp Vault, AWS Secrets Manager. This package owns the abstract contract: `SecretsLike` (read-only, typed against the manifest's `secrets[]` declarations) plus `SecretDescriptor` (the manifest-side declaration). Concrete sources plug behind it (`api-secrets-env`, `api-secrets-bun`, `api-secrets-vault`). Per design §5.2: "every adapter exports `defineCapability({ id, version, scope, provider, disposer })`." Per §5.1: `ctx.secrets: SecretsLike; // typed against manifest.secrets[]`. ## What changes Net-new package. Contract + Zod schema for `SecretDescriptor` + conformance harness. - ADDED: `./secrets-like` — abstract `SecretsLike`: `get(name: string): Promise` (returns the secret value); `has(name: string): Promise`; `dispose(): Promise`. The type MUST be generic over a `Names extends string` so `ctx.secrets.get(name)` autocompletes against the manifest's declared secret names. - ADDED: `./descriptor` — `SecretDescriptor` shape: `{ name: string, required: boolean, description?: string, format?: "string" | "base64" | "json" }`. Zod schema for parsing manifest entries. - ADDED: `./redaction` — `redactSecret(value: string): string` returning `""` for log/audit use. Helper, not enforced — adapters / loggers may use it. - ADDED: `./capability-id` — adds `"secrets"` (and ensures `"secrets.bun"` is reserved for the Stage-8 adapter) to `CapabilityId`. - ADDED: `./error-codes` — frozen `SECRETS_CODES`: `SECRET_NOT_FOUND`, `SECRET_REQUIRED_MISSING_AT_START`, `SECRET_INVALID_FORMAT`, `SOURCE_UNAVAILABLE`. - ADDED: `./testing` — `runSecretsConformance(factory)` reusable harness. ## CapabilityId additions This package contributes `"secrets"` to the `CapabilityId` union. The Bun-specific `"secrets.bun"` capability id is reserved here (declared in the union) for the Stage-8 `api-secrets-bun` adapter, so plugins can declare `requiredCapabilities: ["secrets.bun"]` at type-check time before the adapter ships. ## Out of scope - Concrete sources (`api-secrets-env`, `api-secrets-bun`, `api-secrets-vault`) — Stages 4 and 8. - Secret rotation — out of scope for v1 (separate change). - Encryption-at-rest — source-specific. ## Source mapping Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`. | Requirement | Design doc section | |---|---| | `ctx.secrets: SecretsLike` typed against manifest | §5.1, §5.2 | | Required-secret enforcement at start | §3 manifest example (`"required": true`) + §4 install hook order | | Per-secret format declaration | §3 manifest example | | `"secrets.bun"` capability | §3 manifest example (`"optionalCapabilities": ["events.bounded-queue", "secrets.bun"]`) | ### Requirements with no source line | Requirement | Justification | |---|---| | `redactSecret` helper | Logging plugins / OTel exporters need a canonical redaction marker; pinning the string `""` here means every downstream tool agrees. | | `runSecretsConformance` harness | Stage-5 deliverable (§13.4). | ## Verification approach - `src/secrets-like/secrets-like.spec-conformance.test.ts` — `get`/`has`/`dispose` shape; `Names extends string` generic narrows return type. - `src/descriptor/descriptor.test.ts` — Zod accept/reject suite over `SecretDescriptor`. - `src/redaction/redaction.test.ts` — input → `""`; empty string handled. - `src/capability-id/capability-id.spec-conformance.test.ts` — `"secrets"` AND `"secrets.bun"` in `CapabilityId`. - `tests/dep-graph.test.ts` — deps: `@suaveplan/api-types`, `@suaveplan/error`, `@suaveplan/types`, `zod`. ## Migration impact None — additive only. ## Impact ### Affected dependents - (none) — [x] no action needed (reason: net-new; concrete sources land Stage 4/8)