1 API Architecture and Design Approach

The Axebow architecture specifies a REST API as the primary interface for interacting with a platform instance, accessible only to authenticated users. The API design combines traditional RESTful endpoints with a reactive WebSocket channel for real-time updates.

1.1 Dual Interface Strategy

The API offers two complementary interfaces:

REST Interface
Provides standard HTTP endpoints for all platform operations. Clients make HTTP requests (GET, POST, PUT, DELETE, PATCH) to resource URLs and receive JSON responses. This interface is stateless, requiring authentication on each request, and is ideal for scripting, automation, command-line tools, and traditional web service integrations.
WebSocket Interface
Establishes persistent, bidirectional connections that support both request-response operations and server-initiated push notifications. Clients can execute any API operation over WebSocket using a JSON message protocol, while simultaneously receiving real-time updates about entity state changes. This interface maintains connection state and is ideal for interactive applications, dashboards, and scenarios requiring immediate notification of system changes.

Both interfaces access identical endpoints and operations. A service deployment initiated via REST behaves identically to one initiated via WebSocket. The choice of interface is purely a matter of client requirements and connection model preference.

1.2 Synchronous and Asynchronous Operations

All API operations share a common synchronous authorization phase, but execution strategy varies based on operation complexity:

Synchronous Authentication (interface-dependent):

HTTP Interface: Authentication occurs on every request. Each HTTP request must include a bearer token in the Authorization header, which is validated before the handler executes.

WebSocket Interface: Authentication occurs once during connection establishment. The bearer token is validated during the HTTP upgrade handshake. Once the WebSocket connection is established, all subsequent messages on that connection are inherently authenticated.

Synchronous Authorization and Validation (all operations):

When an operation is invoked (via HTTP request or WebSocket message), the API handler immediately:

  • Authorizes the operation against the user’s permissions (scope-based authorization)

  • Validates the request parameters and payload (schema validation)

Execution Strategy (operation-dependent):

Lightweight Operations (fully synchronous):

Operations that complete quickly, such as tenant creation, account registration, resource creation, execute synchronously within the handler. The API performs the operation, commits changes to the database, and returns the final result immediately.

Examples:

 POST   /tenant/:tenant
 POST   /tenant/:tenant/account/:account
 POST   /tenant/:tenant/secret/:secret

Heavy Operations (asynchronous):

Operations requiring complex orchestration, such as service deployments, resource deletions with dependencies, tenant cleanup, initiate the workflow synchronously but delegate actual execution to background controllers. The API creates initial records, returns immediately, and allows controllers to process the operation asynchronously through multiple stages.

Examples:

 POST   /tenant/:tenant/service/:service
 DELETE /tenant/:tenant
 DELETE /tenant/:tenant/account/:account

During asynchronous execution:

  • Controllers process the operation through a series of specialized stages

  • Each stage updates entity status fields to reflect progress

  • Errors and completion trigger database updates

  • WebSocket-connected clients receive push notifications of all status changes

This hybrid approach ensures that simple operations provide immediate results while complex orchestrations don’t block API responses, maintaining fast and predictable response times across all endpoints.

2 API Organization and Structure

The API is organized into 15 API groups, each focusing on a specific domain of platform functionality. This modular structure allows for clear separation of concerns, consistent patterns across related operations, and straightforward discovery of available endpoints.

2.1 API Groups and Modules

The API comprises the following groups:

