79 lines
2.9 KiB
Markdown
79 lines
2.9 KiB
Markdown
<!-- markdownlint-disable MD024 -->
|
|
|
|
# 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"`
|