Files
suaveplan/openspec/changes/api-plugin-foundation/proposal.md
2026-05-27 23:29:53 +01:00

163 lines
14 KiB
Markdown

# API Plugin Foundation
**Package**: `@suaveplan/api-*` (umbrella across the foundation family — see Stage breakdown below)
**Status**: PROPOSED
**Type**: new-package (umbrella)
## Why
Genesis currently has no API host. `packages/api/` contains only `api-schemas` (Zod request/response shapes) and `sitemap` (XML utility). Every plugin we ship next — billing, users, search, files, vault-API, drive-API, network-API, auth-API — needs the same scaffolding: lifecycle, routes, multi-tenancy, hot reload, observability, error contracts. Building that scaffolding once, at enterprise quality, is what unblocks every subsequent plugin.
This umbrella change captures the foundation as a single coherent design. The full design lives at `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md` (committed under the brainstorming flow, reviewed by `spec-document-reviewer`, approved by the user with explicit "no MVP scoping — enterprise grade only" direction). This proposal is the OpenSpec rollup that the per-package child changes hang off.
## What changes
Eight implementation stages, each landing a coherent slice. Every stage's packages are independent OpenSpec changes — proposals + tasks + spec deltas live under `openspec/changes/api-<package-name>/`. This umbrella tracks the rollup state.
### Stage 1 — Contracts
Zero-runtime, types-only packages plus the Zod schemas + abstract interfaces every downstream package builds against. Stage gates: nothing in Stages 2-8 starts until all Stage-1 packages are merged.
| Change ID | Package | Role |
|---|---|---|
| `api-types` | `@suaveplan/api-types` | Every cross-package interface (Principal, TenantId, PluginContext, RequestContext, Router contract, CapabilityMap, Manifest types, …). Zero runtime, zero deps. |
| `api-manifest` | `@suaveplan/api-manifest` | Zod schema for `plugin.json`, validators, dep-graph resolver, capability resolver. |
| `api-router` | `@suaveplan/api-router` | Abstract Router / Middleware / Request / Response types; composition (prefix mounting, middleware chains, blue-green slot management interfaces). |
| `api-graphql` | `@suaveplan/api-graphql` | Abstract Registry / Resolver / LoaderFactory; federation contract. |
| `api-ws` | `@suaveplan/api-ws` | Abstract WebSocket engine: ConnectionRegistry, SubscriptionBus, broker contract. |
| `api-sse` | `@suaveplan/api-sse` | Abstract SSE engine; Last-Event-Id resume contract. |
| `api-auth` | `@suaveplan/api-auth` | Session-resolver abstract: Request → Principal \| null. |
| `api-auth-provider` | `@suaveplan/api-auth-provider` | Auth-provider abstract: account lifecycle routes (login/register/oauth/verify-email). |
| `api-secrets` | `@suaveplan/api-secrets` | Abstract Secrets contract. |
| `api-tenant-provider` | `@suaveplan/api-tenant-provider` | Abstract "list tenants" contract. |
| `api-loader` | `@suaveplan/api-loader` | Module resolver interface + `RollbackJournal` interface (impls in Stage 2). |
| `api-lifecycle` | `@suaveplan/api-lifecycle` | Lifecycle FSM definition (states + transitions, no concrete plugin yet). |
### Stage 2 — Host orchestration core
| Change ID | Package | Role |
|---|---|---|
| `api-host` | `@suaveplan/api-host` | `CapabilityRegistry`, `definePlugin`, lifecycle driver, hot-reload primitive (blue-green + RollbackJournal), plugin ledger, admin API, error boundary, OTel scaffolding. |
The host metadata DB schema (tables `suaveplan_plugin_ledger`, `suaveplan_rollback_journal`, `suaveplan_saga_instances`, `suaveplan_saga_steps`, `suaveplan_2pc_transactions`) ships with `api-host`'s migrations.
### Stage 3 — Worker isolation infrastructure
| Change ID | Package | Role |
|---|---|---|
| `api-worker-rpc` | `@suaveplan/api-worker-rpc` | `MessagePort` protocol; correlation; AbortSignal forwarding; streaming; error reconstruction; backpressure. |
| `api-worker-host` | `@suaveplan/api-worker-host` | Worker-side runtime hosting one plugin; accepts capability proxies. |
| `api-router-worker-bridge` | `@suaveplan/api-router-worker-bridge` | Request/response marshalling main↔worker. |
| `api-db-worker-proxy` | `@suaveplan/api-db-worker-proxy` | Worker-side proxy → main-process DB adapter. |
| `api-events-worker-proxy` | `@suaveplan/api-events-worker-proxy` | Worker-side proxy → main-process `@suaveplan/events` bus. |
| `api-secrets-worker-proxy` | `@suaveplan/api-secrets-worker-proxy` | Worker-side proxy → main-process secrets adapter. |
| `api-jobs-worker-proxy` | `@suaveplan/api-jobs-worker-proxy` | Worker-side proxy → main-process job scheduler. |
| `api-logger-worker-proxy` | `@suaveplan/api-logger-worker-proxy` | Worker-side proxy → main-process logger. |
Conformance gate: every Stage-4 capability MUST pass its conformance suite under both direct and worker-proxy surfaces before Stage 4 can be marked complete.
### Stage 4 — Base adapters (both isolation surfaces)
| Change ID | Package | Role |
|---|---|---|
| `api-router-hono` | `@suaveplan/api-router-hono` | Hono router adapter. |
| `api-auth-jwt` | `@suaveplan/api-auth-jwt` | Session resolver via `@suaveplan/jws` against JWKS/static key. |
| `api-auth-anonymous` | `@suaveplan/api-auth-anonymous` | Always returns null principal. |
| `api-auth-bearer-static` | `@suaveplan/api-auth-bearer-static` | Service-to-service static-token resolver. |
| `api-tenant-resolver-jwt` | `@suaveplan/api-tenant-resolver-jwt` | Reads `ctx.principal.tenantId`. |
| `api-secrets-env` | `@suaveplan/api-secrets-env` | `process.env` / `Bun.env` source. |
| `api-db-adapter` | `@suaveplan/api-db-adapter` | Bridge from `@suaveplan/db` → plugin `DatabaseLike` (Postgres/SQLite/Surreal). |
| `api-tenant-provider-memory` | `@suaveplan/api-tenant-provider-memory` | In-memory reference + test impl. |
### Stage 5 — Testing kit + conformance suites
| Change ID | Package | Role |
|---|---|---|
| `api-router-in-memory` | `@suaveplan/api-router-in-memory` | In-memory router for tests. |
| `api-auth-static-principal` | `@suaveplan/api-auth-static-principal` | Test auth adapter — supplies a fixed `Principal`. |
| `api-db-sqlite-memory` | `@suaveplan/api-db-sqlite-memory` | In-memory SQLite DB adapter for tests. |
| `api-events-in-memory` | `@suaveplan/api-events-in-memory` | In-memory wrapper over `@suaveplan/events` for tests. |
| `api-ws-broker-in-memory` | `@suaveplan/api-ws-broker-in-memory` | In-memory broker for single-host + tests. |
| `api-host-testing-kit` | `@suaveplan/api-host/testing` (subpath) | `createTestHost` + `await using` lifecycle; not a separate package, ships as a subpath of `api-host` populated in this stage. Tracked here for sequencing. |
Conformance suites under `./testing` subpaths on every abstract package: `runRouterConformance`, `runAuthSessionConformance`, `runTenantResolverConformance`, `runDbAdapterConformance`, `runSecretsConformance`, `runEventsBusConformance`, `runJobsConformance`, `runGraphqlEngineConformance`, `runWsEngineConformance`, `runSseEngineConformance`, `runSagaParticipantConformance`, `run2pcParticipantConformance`.
### Stage 6 — Distributed transactions + Better-Auth
| Change ID | Package | Role |
|---|---|---|
| `api-saga` | `@suaveplan/api-saga` | Saga orchestrator; manifest-declared workflows; compensable steps; persisted state. |
| `api-2pc` | `@suaveplan/api-2pc` | Two-phase commit coordinator (Postgres `PREPARE TRANSACTION`). |
| `api-auth-better-auth` | `@suaveplan/api-auth-better-auth` | Better-Auth wraps both `auth.session` and `auth.provider`; owns its own DB connection. |
### Stage 7 — GraphQL + WebSocket / SSE engines
| Change ID | Package | Role |
|---|---|---|
| `api-graphql-yoga` | `@suaveplan/api-graphql-yoga` | GraphQL Yoga engine adapter (default). |
| `api-graphql-apollo` | `@suaveplan/api-graphql-apollo` | Apollo engine adapter. |
| `api-graphql-ui-graphiql` | `@suaveplan/api-graphql-ui-graphiql` | GraphiQL UI at `/graphql/ui` (default). |
| `api-graphql-ui-altair` | `@suaveplan/api-graphql-ui-altair` | Altair UI alternative. |
| `api-ws-bun` | `@suaveplan/api-ws-bun` | `Bun.serve` WebSocket engine. |
| `api-ws-broker-redis` | `@suaveplan/api-ws-broker-redis` | Cross-host broadcast via Redis pub/sub. |
| `api-sse-bun` | `@suaveplan/api-sse-bun` | `Bun.serve` SSE engine. |
### Stage 8 — Sandboxing + cross-cutting middleware + authoring helpers
| Change ID | Package | Role |
|---|---|---|
| `api-sandbox-permissions` | `@suaveplan/api-sandbox-permissions` | Permission model (fs/fetch allowlist, CPU/memory/wall-clock budgets). |
| `api-sandbox-vm` | `@suaveplan/api-sandbox-vm` | `vm.Context` based sandbox for non-WASM. |
| `api-sandbox-wasm` | `@suaveplan/api-sandbox-wasm` | WASM/WASI target host. |
| `api-rbac` | `@suaveplan/api-rbac` | Scope + rule-based authz; reuses `@suaveplan/rules`. |
| `api-rate-limit` | `@suaveplan/api-rate-limit` | Per-tenant / per-principal / per-IP token bucket. |
| `api-openapi` | `@suaveplan/api-openapi` | Emission from manifest + route schemas; via `@asteasolutions/zod-to-openapi`. |
| `api-openapi-ui-scalar` | `@suaveplan/api-openapi-ui-scalar` | Default `/docs` UI. |
| `api-openapi-ui-swagger` | `@suaveplan/api-openapi-ui-swagger` | Swagger UI alternative. |
| `api-openapi-ui-redoc` | `@suaveplan/api-openapi-ui-redoc` | ReDoc alternative. |
| `api-audit-stdout` | `@suaveplan/api-audit-stdout` | Default audit sink (containers redirect). |
| `api-audit-file` | `@suaveplan/api-audit-file` | File-rotating audit sink. |
| `api-auth-rolled` | `@suaveplan/api-auth-rolled` | Reference rolled-our-own provider via `@suaveplan/crypto-rsa` + `webauthn` + `otp` + `srp` + `hibp` + `password-strength` + `password-generator`. |
| `api-secrets-bun` | `@suaveplan/api-secrets-bun` | `Bun.secrets` (OS keystore). |
| `api-secrets-vault` | `@suaveplan/api-secrets-vault` | `@suaveplan/vault` source. |
| `api-tenant-resolver-header` | `@suaveplan/api-tenant-resolver-header` | Header-based tenant resolver. |
| `api-tenant-resolver-subdomain` | `@suaveplan/api-tenant-resolver-subdomain` | Subdomain tenant resolver. |
| `api-plugin-example` | `@suaveplan/api-plugin-example` | Reference plugin exercising every capability under every isolation tier. Load-bearing integration test. |
| `create-suaveplan-plugin` | `create-suaveplan-plugin` | Scaffold via `@suaveplan/installer`. |
## Out of scope
These are explicit non-goals — captured in the design doc Section 14 and repeated here so per-package changes don't re-litigate them:
- **Multi-region active-active host coordination** — cross-region consistency of the plugin ledger / saga state / 2PC coordinator is a separate design. Foundation assumes one host per region.
- **Plugin marketplace / discovery service** — foundation supports third-party plugins via npm; a curated marketplace + signing infra is its own product.
- **Built-in feature-flag system** — `@suaveplan/feature-flags` already exists; plugins use it via the standard dep.
- **Reversible migrations** — forward-only is the model. Plugin authors can write reversible if they want; host doesn't enforce.
## Source mapping
This is a genesis-original family. No `../suaveplan/enterprise/<pkg>.md` source. The single source of design intent is `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md` (commits `1445b41e`, `e2051e07`, `41419bae`, `1eb2569f`).
Per-package proposals link back to this design doc by section reference (e.g. "implements §5 Capability injection"). No package re-derives the design intent — each one specs ONLY its own surface and references the umbrella for cross-cutting concerns.
## Verification approach
This umbrella has no test files of its own — verification is delegated to per-package changes. The umbrella tracks stage-level acceptance:
- Stage 1 complete WHEN every Stage-1 child change is archived AND `bun openspec validate` passes for every spec delta.
- Stage 2 complete WHEN Stage 1 ✓ AND `api-host` is archived AND boots empty (`bun examples/api-host-empty.ts` returns 200 on `/internal/livez`).
- Stage 3 complete WHEN Stage 2 ✓ AND `api-worker-rpc` conformance suite passes against a trivial proxy capability under both direct and worker surfaces.
- Stage 4 complete WHEN Stage 3 ✓ AND every Stage-4 adapter passes its conformance suite under both surfaces.
- Stage 5 complete WHEN Stage 4 ✓ AND `createTestHost` boots `api-plugin-example`'s skeleton against the in-memory adapter set with 100% conformance pass.
- Stage 6 complete WHEN Stage 5 ✓ AND a multi-step saga in `api-plugin-example` runs end-to-end including a compensation path, AND `api-2pc` commits + aborts an integration scenario against real Postgres.
- Stage 7 complete WHEN Stage 6 ✓ AND `api-plugin-example` mounts a federated GraphQL schema across two plugins AND publishes a tenant-scoped WebSocket broadcast.
- Stage 8 complete WHEN Stage 7 ✓ AND `api-plugin-example` deploys cleanly under every isolation tier including `sandboxed` with WASM, AND `create-suaveplan-plugin <name>` scaffolds a working package whose tests pass first time.
Foundation complete when Stage 8 ✓ AND the reference plugin's full integration test (Section 13.5 of the design doc) is green in CI.
## Migration impact
- `packages/api/api-schemas` and `packages/api/sitemap` keep working as pure libraries during the foundation roll-out. After Stage 8 ships, each can optionally migrate to live as a plugin if it wants lifecycle hooks; if not, they stay as pure libs consumed by plugins.
- No other monorepo package depends on the foundation today — the foundation is additive.
- The host metadata DB introduces a new SQLite file at `./suaveplan-host.db` by default. Production deployments will need a host-config decision (SQLite vs Postgres vs Surreal) but no existing data is touched.