Group Description Example Endpoints
tenant Tenant lifecycle management POST /api/{version}/tenant/:tenant, GET /api/{version}/tenant, DELETE /api/{version}/tenant/:tenant
service Service deployment and management POST /api/{version}/tenant/:tenant/service/:service, GET /api/{version}/tenant/:tenant/service, PUT /api/{version}/tenant/:tenant/service/:service/recover
revision Service revision management GET /api/{version}/tenant/:tenant/service/:service/revision, DELETE /api/{version}/tenant/:tenant/service/:service/revision/:revision
account Cloud provider account configuration POST /api/{version}/tenant/:tenant/account/:account, GET /api/{version}/tenant/:tenant/account
environment Deployment environment management POST /api/{version}/tenant/:tenant/environment/:environment, PATCH /api/{version}/tenant/:tenant/environment/:environment
link Service interconnection POST /api/{version}/tenant/:tenant/link/:link, GET /api/{version}/tenant/:tenant/link
resources Resource management (secrets, domains, ports, certificates) POST /api/{version}/tenant/:tenant/secret/:secret, GET /api/{version}/tenant/:tenant/domain
user User identity and tenant membership GET /api/{version}/user, POST /api/{version}/tenant/:tenant/user/:user, DELETE /api/{version}/tenant/:tenant/user/:user
token API token generation and management POST /api/{version}/token/:token, GET /api/{version}/token, DELETE /api/{version}/token/:token
plans Plan provider integration GET /api/{version}/planprovider/:provider/plan, POST /api/{version}/planprovider/:provider/plan/:plan/instance
marketplace Module search, bundle upload, schema retrieval GET /api/{version}/marketplace, POST /api/{version}/marketplace/bundle, GET /api/{version}/marketplace/bundle/:bundle/manifest
reporting Usage data and vCPU consumption reports GET /api/{version}/reporting/vcpu
operations Group reserved for platform operator actions DELETE /api/{version}/operations/playground-tenant, GET /api/{version}/operations/debug/get
dregistry Docker registry configuration POST /api/{version}/tenant/:tenant/dregistry/:dregistry, GET /api/{version}/tenant/:tenant/dregistry
misc Platform information, health checks, WebSocket endpoint GET /platform, GET /health, GET /api/{version}/ws

Each group is implemented as a directory under /apps/ksds/src/apigroups/<group>/ containing:

  • handlers/http/apis.ts: Route and schema definitions, depends on its facade definitions

  • handlers/http/http.ts: Handler implementations

  • handlers/http/types.ts: Request/response TypeScript types

  • handlers/ws/…: WebSocket-specific handlers, similar to http ones.

2.2 Endpoint Pattern Format

All API endpoints follow a hierarchical resource pattern:

<METHOD> /api/<version>/<entity_type>/[entity_name]/[<child_type>/[child_name]/.../[section]]?[params]

Components:

  • <METHOD>: HTTP verb (GET, POST, PUT, PATCH, DELETE)

  • <version>: API version

  • <entity_type>: Top-level entity (e.g., tenant, user, token)

  • [entity_name]: Specific entity identifier

  • [<child_type>/<child_name>]: Nested child resources (e.g., /tenant/T/service/S/revision/R)

  • [section]: Special operation suffix (e.g., /recover, /log, /manifest)

  • [params]: Query string parameters for filtering, pagination, options

Examples:

  • GET /api/{version}/tenant — List all tenants user can access

  • POST /api/{version}/tenant/acme — Create tenant named "acme"

  • GET /api/{version}/tenant/acme/service — List services in tenant "acme"

  • POST /api/{version}/tenant/acme/service/web — Deploy service "web" in tenant "acme"

  • GET /api/{version}/tenant/acme/service/web/revision — List revisions of service "web"

  • DELETE /api/{version}/tenant/acme/service/web — Delete service revision "web"

4 Authentication and Authorization

All API operations require authentication and are subject to authorization checks.

Note

Websocket requests are authenticated at the time the connection is established. Further operations are subject to uthorization, as are flows of server->client information restricted to only those items the authenticated user is authorized to receive.

The API uses bearer token authentication with JWT and JWE tokens, supporting both idprovider-issued tokens (from OpenID Connect identity providers) and platform-issued API tokens.

Users obtain an idprovider-issued token through Axebow’s web interface, via the OpenID Connect (OIDC) protocol through one of the configured identity providers (the initial specification includes Microsoft, Google, and GitHub). This token is sent using the Authorization header.

Important

Platform-issued tokens are sent using the api-token header, not the Authorization header.

4.1 Common authentication path

