API v1: public read-only search and watch-safe aggregate stats #109

Closed
opened 2026-08-24 06:44:28 +00:00 by lost-rob0t · 0 comments
lost-rob0t commented 2026-08-24 06:44:28 +00:00 (Migrated from github.com)

Parent work

Part of #58 (versioned/capability-discovered HTTP API), #46 (observability), and #62 (auth + integration hardening). This is one coherent RAGE implementation unit for the API overhaul.

Goal

Expose a deliberately public, read-only API surface suitable for third-party clients and small devices such as watches, without weakening the authenticated mutation/control plane.

Required authority split

Public / no bearer credential required

  • GET /api/v1/search
  • GET /api/v1/stats
  • existing capability/schema discovery routes

Authenticated + authorized only

  • document ingest/create/bulk endpoints
  • target create/dispatch/lifecycle/leases
  • destructive document operations
  • administrative/auth lifecycle operations

Legacy authenticated /search remains intact during migration. Do not make the legacy route an authorization bypass.

Public search contract

  • route is versioned and included in the canonical HTTP contract/OpenAPI/client manifest
  • public search executes under a server-owned synthetic read context, never a client-supplied principal
  • reuse the existing authorization-aware query compiler so dataset/tenant scoping is applied in the backend query before result retrieval
  • public datasets are operator-configurable; wildcard public search is allowed only when explicitly/default-configured for this public-server deployment model
  • callers cannot widen scope with tenant or dataset query parameters
  • bounded result limit (default 25, hard maximum 50)
  • preserve backend paging/bookmark support only if it cannot widen authorization
  • malformed/missing query values return client-safe 4xx responses

Stats contract

GET /api/v1/stats is deliberately small, aggregate-only, and safe to poll from a watch.

Initial response should include at least:

  • service/version
  • generated timestamp
  • total document count
  • aggregate document counts by dtype
  • total target-like documents derived from aggregate counts

Constraints:

  • no document bodies, IDs, credentials, internal hostnames, DB/Rabbit credentials, queue payloads, tenant secrets, or raw exception text
  • no arbitrary view selector or user-controlled design/view names
  • use existing registered CouchDB aggregate views
  • client-safe failure envelope on backend failure
  • response should be compact and cacheable for a short interval if compatible with the existing response middleware

Capability discovery

Extend /api/v1/capabilities so endpoint entries describe their authority (public, authenticated, or privileged scope requirements) and advertise the versioned public search/stats surface without misrepresenting legacy routes.

TDD / RAGE acceptance gates

Tests first. At minimum prove:

  1. unauthenticated GET /api/v1/search?q=... reaches the public route instead of returning 401;
  2. the public search context cannot be widened by caller-provided tenant/dataset values;
  3. public search query generation contains only server-owned public dataset/tenant scopes;
  4. unauthenticated GET /api/v1/stats succeeds and contains only the stable aggregate schema;
  5. unauthenticated target creation and ingest remain 401;
  6. authenticated principals still require targets:dispatch, documents:write, or documents:bulk as appropriate;
  7. /api/v1/capabilities, OpenAPI, and client manifest agree on public vs authenticated authority;
  8. no secret values appear in stats/capability output;
  9. full unit/integration/contract suite passes on the exact candidate SHA.

Do not weaken or skip a failing gate. Merge only an exact-head green candidate.

## Parent work Part of #58 (versioned/capability-discovered HTTP API), #46 (observability), and #62 (auth + integration hardening). This is one coherent RAGE implementation unit for the API overhaul. ## Goal Expose a deliberately public, read-only API surface suitable for third-party clients and small devices such as watches, without weakening the authenticated mutation/control plane. ## Required authority split ### Public / no bearer credential required - `GET /api/v1/search` - `GET /api/v1/stats` - existing capability/schema discovery routes ### Authenticated + authorized only - document ingest/create/bulk endpoints - target create/dispatch/lifecycle/leases - destructive document operations - administrative/auth lifecycle operations Legacy authenticated `/search` remains intact during migration. Do **not** make the legacy route an authorization bypass. ## Public search contract - route is versioned and included in the canonical HTTP contract/OpenAPI/client manifest - public search executes under a **server-owned synthetic read context**, never a client-supplied principal - reuse the existing authorization-aware query compiler so dataset/tenant scoping is applied in the backend query before result retrieval - public datasets are operator-configurable; wildcard public search is allowed only when explicitly/default-configured for this public-server deployment model - callers cannot widen scope with `tenant` or `dataset` query parameters - bounded result limit (default 25, hard maximum 50) - preserve backend paging/bookmark support only if it cannot widen authorization - malformed/missing query values return client-safe 4xx responses ## Stats contract `GET /api/v1/stats` is deliberately small, aggregate-only, and safe to poll from a watch. Initial response should include at least: - service/version - generated timestamp - total document count - aggregate document counts by dtype - total target-like documents derived from aggregate counts Constraints: - no document bodies, IDs, credentials, internal hostnames, DB/Rabbit credentials, queue payloads, tenant secrets, or raw exception text - no arbitrary view selector or user-controlled design/view names - use existing registered CouchDB aggregate views - client-safe failure envelope on backend failure - response should be compact and cacheable for a short interval if compatible with the existing response middleware ## Capability discovery Extend `/api/v1/capabilities` so endpoint entries describe their authority (`public`, `authenticated`, or privileged scope requirements) and advertise the versioned public search/stats surface without misrepresenting legacy routes. ## TDD / RAGE acceptance gates Tests first. At minimum prove: 1. unauthenticated `GET /api/v1/search?q=...` reaches the public route instead of returning 401; 2. the public search context cannot be widened by caller-provided tenant/dataset values; 3. public search query generation contains only server-owned public dataset/tenant scopes; 4. unauthenticated `GET /api/v1/stats` succeeds and contains only the stable aggregate schema; 5. unauthenticated target creation and ingest remain 401; 6. authenticated principals still require `targets:dispatch`, `documents:write`, or `documents:bulk` as appropriate; 7. `/api/v1/capabilities`, OpenAPI, and client manifest agree on public vs authenticated authority; 8. no secret values appear in stats/capability output; 9. full unit/integration/contract suite passes on the exact candidate SHA. Do not weaken or skip a failing gate. Merge only an exact-head green candidate.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/starintel-server#109
No description provided.