# `@suaveplan/api-graphql-ui-altair` — Altair UI mount at `/graphql/ui` **Package**: `@suaveplan/api-graphql-ui-altair` **Status**: PROPOSED **Type**: new-package **Stage**: 7 (GraphQL + WebSocket / SSE engines) — see umbrella `openspec/changes/api-plugin-foundation/` ## Why Per design doc §10.6 Altair is the explicit alternative GraphQL UI alongside GraphiQL: "GET /graphql/ui — GraphiQL (or Altair via `api-graphql-ui-altair`)". Altair has a richer feature set — environment management, response history, query-saving collections — preferred by teams who do heavy in-browser API development. Operators wire one OR the other; the engine adapters (`api-graphql-yoga`, `api-graphql-apollo`) return 404 at `/graphql/ui` when neither is wired. This package is the structural sibling of `api-graphql-ui-graphiql` — same plugin shape, same mount semantics, different bundled UI. ## What changes Net-new package depending on `@suaveplan/api-types`, `@suaveplan/api-router`, `@suaveplan/api-graphql`, `@suaveplan/error`, `@suaveplan/logger`, and `altair-static` (peer; bundled assets). - ADDED: `createAltairUi(opts)` — factory returning a `RouterPlugin` value mounting `GET `. - ADDED: `AltairUiOptions` — Zod-validated config: `uiEndpoint` (default `/graphql/ui`), `graphqlEndpoint` (default `/graphql`), `subscriptionEndpoint` (default `/graphql/ws`), `theme` (`"light"` | `"dark"` | `"system"`), `initialQuery`, `initialHeaders`, `initialEnvironments` (Altair-specific feature). - ADDED: Static-asset serving from package's own `dist/assets/` (no CDN). - ADDED: CSP headers — same posture as the GraphiQL variant. - ADDED: `GRAPHQL_UI_ALTAIR_*` error codes: `GRAPHQL_UI_ALTAIR_ASSET_MISSING`, `GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION`. - ADDED: OTel instrument: `graphql.ui.requests_total { ui: "altair" }` — same metric name as the GraphiQL variant, only the label differs, so dashboards can sum across both UIs. ## Out of scope - A CDN-fetched Altair variant. - The GraphiQL alternative. - Co-mounting both UIs on the same path — host config refuses if both are wired against the same `uiEndpoint`. ## Source mapping Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`. | Requirement | Design doc section | |---|---| | Altair as alternative at `/graphql/ui` | §10.6 | | UI as opt-in, separate package | §10.6 | | Bundled assets only | §10.6 + foundation security posture | ## Verification approach - `src/factory/create-altair-ui.test.ts` — factory returns `RouterPlugin`; HTML response embeds configured endpoints; CSP header. - `src/factory/create-altair-ui.spec-conformance.test.ts` — same conformance shape as the GraphiQL spec — endpoint mount + embedded-config. - `src/assets/assets.test.ts` — no external host references; missing asset raises `GRAPHQL_UI_ALTAIR_ASSET_MISSING`. - `tests/integration/altair-end-to-end.test.ts` (30000ms) — boots `api-graphql-yoga` + this UI plugin, Playwright opens `/graphql/ui`, runs a query, asserts results render. ## Migration impact None — additive only.