Skip to content

API Reference

Adrian Burlacu edited this page Feb 15, 2026 · 5 revisions

API Reference

Complete reference documentation for the Stark Orchestrator REST and WebSocket APIs.

Base URL

Environment URL
Development https://localhost:443
Production https://your-orchestrator.example.com

Authentication

All API requests (except health check and initial setup) require authentication via Bearer token.

Authorization: Bearer <access-token>

Obtain tokens via the login endpoint or CLI.


REST Endpoints

Health & Setup

GET /health

Health check endpoint. No authentication required.

Response:

{
  "status": "healthy",
  "timestamp": "2026-02-02T10:00:00.000Z",
  "version": "0.0.1"
}

GET /auth/setup/status

Check if initial admin setup is needed.

Response:

{
  "setupRequired": true
}

POST /auth/setup

Create the initial admin user. Only works when no users exist.

Request:

{
  "email": "admin@example.com",
  "password": "secure-password",
  "displayName": "Admin User"
}

Response:

{
  "user": {
    "id": "user-uuid",
    "email": "admin@example.com",
    "role": "admin"
  },
  "session": {
    "access_token": "...",
    "refresh_token": "..."
  }
}

Authentication

POST /auth/register

Register a new user.

Request:

{
  "email": "user@example.com",
  "password": "secure-password",
  "displayName": "User Name"
}

POST /auth/login

Authenticate and receive tokens.

Request:

{
  "email": "user@example.com",
  "password": "secure-password"
}

Response:

{
  "user": {
    "id": "user-uuid",
    "email": "user@example.com",
    "role": "user"
  },
  "session": {
    "access_token": "...",
    "refresh_token": "...",
    "expires_at": 1706961600
  }
}

POST /auth/logout

Logout current session.

GET /auth/users

List all users (admin only).

Response:

{
  "users": [
    {
      "id": "user-uuid",
      "email": "admin@example.com",
      "role": "admin",
      "created_at": "2026-01-01T00:00:00.000Z"
    }
  ]
}

POST /auth/users

Create a new user (admin only).

Request:

{
  "email": "newuser@example.com",
  "password": "secure-password",
  "role": "node"
}

Packs

GET /api/packs

List all accessible packs.

Query Parameters:

Parameter Description
namespace Filter by namespace
visibility Filter by visibility (private, public)

Response:

{
  "packs": [
    {
      "id": "pack-uuid",
      "name": "my-app",
      "latestVersion": "1.2.0",
      "runtime": "node",
      "visibility": "private",
      "ownerId": "user-uuid",
      "createdAt": "2026-01-15T10:00:00.000Z"
    }
  ]
}

POST /api/packs

Register a new pack.

Request:

{
  "name": "my-app",
  "version": "1.0.0",
  "runtime": "node",
  "visibility": "private",
  "content": "<base64-encoded-bundle>"
}

Response:

{
  "pack": {
    "id": "pack-uuid",
    "name": "my-app",
    "version": "1.0.0",
    "runtime": "node"
  }
}

GET /api/packs/:name/versions

List all versions of a pack.

Response:

{
  "versions": [
    {
      "version": "1.0.0",
      "createdAt": "2026-01-15T10:00:00.000Z"
    },
    {
      "version": "1.1.0",
      "createdAt": "2026-01-20T12:00:00.000Z"
    }
  ]
}

Pods

GET /api/pods

List all pods.

Query Parameters:

Parameter Description
namespace Filter by namespace
status Filter by status
node Filter by node ID
pack Filter by pack name

Response:

{
  "pods": [
    {
      "id": "pod-uuid",
      "packName": "my-app",
      "packVersion": "1.0.0",
      "nodeId": "node-uuid",
      "namespace": "default",
      "status": "running",
      "labels": {"app": "web"},
      "createdAt": "2026-01-20T10:00:00.000Z"
    }
  ]
}

POST /api/pods

Create a new pod.

Request:

{
  "packName": "my-app",
  "packVersion": "1.0.0",
  "nodeId": "node-uuid",
  "namespace": "default",
  "priority": 100,
  "labels": {"app": "web"},
  "nodeSelector": {"env": "production"},
  "tolerations": [{"key": "dedicated", "value": "gpu", "effect": "NoSchedule"}],
  "resources": {
    "cpu": 500,
    "memory": 256
  }
}

Response:

{
  "pod": {
    "id": "pod-uuid",
    "status": "pending"
  }
}

GET /api/pods/:id

Get pod details.

Response:

{
  "pod": {
    "id": "pod-uuid",
    "packName": "my-app",
    "packVersion": "1.0.0",
    "nodeId": "node-uuid",
    "nodeName": "my-node",
    "namespace": "default",
    "status": "running",
    "labels": {"app": "web"},
    "resources": {"cpu": 500, "memory": 256},
    "createdAt": "2026-01-20T10:00:00.000Z",
    "scheduledAt": "2026-01-20T10:00:01.000Z",
    "startedAt": "2026-01-20T10:00:02.000Z"
  }
}

POST /api/pods/:id/rollback

Rollback pod to a previous version.

Request:

{
  "version": "0.9.0"
}

DELETE /api/pods/:id

Delete a pod.


Nodes

GET /api/nodes

List all nodes.

Response:

{
  "nodes": [
    {
      "id": "node-uuid",
      "name": "my-node",
      "runtime": "node",
      "status": "ready",
      "labels": {"env": "production"},
      "taints": [],
      "allocatable": {"cpu": 2000, "memory": 4096, "pods": 20},
      "allocated": {"cpu": 500, "memory": 512, "pods": 2},
      "lastHeartbeat": "2026-02-02T10:00:00.000Z"
    }
  ]
}

GET /api/nodes/:id

Get node details by ID.

GET /api/nodes/name/:name

Get node details by name.


Namespaces

GET /api/namespaces

List all namespaces.

Response:

{
  "namespaces": [
    {
      "id": "ns-uuid",
      "name": "default",
      "createdAt": "2026-01-01T00:00:00.000Z"
    },
    {
      "id": "ns-uuid-2",
      "name": "production",
      "createdAt": "2026-01-15T00:00:00.000Z"
    }
  ]
}

POST /api/namespaces

Create a namespace.

Request:

{
  "name": "production"
}

DELETE /api/namespaces/:id

Delete a namespace.


WebSocket API

Connect to /ws for real-time communication.

Connection

const ws = new WebSocket('wss://localhost/ws');

// Include auth token in connection (implementation may vary)
ws.onopen = () => {
  ws.send(JSON.stringify({
    type: 'auth',
    payload: { token: 'your-access-token' }
  }));
};

Message Format

All messages follow this structure:

{
  "type": "message-type",
  "payload": { ... }
}

Node Registration

node:register

Register a node with the orchestrator.

Send:

{
  "type": "node:register",
  "payload": {
    "name": "my-node",
    "runtimeType": "node",
    "labels": {"env": "production"},
    "taints": [],
    "allocatable": {
      "cpu": 2000,
      "memory": 4096,
      "pods": 20
    }
  }
}

Receive:

{
  "type": "node:registered",
  "payload": {
    "nodeId": "node-uuid"
  }
}

node:heartbeat

Send periodic heartbeat to indicate node is alive.

Send:

{
  "type": "node:heartbeat",
  "payload": {
    "nodeId": "node-uuid",
    "timestamp": "2026-02-02T10:00:00.000Z"
  }
}

Pod Events

pod:assign

Received when a pod is assigned to the node.

Receive:

{
  "type": "pod:assign",
  "payload": {
    "podId": "pod-uuid",
    "packName": "my-app",
    "packVersion": "1.0.0",
    "packContent": "<bundle-content>"
  }
}

pod:status

Send pod status updates.

Send:

{
  "type": "pod:status",
  "payload": {
    "podId": "pod-uuid",
    "status": "running",
    "message": "Pod started successfully"
  }
}

pod:stop

Received when a pod should be stopped.

Receive:

{
  "type": "pod:stop",
  "payload": {
    "podId": "pod-uuid"
  }
}

Real-time Updates

Subscribe to real-time updates for resources.


PodGroup Messages (Ephemeral Data Plane)

PodGroup operations use the group:* message namespace. The orchestrator manages a central PodGroupStore — all membership state is authoritative and in-memory.

Full reference: PodGroups & Ephemeral Data Plane

group:join

Join or refresh membership in a PodGroup.

Send:

{
  "type": "group:join",
  "correlationId": "req-200",
  "payload": {
    "podId": "pod-abc-123",
    "groupId": "demo:podgroup-chat",
    "ttl": 120000,
    "metadata": { "role": "echo" }
  }
}

Receive:

{
  "type": "group:join:ack",
  "correlationId": "req-200",
  "payload": {
    "membership": {
      "podId": "pod-abc-123",
      "joinedAt": 1707700000000,
      "lastRefreshedAt": 1707700000000,
      "ttl": 120000,
      "metadata": { "role": "echo" }
    }
  }
}
Field Type Required Description
podId string Yes The pod joining the group
groupId string Yes Group identifier
ttl number No Membership TTL in ms (default: 60000, 0 = infinite)
metadata object No Arbitrary metadata attached to the membership

Identity enforcement: Pod-type connections can only join as themselves. A podId mismatch returns group:error.

group:leave

Leave a specific PodGroup.

Send:

{
  "type": "group:leave",
  "correlationId": "req-201",
  "payload": {
    "podId": "pod-abc-123",
    "groupId": "demo:podgroup-chat"
  }
}

Receive:

{
  "type": "group:leave:ack",
  "correlationId": "req-201",
  "payload": { "removed": true }
}

group:leave-all

Leave all groups. Typically sent during graceful shutdown.

Send:

{
  "type": "group:leave-all",
  "correlationId": "req-202",
  "payload": { "podId": "pod-abc-123" }
}

Receive:

{
  "type": "group:leave-all:ack",
  "correlationId": "req-202",
  "payload": { "removedFrom": ["demo:podgroup-chat", "room:lobby"] }
}

group:get-pods

List all members of a PodGroup.

Send:

{
  "type": "group:get-pods",
  "correlationId": "req-203",
  "payload": { "groupId": "demo:podgroup-chat" }
}

Receive:

{
  "type": "group:get-pods:ack",
  "correlationId": "req-203",
  "payload": {
    "members": [
      { "podId": "pod-abc-123", "joinedAt": 1707700000000, "lastRefreshedAt": 1707700060000, "ttl": 120000, "metadata": { "role": "echo" } },
      { "podId": "pod-def-456", "joinedAt": 1707700010000, "lastRefreshedAt": 1707700070000, "ttl": 120000, "metadata": { "role": "greeter" } }
    ]
  }
}

group:get-groups

List all groups a pod belongs to.

Send:

{
  "type": "group:get-groups",
  "correlationId": "req-204",
  "payload": { "podId": "pod-abc-123" }
}

Receive:

{
  "type": "group:get-groups:ack",
  "correlationId": "req-204",
  "payload": { "groups": ["demo:podgroup-chat", "room:lobby"] }
}

group:error

Returned when a group operation fails.

Receive:

{
  "type": "group:error",
  "correlationId": "req-200",
  "payload": { "error": "Missing podId or groupId" }
}

Automatic cleanup: When a pod's WebSocket connection closes, the server automatically removes it from all groups.


Error Responses

All endpoints return consistent error responses:

{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Pod not found",
    "details": {
      "podId": "invalid-id"
    }
  }
}

HTTP Status Codes

Code Description
200 Success
201 Created
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
500 Internal Server Error

Secrets API

List Secrets

GET /api/secrets

Query Parameters:

Parameter Type Description
namespace string Filter by namespace
type string Filter by type (opaque, tls, docker-registry)

Response:

{
  "secrets": [
    {
      "id": "secret-uuid",
      "name": "db-creds",
      "type": "opaque",
      "namespace": "default",
      "keyCount": 2,
      "injection": { "mode": "env", "prefix": "DB_" },
      "createdAt": "2026-02-10T12:00:00.000Z",
      "updatedAt": "2026-02-10T12:00:00.000Z"
    }
  ]
}

Note: Secret data (values) are never included in list responses.

Create Secret

POST /api/secrets

Request Body:

{
  "name": "db-creds",
  "type": "opaque",
  "namespace": "default",
  "data": {
    "username": "admin",
    "password": "s3cret"
  },
  "injection": {
    "mode": "env",
    "prefix": "DB_"
  }
}

Response: 201 Created

{
  "secret": {
    "id": "secret-uuid",
    "name": "db-creds",
    "type": "opaque",
    "namespace": "default",
    "keyCount": 2,
    "injection": { "mode": "env", "prefix": "DB_" },
    "createdAt": "2026-02-10T12:00:00.000Z"
  }
}

Get Secret

GET /api/secrets/name/:name

Query Parameters:

Parameter Type Description
namespace string Namespace (default: default)

Response:

{
  "secret": {
    "id": "secret-uuid",
    "name": "db-creds",
    "type": "opaque",
    "namespace": "default",
    "keyCount": 2,
    "keys": ["username", "password"],
    "injection": { "mode": "env", "prefix": "DB_" },
    "createdAt": "2026-02-10T12:00:00.000Z",
    "updatedAt": "2026-02-10T12:00:00.000Z"
  }
}

Note: Only key names are returned, never values.

Update Secret

PATCH /api/secrets/name/:name

Request Body:

{
  "namespace": "default",
  "data": {
    "username": "new-admin",
    "password": "new-s3cret"
  },
  "injection": {
    "mode": "volume",
    "mountPath": "/etc/secrets/db"
  }
}

Response: 200 OK

Delete Secret

DELETE /api/secrets/name/:name

Query Parameters:

Parameter Type Description
namespace string Namespace (default: default)

Response: 204 No Content


Volume Endpoints

Create Volume

POST /api/volumes

Request Body:

{
  "name": "counter-data",
  "nodeId": "node-uuid"
}
Field Type Description
name string Volume name (lowercase alphanumeric + hyphens, 1–63 chars)
nodeId string Target node UUID

Response: 201 Created

{
  "success": true,
  "data": {
    "volume": {
      "id": "volume-uuid",
      "name": "counter-data",
      "nodeId": "node-uuid",
      "createdAt": "2026-02-15T12:00:00.000Z",
      "updatedAt": "2026-02-15T12:00:00.000Z"
    }
  }
}

Errors:

Code Status Description
VALIDATION_ERROR 400 Invalid name or missing nodeId
CONFLICT 409 Volume name already exists on the node

List Volumes

GET /api/volumes

Query Parameters:

Parameter Type Description
nodeId string Filter by node UUID (optional)

Response: 200 OK

{
  "success": true,
  "data": {
    "volumes": [
      {
        "id": "volume-uuid",
        "name": "counter-data",
        "nodeId": "node-uuid",
        "createdAt": "2026-02-15T12:00:00.000Z",
        "updatedAt": "2026-02-15T12:00:00.000Z"
      }
    ]
  }
}

Get Volume by Name

GET /api/volumes/name/:name

Query Parameters:

Parameter Type Description
nodeId string (required) Node UUID

Response: 200 OK

{
  "success": true,
  "data": {
    "volume": {
      "id": "volume-uuid",
      "name": "counter-data",
      "nodeId": "node-uuid",
      "createdAt": "2026-02-15T12:00:00.000Z",
      "updatedAt": "2026-02-15T12:00:00.000Z"
    }
  }
}

Download Volume

GET /api/volumes/name/:name/download

Query Parameters:

Parameter Type Description
nodeId string (required) Node UUID

Response: 200 OK with Content-Type: application/x-tar

Returns volume contents as a tar archive. In V1 this is a placeholder — full content retrieval from remote nodes is planned.


TODO: Additional Documentation

Clone this wiki locally