Access to Axebow’s functionality is designed to proceed exclusively through its HTTPS API, which mandates OIDC-based authentication.

It is important to note that successful authentication does not automatically grant platform access; an unregistered user authenticating via the web application would be presented with a registration process before gaining full access to the Web User Interface (WUI). Once registered and authenticated, a user’s access to WUI functionality is governed by the platform’s authorization policies.

4.2 Certificate-based authentication

The design includes a secondary authentication method based on mutual TLS (mTLS) for operational purposes. This method would utilize client certificates issued by an Axebow-owned Certificate Authority (CA). The issuance of these certificates is intended to be a restricted operation, available only to platform operators.

4.3 Token-based access

To facilitate API automation, a user can obtain bearer API tokens. These tokens can then be used for direct, programmatic access to the API.

4.4 Authorization

Axebow has a primary authorization mechanism based on the role a user has within a tenant. API operations are carried out on tenants. Thus, authorization policies are based, primarily, on the role a user has within the tenant in which it wants to operate.

4.4.1 Token authorization

The primary token obtained by a user represents the full set of capabilities of the user. As explained above, axebow can issue API tokens for a user. Furthermore The set of capabilities an API token carries can be configured on token creation as well as its validity period.

The capabilities of a restricted token are embedded in the token itself. They are checked before any further fine grained authorization.

Note

An API token capabilities are checked at the time of their usage. If the user changes its tenant role after the token is issued, its capabilities are adjusted correspondingly

6 Response Formats and Error Handling

The API uses consistent response formats for both success and error cases, enabling clients to handle responses uniformly regardless of endpoint or operation type.

6.1 Success Response Format

Successful operations return an HTTP 200 with a JSON body:

{
  "data": { /* operation result */ },
}

Fields:

  • data: The result of the operation (entity record, list of entities, computed value)

6.2 Error Response Format

Error responses use appropriate HTTP status codes with a JSON body:

{
  "code": "_error_code_",
  "content": { /* optional error details */ }
}

Fields:

  • code: Machine-readable error code

  • content: Human-readable error message or structured error details

6.3 Error Code System

Axebow defines over many error codes categorized by type, some of them are:

Authentication/Authorization Errors (HTTP 401, 403):

  • unauthorized: Missing or invalid authentication token

  • forbidden: User lacks required permissions

  • _token_expired_: Authentication token has expired

  • _invalid_scope_: Operation not permitted by token scopes

Resource Errors (HTTP 404, 409):

  • _tenant_not_found_, _service_not_found_, _environment_not_found_: Entity does not exist

  • _tenant_exists_, _service_exists_, _user_exists_: Entity already exists (conflict)

  • _resource_conflict_: Port or domain already in use by another service

Quota/Limit Errors (HTTP 409, 403):

  • _limits_exceeded_: Operation would exceed plan limits

Rate Limiting (HTTP 429):

  • _rate_limit_exceeded_: Request rate limit exceeded

  • _payload_too_large_: Request payload exceeds size limit (1MB for WebSocket)

Operational Errors (HTTP 500):

  • generic: Unexpected internal error occurred

Feature Errors (HTTP 403):

  • disabled: API group is disabled via feature flag

6.4 Reference IDs for Defects

When unexpected errors occur:

  1. Reference ID generated: Unique UUID created

  2. Error logged: Full error stack trace logged with reference ID

  3. Trace correlation: Reference ID added to trace span

  4. User notification: Reference ID included in error response

Users can report the reference ID in support requests, allowing to correlate the error with logs and traces for root cause analysis.

7 WebSocket API

While the REST API provides straightforward request-response interactions, many client applications (notably Axebow’s dashboard) require real-time notifications of platform state changes. Polling REST endpoints for updates is inefficient and introduces latency. The WebSocket API solves this by establishing persistent, bidirectional connections that support both request-response operations and server-initiated push notifications.

The WebSocket API is not a separate interface, it shares the same business logic, facades, authorization model, and error handling as the REST API. Every REST operation can be performed over WebSocket, with the added benefit of receiving real-time updates about any entity changes relevant to the authenticated user.

