Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.novacula.io/llms.txt

Use this file to discover all available pages before exploring further.

The control plane exposes a single GraphQL endpoint. Every UI page reads through it; every executor sync, log session, and lifecycle action goes through it. There is no REST surface — if you need to integrate programmatically, this is the API.

Endpoint

POST  /graphql
Accepts standard GraphQL request bodies (query, variables, operationName). Apollo Server is the implementation; introspection is enabled in non-production builds.

Authentication

Two credential types are accepted on the same endpoint: UI sessions hand the better-auth session cookie to every GraphQL request. You don’t typically replicate this from a script — it’s tied to the browser login flow.

API key (programmatic)

Send the org-scoped API key as a bearer token:
POST /graphql HTTP/1.1
Authorization: Bearer <api-key>
Content-Type: application/json
The AuthGuard validates the key, looks up its organizationId from key metadata, and sets session.activeOrganizationId to it. Every tenant-scoped query and mutation reads from there — you don’t pass organizationId explicitly. API keys are managed at API keys. Use a purpose=executor key for an executor; purpose=personal for human-driven scripts.

Authorization

Every query and mutation goes through CASL. The default rules:
  • System admin can manage all entities.
  • Org members can read all entities in their active org; only owner and admin can mutate.
Trying to access something outside your scope returns the entity as null (for query-by-id) or as an empty list (for list queries) — not a 403, by design — so an attacker can’t probe membership through error codes. Mutations that fail authorization return a typed error.

Schema highlights

The schema is generated from Prisma + NestJS via prisma-nestjs-graphql. The generated schema.gql lives in apps/control-plane/src/schema.gql.

Core types

  • User, Organization, Member, Invitation, Session — auth (better-auth-managed).
  • Executor + ExecutorResources + ExecutorApiKeyUsage — executor inventory.
  • Node + NotificationNodeOverride — node inventory + per-node overrides.
  • Event — events feed entries.
  • AlertIncident, NotificationSettings, WebhookDelivery — notifications.
  • AuditAction — audit log entries.
  • ExecutorRelease — global release catalog (read by all; managed by system admin).

Common queries

QueryPurpose
executors(where, orderBy, take, skip)List executors
executor(where: { id })One executor
nodes(where, orderBy, take, skip)List nodes
node(where: { id })One node
events(filter: EventFilterInput)Events feed with cursor pagination
auditActions(filter: AuditActionFilterInput)Audit log with cursor pagination
notificationCenterOpen-incident counts + latest 10 incidents (topbar bell)
notificationSettingsEffective org notification settings + per-node policies
notificationIncidents(status)Full incident history
webhookDeliveriesLast 50 webhook delivery attempts
executorReleases(where, orderBy)Release catalog

Common mutations

MutationPurpose
syncExecutor(input: SyncExecutorInput!)Executor heartbeat. Upsert by (orgId, name); carries kind, version, channel, capabilities, resources
reportRuntimeEvents(input: [RuntimeEventInput!]!)Push runtime events from an executor
createNode(data: NodeCreateInput!)Deploy a node
updateNode(data, where)Edit configuration / change desired state
requestNodeLifecycleAction(id, action)start / stop / restart
deleteNode(where)Delete a node
requestExecutorUpgrade(executorId, targetVersion)Initiate an executor self-update
deleteExecutor(where)Remove an executor row
saveNotificationSettings(input)Org-wide notification settings + per-node overrides
testNotificationWebhookSend a synthetic delivery to the configured URL
System-admin-only:
MutationPurpose
adminUpdateOrganizationMemberRoleChange a member’s role
adminSetOrganizationActiveActivate or deactivate an organization
createExecutorRelease / updateExecutorRelease / deleteExecutorReleaseManage the release catalog

Filters and pagination

  • where / orderBy / take / skip on most list queries follow standard Prisma input shapes (StringFilter, DateTimeFilter, …).
  • Events use a custom cursor pagination (EventFilterInput.cursor + direction).
  • AuditActions use cursor + connection edges (AuditActionConnection.pageInfo.endCursor).
The schema is exhaustive for filters — for example, events filters by executorName, nodeName, and level.

Errors

Errors fall into three buckets:
  • Validation — bad input shape. Returns a GraphQL error with the field path; HTTP 200 with errors[].
  • Authorization — trying to mutate an entity outside your scope. Typed Forbidden error.
  • Domainchain_not_supported_by_executor, executor_offline, invalid_node_config, upgrade_already_pending, release_not_found, etc. Codes are an enum; the messages are intended to be safe to surface in the UI.
The full error code list is in UserErrorCode in @vos/protocol.

Versioning

The schema is not versioned via URL or version field. Backwards-compatible additions (new fields, new queries) ship continuously; backwards-incompatible changes are rare and announced via release notes.

Tools

  • The Apollo sandbox is available at /graphql in non-production builds.
  • The @vos/control-plane-client package wraps the schema with graphql-codegen-generated TypeScript types — useful as a reference even if you’re integrating from a different language.