Skip to content

Blueprints HTTP API

This is the reference for the /v1/blueprints HTTP surface. It maps each operation to its OpenAPI schema, ReBAC gate, audit emission, 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 catalog is read-only on the wire — operators publish new Blueprints and BlueprintVersions through a separate management path that is out of scope here.

Operations

MethodPathOperation IDReBAC gateAudit relationBody cap
GET/v1/blueprintsListBlueprintsper-row blueprint#read filterblueprint.list (granted, with post-filter item_count)n/a
GET/v1/blueprints/{id}GetBlueprintblueprint#read (BEFORE persistence read)blueprint.getn/a
  • ListBlueprints.limit is clamped at the handler to [1, 200] with default 50.
  • ListBlueprints.cursor is opaque, HMAC-signed by the server through the CursorCodec port; a tampered cursor surfaces as 400 invalid_cursor. A cursor minted by user A and replayed by user B surfaces as 403 cursor_binding_mismatch.
  • ListBlueprints layers a per-row blueprint#read ReBAC check on top of the slug-ordered persistence window — the response page is the subset the caller is authorised to see. The next_cursor is set whenever the persistence layer returned a full page regardless of how many rows the per-row filter dropped, so a thin authorised cohort still pages forward. The OpenAPI prose names the gate blueprint#user for the ubiquitous-language reader role; the underlying SpiceDB permission is read (owner + publisher + reader).
  • GetBlueprint runs the blueprint#read ReBAC check before the persistence read, so an unauthorised caller receives 403 without the existence side-channel a "load-then-check" flow would leak. The response includes the published versions array; each version carries a typed parameter_schema (a closed set of BlueprintParameter rows the wizard renders).

Path & query parameters

OperationParameterTypeRequiredNotes
GetBlueprintid (path)string (uuid)yesUUIDv7. Non-zero. Malformed → 400 invalid_blueprint_id.
ListBlueprintscursor (query)stringnoOpaque HMAC-signed continuation. Tampered → 400 invalid_cursor.
ListBlueprintslimit (query)integerno[1, 200], default 50. Out-of-range → 400 invalid_limit.

Schemas

The OpenAPI spec is the authoritative source for field shapes. The schemas this surface uses are:

  • Response: BlueprintResponse (single, with embedded versions: BlueprintVersionResponse[]), BlueprintList (paged).
  • Embedded: BlueprintVersionResponse (publication metadata plus the typed parameter_schema), BlueprintParameter (one row per parameter: name, type, required, default).

The Blueprint response carries the resolved id, slug, display_name, optional description, status (closed enum: active, retired), the optional owning domain_id (or null for catalogue-wide entries), the created_at / updated_at lifecycle timestamps, and the versions array. The shape is shared by every read surface (ListBlueprints, GetBlueprint) so clients only need one binding — the difference between the two surfaces is the population of the versions array, not the envelope.

BlueprintVersionResponse

Each Blueprint version is keyed by its parent Blueprint and its version string. It carries:

  • version — non-empty string, unique within the parent Blueprint.
  • provider_kinds — non-empty closed-set array of infrastructure substrates this version can target: aws, gcp, hetzner, openstack.
  • injection_strategy — closed-set discriminator naming how the version threads request parameters into the rendered Composite Resource: cloud-init-user-data, helm-values, provider-secret.
  • parameter_schema — array of typed parameter declarations. May be empty when the version declares no parameters.
  • created_at — version creation timestamp (UTC).

The Crossplane XRD and Composition manifests are storage-internal and are deliberately not exposed on this read surface.

BlueprintParameter

A single typed parameter declaration:

  • name — non-empty parameter name an operator fills in at provisioning time.
  • type — closed scalar type: boolean, integer, string. Object and array types are intentionally not modelled; the Crossplane rendering path accepts only the closed scalar taxonomy.
  • required — whether the operator MUST supply a value.
  • default — optional default value applied when a non-required parameter is omitted. Present only when the parameter declares one; its JSON type matches type. Absent for required parameters and for optional parameters with no declared default.

Error taxonomy

All error responses use the shared Problem envelope (application/problem+json). The 403 path uses the richer PermissionDenied shape carrying the ReBAC denial reason, relation_path, and request correlation_id.

CodeStatusWhereMeaning
invalid_blueprint_id400path-id familyMalformed UUID or zero UUID.
invalid_cursor400ListHMAC verification failed.
invalid_limit400ListOut of [1, 200].
unauthenticated401every operationRequest carries no authenticated principal.
cursor_binding_mismatch403ListCursor presented by a different caller than the one that minted it; per-(caller, pepper) HMAC binding rejected the replay.
blueprint_not_found404GetNo Blueprint with the given {id}. The pre-persistence blueprint#read gate returns 403; this surface returns 404 only after the gate has passed and the row was not found.
blueprints_not_provisioned501every operationComposition root did not wire the Blueprint Catalog port (non-production builds only).
internal500every operationServer-side failure path.

A 403 response carries Problem.code = permission_denied plus the extended PermissionDenied fields documented in ./authz.md.

Audit emissions

The Blueprint Catalog application service does not emit audit rows on reads — emission is a transport-layer concern. The handlers stamp:

  • blueprint.list on the granted list path, with the post-filter item_count recorded in the caveat context, and on every denial path.
  • blueprint.get on the granted get path and on every denial path.

Every denial row carries outcome = permission_denied and the missing_relation caveat naming the gate that refused. Invariant rejections (malformed id, missing row) stamp outcome = invariant_violation.

Cross-references

  • ../../../api/openapi/plexsphere-v1.yaml — OpenAPI 3.1 spec; the ListBlueprints and GetBlueprint operations and the BlueprintResponse / BlueprintList / BlueprintVersionResponse / BlueprintParameter schemas.
  • ../../../internal/transport/http/v1/blueprints/ — the transport-tier implementation: the two handlers, the closed Problem.code taxonomy, the limit-clamp constants, and the per-row visibility filter on ListBlueprints.
  • ../../../schema/authz.zed — ReBAC schema; the blueprint definition declares the read permission this surface gates on.
  • ./resources.md — companion surface for the Tenancy Resource aggregate; the Resource Create Wizard consumes the BlueprintVersion's parameter_schema and posts a provisioned-flow ResourceCreateRequest.