7.1 Connection Establishment and Authentication

The WebSocket endpoint is located at:

    /api/{version}/ws

Authentication:

The bearer token is validated during the HTTP upgrade phase:

  1. Token extraction: Bearer token extracted from Authorization header

  2. User authentication: Token validated, user record loaded

If authentication fails, the WebSocket upgrade is rejected with HTTP 401 or 403.

Initial Context Synchronization:

Immediately after connection establishment, the server sends all entities the user can access:

  • User’s own user record

  • All tenant records (for tenants the user is a member of)

  • All environments, services, revisions, and resources within those tenants

  • All service links (where user is a member of either client or server tenant)

  • All provider user records

  • All plan instance records

This ensures clients have complete initial state before receiving incremental updates.

7.2 Message Protocol and Format

All WebSocket communication uses JSON messages with a standarized schema:

{
  "messageId": "uuid-client-generated",
  "type": "request|multipartrequest|success|error|event",
  "topic": "api_group_name:endpoint_id",
  "payload": { /* message-specific data */ }
}

7.3 Message Types

The WebSocket protocol supports five message types:

7.3.1 Request Messages

Purpose: Execute API operations over WebSocket

Direction: Client → Server

Format:

{
  "messageId": "550e8400-e29b-41d4-a715-446655440000",
  "type": "request",
  "topic": "service:create_service",
  "payload": {
    "tenant": "acme",
    "service": "web",
    "spec": {
      "type": "bundle",
      "bundleRef": "bundle-id",
      "comment": "Deploy web service"
    }
  }
}

Payload Structure:

  • Path parameters: payload.tenant, payload.service, etc.

  • Query parameters: payload.filter, payload.limit, etc.

  • Request body: payload.spec (equivalent to HTTP POST/PUT body)

Topic Format: <api_group>:<endpoint_id>

Examples: tenant:create_tenant, service:list_services, user:get_user

7.3.2 Success Messages

Purpose: Return successful operation results

Direction: Server → Client

Format:

{
  "messageId": "550e8400-e29b-41d4-a715-446655440000",
  "type": "success",
  "topic": "service:create_service",
  "payload": {
    "data": { /* service record */ },
    "events": [
      { "severity": "INFO", "content": "Service created" }
    ]
  }
}

The payload follows the same structure as REST API success responses ({data, events}).

7.3.3 Error Messages

Purpose: Return operation errors

Direction: Server → Client

Format:

{
  "messageId": "550e8400-e29b-41d4-a715-446655440000",
  "type": "error",
  "topic": "service:create_service",
  "payload": {
    "error": {
      "status": 409,
      "code": "_service_exists_",
      "content": "Service 'web' already exists in tenant 'acme'"
    }
  }
}

