Initial Commit
This commit is contained in:
69
openspec/archive/2026-05-25-api-secrets/proposal.md
Normal file
69
openspec/archive/2026-05-25-api-secrets/proposal.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# `@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<string>` (returns the secret value); `has(name: string): Promise<boolean>`; `dispose(): Promise<void>`. 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 `"<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) 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 `"<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`/`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 → `"<redacted>"`; 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)
|
||||
Reference in New Issue
Block a user