Initial Commit
This commit is contained in:
136
packages/api-stack/api/api-secrets-bun/README.md
Normal file
136
packages/api-stack/api/api-secrets-bun/README.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# @suaveplan/api-secrets-bun — SecretsLike adapter backed by Bun.secrets (macOS Keychain / Linux libsecret / Windows Credential Manager). Per-plugin allowlist enforcement, locked-keystore detection, idempotent dispose, OTel api.secrets.reads_total{backend="bun"} counter.
|
||||
|
||||
Stage-8 `SecretsLike` adapter backed by `Bun.secrets` (macOS Keychain,
|
||||
Linux libsecret, Windows Credential Manager) with per-plugin allow-list
|
||||
enforcement, OS-keystore failure-mode classification, and an idempotent
|
||||
lifecycle.
|
||||
|
||||
The right default for local-dev and single-host operator setups: secrets
|
||||
never touch disk in plaintext and rotate via the OS UI.
|
||||
|
||||
Per-plugin allow-list keeps each plugin scoped to its manifest-declared
|
||||
secrets — a leaked plugin cannot enumerate or read any keystore entry
|
||||
beyond what its `manifest.secrets[]` declared. Values never appear in
|
||||
logs or errors — only names.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [adapter](./src/adapter/adapter.md) — The adapter module builds a concrete `SecretsLike` over the `Bun.secrets` API — the cross-platform wrapper Bun ships ar…
|
||||
- [adapter](./src/adapter/adapter.md) — The adapter module builds a concrete `SecretsLike` over the `Bun.secrets` API — the cross-platform wrapper Bun ships ar…
|
||||
- [allowlist](./src/allowlist/allowlist.md) — The allow-list module encapsulates the per-plugin scoping rule that defines the package's security boundary: a plugin c…
|
||||
- [allowlist](./src/allowlist/allowlist.md) — The allow-list module encapsulates the per-plugin scoping rule that defines the package's security boundary: a plugin c…
|
||||
- [descriptor-builder](./src/descriptor-builder/descriptor-builder.md) — This module produces the `SecretDescriptor[]` shape the plugin manifest emits in its `secrets[]` block from a compact a…
|
||||
- [descriptor-builder](./src/descriptor-builder/descriptor-builder.md) — This module produces the `SecretDescriptor[]` shape the plugin manifest emits in its `secrets[]` block from a compact a…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable `SECRETS_BUN.*` code registry and the `SecretsBunError` subclass thrown by every failure pa…
|
||||
- [error-codes](./src/error-codes/error-codes.md) — This module owns the stable `SECRETS_BUN.*` code registry and the `SecretsBunError` subclass thrown by every failure pa…
|
||||
- [otel](./src/otel/instruments.md) — This module owns the OpenTelemetry instrument bundle the `Bun.secrets`-backed adapter emits and a lazy cache that re-bi…
|
||||
- [instruments](./src/otel/instruments.md) — This module owns the OpenTelemetry instrument bundle the `Bun.secrets`-backed adapter emits and a lazy cache that re-bi…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/api-secrets-bun
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { createBunSecretsAdapter } from "@suaveplan/api-secrets-bun";
|
||||
|
||||
const adapter = createBunSecretsAdapter({
|
||||
service: "billing-plugin",
|
||||
allowlist: ["STRIPE_SECRET_KEY", "JWT_SIGNING_KEY"],
|
||||
});
|
||||
|
||||
const key = await adapter.secrets.require("STRIPE_SECRET_KEY");
|
||||
```
|
||||
|
||||
### Manifest-driven allow-list
|
||||
|
||||
The companion `buildBunSecretDescriptors` builder lets a plugin derive
|
||||
its `manifest.secrets[]` and the adapter's `allowlist` from a single
|
||||
declaration so they cannot drift:
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildBunSecretDescriptors,
|
||||
descriptorAllowlist,
|
||||
createBunSecretsAdapter,
|
||||
} from "@suaveplan/api-secrets-bun";
|
||||
|
||||
const descriptors = buildBunSecretDescriptors([
|
||||
"STRIPE_SECRET_KEY",
|
||||
{ name: "OPTIONAL_WEBHOOK_KEY", required: false },
|
||||
]);
|
||||
|
||||
const adapter = createBunSecretsAdapter({
|
||||
service: "billing-plugin",
|
||||
allowlist: descriptorAllowlist(descriptors),
|
||||
});
|
||||
|
||||
// `descriptors` goes on the plugin manifest's `secrets[]`.
|
||||
```
|
||||
|
||||
## Capability
|
||||
|
||||
This package declares `secrets.bun`. Plugins that specifically require
|
||||
the Bun keystore (e.g. desktop runtimes that bundle Bun) can pin the
|
||||
backend in `requiredCapabilities`; otherwise the host's `host.secrets.backend`
|
||||
config selects this adapter alongside the abstract `secrets` capability.
|
||||
|
||||
## Failure modes
|
||||
|
||||
Distinguishes:
|
||||
|
||||
- `SECRETS_BUN.NOT_FOUND` — permitted name; OS keystore returned `null`.
|
||||
`get()` resolves to `undefined`; `require()` throws.
|
||||
- `SECRETS_BUN.NOT_ALLOWED` — name not in the plugin's allow-list.
|
||||
Raised BEFORE any keystore round-trip.
|
||||
- `SECRETS_BUN.KEYSTORE_LOCKED` — OS keystore is locked. The adapter
|
||||
does NOT fall back silently — operator must unlock.
|
||||
- `SECRETS_BUN.KEYSTORE_UNAVAILABLE` — `Bun.secrets` is missing
|
||||
(running under Node without the shim), the OS keystore daemon is not
|
||||
running, or the adapter has been `dispose()`d.
|
||||
- `SECRETS_BUN.WRITE_FAILED` — `set`/`delete` failed at the OS layer.
|
||||
Always wraps the underlying error as `cause`.
|
||||
- `SECRETS_BUN.INVALID_OPTIONS` — adapter construction failed Zod
|
||||
validation.
|
||||
|
||||
## OpenTelemetry
|
||||
|
||||
Emits:
|
||||
|
||||
- `api.secrets.reads_total{backend="bun"}` — successful reads.
|
||||
- `api.secrets.not_found_total{backend="bun"}` — permitted-but-absent.
|
||||
- `api.secrets.denied_total{backend="bun"}` — allow-list rejections.
|
||||
|
||||
Metric names are backend-agnostic so dashboards roll up across every
|
||||
`api.secrets.*` source via the `backend` label.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
`adapter.dispose()` is idempotent — repeat calls are no-ops. Post-dispose
|
||||
reads / writes throw `KEYSTORE_UNAVAILABLE` rather than leaking the
|
||||
keystore reference.
|
||||
|
||||
## Conformance
|
||||
|
||||
Passes `runSecretsConformance` from `@suaveplan/api-secrets/testing`
|
||||
unchanged — see `src/adapter/adapter.spec-conformance.test.ts`.
|
||||
|
||||
## Modules
|
||||
|
||||
| Module | Subpath | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `adapter` | `./adapter` | `createBunSecretsAdapter` factory + types. |
|
||||
| `allowlist` | `./allowlist` | Per-plugin allow-list with `assertAllowed`. |
|
||||
| `descriptor-builder` | `./descriptor-builder` | Compact → `SecretDescriptor[]`. |
|
||||
| `error-codes` | `./error-codes` | `SECRETS_BUN.*` registry + `SecretsBunError`. |
|
||||
| `otel` | `./otel` | OTel instruments + reset hook. |
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user