Error payloads include:

  • status: HTTP status code equivalent (400, 401, 403, 404, 409, 429,

  • code: Machine-readable error code (same codes as REST API)

  • content: Human-readable error details

7.3.4 Event Messages

Purpose: Push real-time entity change notifications

Direction: Server → Client

Format:

{
  "messageId": "server-generated-uuid",
  "type": "event",
  "topic": "tenant-acme",
  "payload": {
    "key": "/tenant/acme/service/web",
    "record": { /* complete service entity */ }
  }
}

Delete Events:

{
  "messageId": "server-generated-uuid",
  "type": "event",
  "topic": "tenant-acme",
  "payload": {
    "key": "/tenant/acme/service/web",
    "deleted": true
  }
}

Events are pushed automatically when any entity changes that the user has permissions to observe.

7.3.5 Multipart Request Messages

Purpose: Upload large files (bundles) that exceed the 1MB message size limit

Direction: Client → Server (three-phase protocol)

See "Multipart Upload Protocol" section below for complete details.

7.5 Multipart Upload Protocol

Large file uploads (e.g., service bundles) cannot be sent in a single WebSocket message due to the 1MB message size limit. The multipart upload protocol solves this by chunking data across multiple messages.

Protocol Overview:

The multipart upload follows a three-phase protocol:

  1. Phase 1: Initialization (part: "start")

  2. Phase 2: Data Transfer (part: <number>)

  3. Phase 3: Finalization (part: "end")

7.5.1 Phase 1: Initialization

Client sends a multipart request to initiate the upload:

{
  "messageId": "upload-init-uuid",
  "type": "multipartrequest",
  "topic": "service:create_service",
  "payload": {
    "tenant": "acme",
    "service": "web",
    "part": "start",
    "totalParts": 5,
    "data": ""
  }
}

Server Actions:

  1. Validates user has permission for the tenant

  2. Generates unique uploadId (UUIDv7)

  3. Initializes MultipartUploadState in memory

  4. Returns success response:

{
  "messageId": "upload-init-uuid",
  "type": "success",
  "topic": "service:create_service",
  "payload": {
    "uploadId": "abc-123-def-456",
    "status": "started"
  }
}

7.5.2 Phase 2: Data Transfer

Client sends data in numbered parts (base64-encoded):

{
  "messageId": "upload-part-1-uuid",
  "type": "multipartrequest",
  "topic": "service:create_service",
  "payload": {
    "uploadId": "abc-123-def-456",
    "part": 1,
    "data": "base64encodeddata...",
    "totalParts": 5
  }
}

Server Actions:

  1. Validates uploadId exists and belongs to this user

  2. Converts base64 data to Uint8Array

  3. Stores part in MultipartUploadState.parts Map

  4. Returns acknowledgment:

{
  "messageId": "upload-part-1-uuid",
  "type": "success",
  "topic": "service:create_service",
  "payload": {
    "uploadId": "abc-123-def-456",
    "part": 1,
    "status": "received"
  }
}

Client repeats for parts 2, 3, 4, 5…

7.5.3 Phase 3: Finalization

Client sends finalization message with the actual request parameters:

{
  "messageId": "upload-end-uuid",
  "type": "multipartrequest",
  "topic": "service:create_service",
  "payload": {
    "uploadId": "abc-123-def-456",
    "part": "end",
    "totalParts": 5,
    "data": "",
    "props": {
      "tenant": "acme",
      "service": "web",
      "spec": {
        "type": "bundle",
        "bundleRef": "abc-123-def-456",
        "comment": "Web service bundle"
      }
    }
  }
}

Server Actions:

  1. Validates all parts received (count matches totalParts)

  2. Combines parts in numerical order: combinePartsInOrder()

  3. Writes complete file to object store: /tmp/${userId}/${uploadId}

  4. Forwards request to target endpoint as normal BusRequest

  5. Executes handler with bundleRef pointing to uploaded file

  6. Schedules cleanup after 10 seconds (deletes temp file)

  7. Returns normal operation response

Security:

  • Authorization validated once during initialization phase

  • Upload sessions isolated per user and connection

  • Temporary files stored in user-specific directories

  • Automatic cleanup prevents resource leaks

7.6 Rate Limiting

To prevent abuse and ensure fair resource allocation, the WebSocket API enforces rate limiting on incoming messages using a Leaky bucket algorithm.

Message Size Limit:

  • Maximum message size: 1MB (1024 KB)

  • Oversized messages rejected with _payload_too_large_ error

  • Multipart protocol recommended for large payloads

Rate limits apply per connection, not per user. Connections are limited independently on each other.

7.8 Integration with REST API

The WebSocket API is fully complementary to the REST API, not a replacement. Both interfaces access the same business logic and provide consistent behavior.

When to Use Each:

  • REST API: Scripts, automation, command-line tools, simple integrations, operations where real-time updates aren’t needed. Axebow makes available a swagger definition of the web interface.

  • WebSocket API: Interactive dashboards, monitoring UIs, applications requiring immediate notification of changes, high-frequency operations

Both interfaces are first-class citizens in the Axebow platform, providing flexibility for diverse client requirements.