# SPEC-009: Feature-Rich, Fully-Documented Management API **Status:** Proposed **Priority:** P2 **Requested By:** Mike (2026-05-30) **Estimated Effort:** Large ## Overview Make GuruConnect fully operable by API: nearly anything a tech can do in the console should be callable programmatically, against a **documented, discoverable, versioned** REST API with first-class auth for automation. Today the surface is decent but **undocumented** (no OpenAPI/Swagger), authable only by a 24h dashboard JWT or agent keys (no automation tokens), and has UI-only gaps. Success = an OpenAPI spec + browsable docs cover every public endpoint, a scripted client can authenticate with a long-lived revocable token and perform every management action the dashboard exposes, and new endpoints are documented by construction. This is the GuruConnect half of a cross-product request; the GuruRMM side is **guru-rmm SPEC-019**. Keep auth model, error envelope, pagination, and doc conventions aligned across both Rust/Axum products. ## Current state (researched) - **No API documentation tooling** — no `utoipa`/`openapi`/Swagger/Redoc anywhere (`server/Cargo.toml`, `server/src/`). The contract lives only in `main.rs` route registrations and handler code. - **Reasonable but partial surface:** auth, users (CRUD + client access), support codes, sessions (list/get/disconnect/viewer-token), machines (list/get/delete/history/keys), releases (CRUD), downloads, version. Several actions are dashboard-only or being added piecemeal (session purge/bulk → SPEC-004, machine search → SPEC-006, installer build → SPEC-007). - **Auth is dashboard/agent-shaped, not automation-shaped:** JWT bearer (24h, `server/src/auth/mod.rs:101`) for humans + agent/site keys for agents. **No long-lived, revocable, scoped API token** for external scripts/integrations. - **No stable error contract** until SPEC-008 lands (two envelopes today). ## Scope ### Included in v1 - **API completeness audit + gap-fill.** Enumerate every console action; ensure each has a documented public endpoint. Fill gaps (e.g. anything currently done only via in-memory state or dashboard-special handlers). The in-flight specs (SPEC-004 purge/bulk, SPEC-006 search, SPEC-007 installer build) register under this umbrella. - **OpenAPI 3.x, generated from code.** Adopt `utoipa` (Axum-native: derive schemas from handler/DTO annotations) → serve `GET /api/openapi.json` + interactive docs (Swagger UI or Redoc) at `/api/docs`. Document auth, every endpoint, request/response schemas, the SPEC-008 error envelope, and examples. Docs generated by construction so they can't drift. - **Programmatic API tokens.** Long-lived, **revocable, scoped** user/integration tokens (PAT-style) distinct from the 24h dashboard JWT and from agent keys: create/list/revoke in the dashboard, hashed at rest, last-used tracking, optional expiry, scopes (read-only vs. read-write, resource-scoped). Accepted as `Authorization: Bearer` on all `/api/*` like the JWT. - **Consistency conventions:** uniform pagination (`limit`/`offset` or cursor) + filtering + sorting on list endpoints; consistent resource naming; documented HTTP status usage (pairs with SPEC-008's structured errors). - **Versioning policy** for the public API (header or path), and a documented relationship to the ADR-001 `/api/integration/v1/` GuruRMM contract (that narrow, semver'd contract stays authoritative for its surface; this is the broader operator API). ### Explicitly out of scope - GraphQL or gRPC public API — REST + OpenAPI only. - SDK/client-library generation — OpenAPI enables it later; not built here. - Webhooks/event subscriptions — separate feature. - Re-speccing the integration contract — owned by `specs/native-remote-control/`. ## Architecture - **Relay-server:** add `utoipa` derives to handler DTOs (`server/src/api/*`), an `ApiDoc` aggregator, and routes for `openapi.json` + docs UI (`server/src/main.rs`). New `api_tokens` module + middleware that accepts either a JWT or a valid API token (`server/src/auth/`). Gap endpoints added to the relevant `api/*` modules. - **DB:** `connect_api_tokens` table (id, user_id, name, token_hash, scopes, created_at, last_used_at, expires_at, revoked_at) + migration. Mirrors the existing `connect_agent_keys`/`machine_keys` hashing pattern. - **Dashboard:** a "API Tokens" settings page (create → show-once → list/revoke) and a link to `/api/docs`. - **Protobuf:** none. ## Implementation details - Files to touch: `server/Cargo.toml` (`utoipa`, `utoipa-swagger-ui`/`utoipa-redoc`); `server/src/api/*.rs` (DTO `#[derive(ToSchema)]` + `#[utoipa::path]` annotations); `server/src/main.rs` (docs routes; token-aware auth layer); `server/src/auth/` (API-token verification beside JWT, `mod.rs:101`); `server/src/db/api_tokens.rs` (new) + migration `connect_api_tokens`; dashboard API-tokens settings page. - Reuse the agent-key hashing/verification approach for token storage; never store plaintext. ## Security considerations - **Tokens are credentials:** hash at rest, show plaintext once, support revoke + expiry + scopes; log creation/revocation to `events`; rate-limit token auth like login. - **Scope enforcement:** read-only tokens cannot mutate; resource-scoped tokens limited to their resources; admin-only actions require an admin-scoped token. - **Docs exposure:** `/api/docs` describes the API but must not leak secrets or be a CSRF/SSRF vector; gate write-trying "try it out" behind auth; consider auth-gating the docs UI in production. - **No internal leakage** in documented error examples (pairs with SPEC-008). - **Auth uniformity:** the token path goes through the same guards as JWT so no endpoint is accidentally left token-bypassable. ## Testing strategy - **Unit:** OpenAPI spec builds and validates; every registered route appears in it (a test that diffs the router against the spec catches undocumented endpoints). Token verify (valid/expired/revoked/wrong-scope). - **Integration:** a scripted client authenticates with an API token and performs a full CRUD lifecycle on machines/sessions/users/codes; read-only token is denied writes; the route↔spec parity test fails if a new endpoint lacks annotation. - **Manual:** open `/api/docs`, exercise representative calls with a created token; confirm examples match real responses. ## Effort estimate & dependencies - **Size: Large.** Annotating the full surface for OpenAPI + the API-token subsystem + the gap audit is broad, though each handler annotation is mechanical. - **Depends on:** **SPEC-008** for the documented error envelope (document it once, right). Composes with SPEC-004/006/007 (their new endpoints land documented). Independent of the ADR-001 integration contract but should cross-reference it. - **Unblocks:** automation/scripting against GuruConnect, future SDKs, and third-party integrations; a self-documenting API that stays current. ## Open questions 1. **Docs UI in production** — public, or auth-gated? Proposed: spec JSON public, "try it out" + UI auth-gated. 2. **Token scopes granularity** — coarse (read/write/admin) for v1 vs. per-resource scopes? Proposed: coarse first, structure for finer later. 3. **Versioning mechanism** — path (`/api/v1`) vs. header; and do we retrofit existing `/api/*` as v1 or leave unversioned with v1 as an alias? 4. **utoipa vs. hand-maintained spec** — confirm utoipa (codegen, drift-proof) over a hand-written YAML.