Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# @suaveplan/api-secrets-vault — SecretsLike adapter backed by @suaveplan/vault. The vault is passed in unlocked; the adapter never sees the master key. Per-plugin allow-list enforcement, vault `secret.updated` → host bus `secret.changed` forwarding, OTel `api.secrets.reads_total{backend="vault"}`, idempotent dispose. Real vault instance, no mocks.
|
||||
|
||||
Stage-8 `SecretsLike` adapter backed by `@suaveplan/vault`. The vault is
|
||||
passed in already unlocked by the host's startup sequence; the adapter
|
||||
never sees the master key. Per-plugin allow-list enforcement, vault
|
||||
`secret.updated` → host event-bus `secret.changed` forwarding, OTel
|
||||
`api.secrets.reads_total{backend="vault"}` counter, idempotent dispose.
|
||||
Real vault instance, no mocks.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [adapter](./src/adapter/adapter.md) — Stage-8 concrete `SecretsLike` implementation backed by a `VaultSecretsBackend` (real `@suaveplan/vault.Vault` via `fro…
|
||||
- [adapter](./src/adapter/adapter.md) — Stage-8 concrete `SecretsLike` implementation backed by a `VaultSecretsBackend` (real `@suaveplan/vault.Vault` via `fro…
|
||||
- [allowlist](./src/allowlist/allowlist.md) — Compiled per-plugin allow-list of secret names. The vault-backed adapter calls `assertAllowed` BEFORE consulting the ba…
|
||||
- [allowlist](./src/allowlist/allowlist.md) — Compiled per-plugin allow-list of secret names. The vault-backed adapter calls `assertAllowed` BEFORE consulting the ba…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Frozen registry of `SECRETS_VAULT.*` error codes thrown by the vault-backed `SecretsLike` adapter and its watcher / all…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — Frozen registry of `SECRETS_VAULT.*` error codes thrown by the vault-backed `SecretsLike` adapter and its watcher / all…
|
||||
- [from-vault](./src/from-vault/from-vault.md) — Bridge a live `@suaveplan/vault.Vault` instance to the structural `VaultSecretsBackend` the adapter consumes. The bridg…
|
||||
- [from-vault](./src/from-vault/from-vault.md) — Bridge a live `@suaveplan/vault.Vault` instance to the structural `VaultSecretsBackend` the adapter consumes. The bridg…
|
||||
- [otel](./src/otel/instruments.md) — OpenTelemetry instrument bundle for the vault-backed secrets adapter. Owns the three counters the package emits and a l…
|
||||
- [instruments](./src/otel/instruments.md) — OpenTelemetry instrument bundle for the vault-backed secrets adapter. Owns the three counters the package emits and a l…
|
||||
- [types](./src/types/types.md) — Structural type contracts the vault-backed secrets adapter consumes. Decoupling the adapter from the concrete `@suavepl…
|
||||
- [types](./src/types/types.md) — Structural type contracts the vault-backed secrets adapter consumes. Decoupling the adapter from the concrete `@suavepl…
|
||||
- [watcher](./src/watcher/watcher.md) — Live subscription that bridges the vault's `secret.updated` events onto the host event bus as `secret.changed`. Plugins…
|
||||
- [watcher](./src/watcher/watcher.md) — Live subscription that bridges the vault's `secret.updated` events onto the host event bus as `secret.changed`. Plugins…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Modules
|
||||
|
||||
- `./adapter` — `createVaultSecretsAdapter({ vault, allowlist })`
|
||||
factory. Returns a `VaultSecretsAdapter` (extends `SecretsLike`)
|
||||
enforcing the per-plugin allow-list BEFORE every read, raising
|
||||
`SECRETS_VAULT.VAULT_LOCKED` on a locked vault without plaintext
|
||||
fallback, wrapping unexpected backend throws as
|
||||
`SECRETS_VAULT.VAULT_UNAVAILABLE`, emitting
|
||||
`api.secrets.reads_total{backend="vault"}` on success.
|
||||
- `./allowlist` — `createAllowlist({ names })`. Zod-validated factory
|
||||
returning a frozen `Allowlist` (`isAllowed`, `assertAllowed`,
|
||||
`names`). Compiled into an O(1) `Set` for the hot read path.
|
||||
Shared between the adapter and the watcher.
|
||||
- `./watcher` — `createSecretsVaultWatcher({ vault, allowlist, bus })`.
|
||||
Subscribes once to the vault's `secret.updated` channel, forwards
|
||||
allow-listed updates onto the host bus as `secret.changed` carrying
|
||||
ONLY `{ name }` (value is never broadcast). Idempotent `dispose()`
|
||||
with an in-listener disposed-flag guard that survives lazy backend
|
||||
unsubscribes.
|
||||
- `./error-codes` — `SECRETS_VAULT_CODES` registry: `NOT_FOUND`,
|
||||
`NOT_ALLOWED`, `VAULT_LOCKED`, `VAULT_UNAVAILABLE`, `WRITE_FAILED`,
|
||||
`INVALID_OPTIONS`. `SecretsVaultError` subclass with stable
|
||||
`error.name === "SecretsVaultError"`.
|
||||
- `./otel` — `getSecretsVaultInstruments()` / `resetSecretsVaultInstruments()`.
|
||||
Counters: `api.secrets.reads_total{backend}`,
|
||||
`api.secrets.writes_total{op}`,
|
||||
`api.secrets.watcher_emits_total`.
|
||||
- `./types` — `VaultSecretsBackend`, `VaultSecretListener`,
|
||||
`VaultUnsubscribe`, `SecretChangedPayload`. Structural surface the
|
||||
adapter consumes — decoupled from `@suaveplan/vault.Vault` so tests
|
||||
can wire deterministic backends.
|
||||
- `./from-vault` — `fromVault(vault)` bridges a live
|
||||
`@suaveplan/vault.Vault` instance to the structural backend.
|
||||
Secrets are stored as items with `category === "secret"` and a
|
||||
single concealed field `"value"` so the underlying field-encryption
|
||||
layer covers them at rest. In-bridge `secret.updated` fan-out fires
|
||||
on every mutation that goes through the bridge.
|
||||
|
||||
## Quickstart
|
||||
|
||||
```ts
|
||||
import {
|
||||
createSecretsVaultWatcher,
|
||||
createVaultSecretsAdapter,
|
||||
fromVault,
|
||||
} from "@suaveplan/api-secrets-vault";
|
||||
import { EventBus } from "@suaveplan/events";
|
||||
import { Vault } from "@suaveplan/vault";
|
||||
|
||||
const vault = Vault.open(vaultConfig);
|
||||
await vault.unlock(password, secretKey);
|
||||
|
||||
const backend = fromVault(vault);
|
||||
const adapter = createVaultSecretsAdapter({
|
||||
vault: backend,
|
||||
allowlist: ["STRIPE_SECRET_KEY", "JWT_SIGNING_KEY"],
|
||||
});
|
||||
|
||||
const stripeKey = await adapter.require("STRIPE_SECRET_KEY");
|
||||
|
||||
const hostBus = new EventBus<{ "secret.changed": { name: string } }>();
|
||||
const watcher = createSecretsVaultWatcher({
|
||||
vault: backend,
|
||||
allowlist: adapter.allowlist,
|
||||
bus: hostBus,
|
||||
});
|
||||
hostBus.on("secret.changed", (p) => {
|
||||
console.log(`refresh ${p.name}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Conformance
|
||||
|
||||
Passes `runSecretsConformance` from `@suaveplan/api-secrets/testing` —
|
||||
see `src/adapter/adapter.spec-conformance.test.ts`. The
|
||||
`tests/integration.test.ts` suite drives a real
|
||||
`@suaveplan/vault.Vault` end-to-end with Argon2id parameters set to
|
||||
the minimum viable values so the lifecycle fits inside vitest's 60s
|
||||
timeout. No mocks.
|
||||
|
||||
Reference in New Issue
Block a user