# Capability: api-secrets-vault **Package**: `@suaveplan/api-secrets-vault` **Status**: ACTIVE > Every requirement below carries a `**Verified by:**` line. ## Requirements ### Requirement: Vault-backed adapter implements `secrets` The package SHALL implement the abstract `secrets` capability over a `@suaveplan/vault` instance. The vault is passed in already unlocked; the adapter MUST NOT see the master key. #### Scenario: get returns stored value - WHEN a secret is set on the underlying vault AND `get(name)` is called - THEN the adapter returns the value AND `api.secrets.reads_total{backend="vault"}` increments **Verified by:** `src/adapter/adapter.test.ts::"get returns stored value AND increments reads_total"` --- ### Requirement: Locked vault surfaces explicitly The package SHALL distinguish a locked vault (`SECRETS_VAULT_LOCKED`) from a missing entry (`SECRETS_VAULT_NOT_FOUND`). #### Scenario: locked vault raises specific code - WHEN the vault is locked at read time - THEN reads raise `SECRETS_VAULT_LOCKED` AND no plaintext fallback occurs **Verified by:** `src/adapter/adapter.test.ts::"locked vault raises SECRETS_VAULT_LOCKED"` --- ### Requirement: Per-plugin allowlist enforcement The package SHALL refuse reads of secret names not in the plugin manifest's `secrets[]`. Refusals raise `SECRETS_VAULT_NOT_ALLOWED`. #### Scenario: un-manifested read refused - WHEN a plugin's manifest declares `secrets = [{ name: "STRIPE" }]` AND code calls `get("AWS")` - THEN the call raises `SECRETS_VAULT_NOT_ALLOWED` **Verified by:** `src/allowlist/allowlist.test.ts::"un-manifested read raises SECRETS_VAULT_NOT_ALLOWED"` --- ### Requirement: Vault updates surface as `secret.changed` events The package SHALL subscribe to the vault's `secret.updated` event AND, when the updated secret is in the plugin's allowlist, emit `secret.changed` on the host event bus carrying `{ name }` only (no value). #### Scenario: allowlisted update emits secret.changed - WHEN the vault publishes `secret.updated` for a name in the allowlist - THEN the host bus receives exactly one `secret.changed` event AND the payload contains only `{ name }` #### Scenario: non-allowlisted update does not emit - WHEN the vault publishes `secret.updated` for a name NOT in the allowlist - THEN no `secret.changed` event is emitted on the host bus **Verified by:** `src/watcher/watcher.test.ts::"allowlisted update emits secret.changed"` AND `src/watcher/watcher.test.ts::"non-allowlisted update does not emit"` --- ### Requirement: Passes `runSecretsConformance` The package SHALL pass `runSecretsConformance` from `@suaveplan/api-secrets/testing`. #### Scenario: conformance passes - WHEN `runSecretsConformance("vault", makeVaultAdapter)` runs - THEN every harness `it(...)` passes **Verified by:** `src/adapter/adapter.spec-conformance.test.ts::"vault adapter passes runSecretsConformance"`