From 7ab87384a7a2cce8c9d42c660df92994cb4d7ea6 Mon Sep 17 00:00:00 2001 From: Mike Swanson Date: Sat, 30 May 2026 16:35:57 -0700 Subject: [PATCH] spec: add SPEC-009 feature-rich documented API Everything the console does should be callable by API, documented and discoverable. Adds: OpenAPI 3.x generated from code (utoipa) + Swagger/Redoc at /api/docs (drift-proof, route<->spec parity test); long-lived revocable scoped API tokens (connect_api_tokens, hashed like agent keys) distinct from the 24h dashboard JWT and agent keys; an API-completeness gap audit (folds in SPEC-004/ 006/007 endpoints); consistent pagination/filtering + versioning policy. Today there is zero API doc tooling and no programmatic token. Depends on SPEC-008 for the documented error envelope; distinct from the ADR-001 integration contract. Large. Parallel guru-rmm SPEC-019. Requested by Mike 2026-05-30. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/FEATURE_ROADMAP.md | 1 + .../SPEC-009-feature-rich-documented-api.md | 136 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 docs/specs/SPEC-009-feature-rich-documented-api.md diff --git a/docs/FEATURE_ROADMAP.md b/docs/FEATURE_ROADMAP.md index ebf7161..2923612 100644 --- a/docs/FEATURE_ROADMAP.md +++ b/docs/FEATURE_ROADMAP.md @@ -54,6 +54,7 @@ Bringing GC to parity with GuruRMM's release engineering. Full plan: [SPEC-001]( - [ ] **Universal machine search ("everything is searchable")** — P2 — server-side `?q=` on `/api/machines` matching case-insensitive substring across ALL attributes (OS, logged-on user, external/private IP, company, site, tag, serial, MAC, version, …), pg_trgm GIN-indexed; multi-term AND + optional field-scoped syntax (`os:`, `user:`, `ip:`). Replaces the hostname-only client filter. Depends on SPEC-003 (attrs must be persisted). ([SPEC-006](specs/SPEC-006-universal-machine-search.md)) - [ ] **Managed-agent installer builder ("Build Installer")** — P2 — dashboard wizard to build a pre-labeled persistent-agent installer (Name/Company/Site/Department/Device Type/Tag/Type) with Download / Copy URL / Send Link, reusing the existing embed-config download path; adds department + device_type to EmbeddedConfig/AgentStatus so labels persist at install time. Pairs with revocable per-machine keys; signature-vs-appended-config is the key open question. ([SPEC-007](specs/SPEC-007-managed-agent-installer-builder.md)) - [ ] **Valuable error messages (structured errors + no silent swallows)** — P2 — one structured API error envelope with stable codes + a correlation id that also lands in the logs; contextual tracing on server/agent; sweep the 37 `let _ =` swallows (the pattern that hid the migration-005 bug); dashboard surfaces the real cause + id instead of a generic line. ([SPEC-008](specs/SPEC-008-valuable-error-messages.md)) +- [ ] **Feature-rich, fully-documented management API** — P2 — everything the console can do, callable by API: OpenAPI 3.x generated from code (utoipa) + browsable docs at `/api/docs`, long-lived revocable scoped API tokens (PAT-style, distinct from the 24h JWT + agent keys), an API-completeness gap audit, and consistent pagination/error conventions. Distinct from the ADR-001 RMM integration contract. ([SPEC-009](specs/SPEC-009-feature-rich-documented-api.md)) - [ ] Programmatic session pre-create + viewer-token (integration contract) — P2 ## Security & Infrastructure diff --git a/docs/specs/SPEC-009-feature-rich-documented-api.md b/docs/specs/SPEC-009-feature-rich-documented-api.md new file mode 100644 index 0000000..78b5863 --- /dev/null +++ b/docs/specs/SPEC-009-feature-rich-documented-api.md @@ -0,0 +1,136 @@ +# 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.