Skip to content

Capacity HTTP API

This is the reference for the per-Domain capacity snapshot endpoint GET /v1/domains/{domainId}/capacityGetDomainCapacity. It maps the operation to its OpenAPI schema, the operator authentication seam, the ReBAC oracle-prevention gate, the response shape, and the closed Problem.code taxonomy. The wire-contract origin is api/openapi/plexsphere-v1.yaml; this doc is a map, not a duplicate contract.

The capacity surface is the read side of the platform's capacity-and-scale collector. On a fixed interval the collector samples each Domain's usage against its target on six catalogued axes — Node enrolment, SSE fan-out, Secret Store reads, mediated sessions, observability ingest, and action executions — and records a threshold crossing when a dimension reaches a share of its target. This endpoint returns the latest sampled snapshot: one used / target reading per dimension in canonical order. The Dashboard capacity view and operators reading scale headroom consume the pull. For the operator runbook — the sample interval, the threshold alerts, and the capacity_exceeded refusal contract on the throttled write surfaces — see ../../operations/capacity.md.

Operation

MethodPathOperation IDTagAuthReBAC/authz gateAudit relation
GET/v1/domains/{domainId}/capacityGetDomainCapacitycapacityoperator bearerDomain-read on the addressed Domain, checked before the snapshot readdomain.capacity.read (only on a 403 denial)
  • The authorization gate is checked before the snapshot read. The handler confirms the caller holds Domain-read permission on the addressed Domain before it ever touches the collector, so a caller who lacks the relation cannot use the endpoint as a Domain-id oracle: an unauthorised request and a request for a never-sampled Domain are indistinguishable from the caller's side because the 403 lands first. An unauthorised caller receives 403 with the PermissionDenied shape and reason insufficient_relation. (The OpenAPI 403 prose names the required permission "domain-view"; in the codebase this is the Domain-read relation that GetDomain authorises against.)
  • The audit relation domain.capacity.read is stamped only on the 403 denial — the security-relevant event of a caller attempting to read a Domain's capacity without the relation. A successful read is not audited.
  • The handler ships behind a fail-closed deferred-wiring gate — until the production composition root supplies the collector-backed snapshot provider and the ReBAC checker, every request returns 501 capacity_not_provisioned so log scrapers can alert on the unwired state. The surface is either fully wired or fully off.

Authentication

The operation uses the operator bearer scheme, the same seam every other /v1/domains/{domainId}/... operation authenticates against — not the per-Node NSK scheme the node-facing surfaces use. The middleware resolves the Principal from the presented credential and attaches it to the request context. A request that carries no resolved Principal is refused with 401 unauthenticated before the Domain-read gate runs.

Path parameters

ParameterTypeRequiredNotes
domainId (path)string (uuid)yesThe addressed Domain (UUIDv7). A zero or otherwise invalid UUID is refused with 400 invalid_domain_id.

Success response

A 200 OK carries a DomainCapacitySnapshot — the latest sampled snapshot for the addressed Domain.

FieldTypeMeaning
sampled_atstring (date-time)RFC 3339, the wall-clock instant the collector last sampled the Domain's usage. The snapshot is unavailable (503) until the first sample completes.
dimensionsarray of DomainCapacityDimensionReadingOne reading per catalogued dimension, in the canonical order the Dashboard capacity view renders.

Each DomainCapacityDimensionReading carries five required fields:

FieldTypeMeaning
dimensionCapacityDimension enumThe capacity-and-scale axis this reading covers.
unitCapacityUnit enumThe unit used and target are expressed in.
usednumberLatest sampled usage in the dimension's unit — an absolute tally for count, a sustained rate for the _per_second units.
targetnumberThe per-Domain target in the dimension's unit. A value of 0 means no target is configured.
rationumberused / target, the fraction of the target consumed (0 when no target). A value at or above 0.8 is the threshold the collector records a crossing for.

Dimensions and units

CapacityDimension is a closed enum: nodes, sse_fanout, secret_reads, mediated_sessions, observability_ingest, action_executions. CapacityUnit is a closed enum: count, events_per_second, reads_per_second, bytes_per_second. Each dimension reports in a fixed unit:

dimensionunit
nodescount
sse_fanoutevents_per_second
secret_readsreads_per_second
mediated_sessionscount
observability_ingestbytes_per_second
action_executionscount

Example

The 200 body the spec carries (the six readings in canonical order):

json
{
  "sampled_at": "2026-05-25T10:00:00Z",
  "dimensions": [
    { "dimension": "nodes",                "unit": "count",             "used": 8200,    "target": 10000,   "ratio": 0.82 },
    { "dimension": "sse_fanout",           "unit": "events_per_second", "used": 540,     "target": 1000,    "ratio": 0.54 },
    { "dimension": "secret_reads",         "unit": "reads_per_second",  "used": 3100,    "target": 10000,   "ratio": 0.31 },
    { "dimension": "mediated_sessions",    "unit": "count",             "used": 120,     "target": 500,     "ratio": 0.24 },
    { "dimension": "observability_ingest", "unit": "bytes_per_second",  "used": 2097152, "target": 5242880, "ratio": 0.4  },
    { "dimension": "action_executions",    "unit": "count",             "used": 60,      "target": 1000,    "ratio": 0.06 }
  ]
}

Error taxonomy

Error responses use the shared Problem envelope (application/problem+json); the 403 path uses the PermissionDenied shape. Only the 503 arm carries a Retry-After header. The closed Problem.code set this surface emits:

HTTP statusProblem.codeTriggerRetry-After
400invalid_domain_idPath {domainId} is not a non-zero UUID.no
401unauthenticatedNo resolved Principal — the caller is not authenticated.no
403(PermissionDenied, reason insufficient_relation)Caller lacks Domain-read permission on the addressed Domain; surfaced before the snapshot read so the endpoint is not a Domain-id oracle.no
500(Problem)A snapshot-read fault or an authorization-check fault.no
501capacity_not_provisionedThe capacity snapshot surface is not yet wired in this build (deferred-wiring state).no
503capacity_snapshot_unavailableThe collector has not produced a snapshot for the Domain yet (no sample has completed since boot); the Retry-After header carries the seconds to wait, derived from the sample interval.yes (Retry-After)

The 403 arm uses the PermissionDenied body shape; every other arm uses the Problem shape. The 500 arm is the transport's defensive fallback for an unexpected server-side failure — the wire body stays generic and no backend or driver text is interpolated into it.

Note on Problem.dimension

The shared Problem schema carries an optional dimension member, but this capacity snapshot endpoint never sets it. The member is populated exclusively by the capacity_exceeded 429 refusals on the Observability Ingest and Action Orchestrator write surfaces — values observability_ingest and action_executions respectively — so a client can attribute a throttle to a specific scale axis without string-matching detail. A 200 capacity snapshot response and the Problem arms of this endpoint never carry dimension. For the capacity_exceeded refusal contract on those throttled write surfaces, see the operator runbook ../../operations/capacity.md.

Cross-references