4.2 KiB
@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— abstractSecretsLike:get(name: string): Promise<string>(returns the secret value);has(name: string): Promise<boolean>;dispose(): Promise<void>. The type MUST be generic over aNames extends stringsoctx.secrets.get(name)autocompletes against the manifest's declared secret names. - ADDED:
./descriptor—SecretDescriptorshape:{ name: string, required: boolean, description?: string, format?: "string" | "base64" | "json" }. Zod schema for parsing manifest entries. - ADDED:
./redaction—redactSecret(value: string): stringreturning"<redacted>"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) toCapabilityId. - ADDED:
./error-codes— frozenSECRETS_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 "<redacted>" 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/disposeshape;Names extends stringgeneric narrows return type.src/descriptor/descriptor.test.ts— Zod accept/reject suite overSecretDescriptor.src/redaction/redaction.test.ts— input →"<redacted>"; empty string handled.src/capability-id/capability-id.spec-conformance.test.ts—"secrets"AND"secrets.bun"inCapabilityId.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)