Initial Commit
This commit is contained in:
78
openspec/specs/api-secrets-vault/spec.md
Normal file
78
openspec/specs/api-secrets-vault/spec.md
Normal file
@@ -0,0 +1,78 @@
|
||||
<!-- 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"`
|
||||
Reference in New Issue
Block a user