Server & API
The Sixb server exposes a configured SixbHost through an
Elysia HTTP + WebSocket API. It authenticates callers and enforces their
grants across objects, telemetry, actions, automation runs, agents, and domain events under
/api/* and /ws/*.
Reach for it whenever a browser front-end or another service needs to talk to your runtime over the network. The server serves the API only — the built-in admin UI (atlas) and any custom app run as separate servers that call it over HTTP.
Mental model
Sixb splits into three layers. The server is the middle one: it takes an already-built host and exposes its domain operations with authentication and authorization. It does not construct the host.
| Layer | You call | What it is |
|---|---|---|
| Host | createSixb() | Providers, definitions, and process lifecycle, in-process. |
| API | createSixbServer({ host, browser }) | Authorized HTTP + WebSocket access to domain operations. |
| Front-ends | atlas + your app | Separate browser clients that call /api over HTTP. |
Data flows one direction: front-ends → /api (cookies + CSRF) → server → runtime → storage, with domain events streaming back out over /ws/events.
Starting the server
The common path is sixb dev (runtime + atlas + custom app) or sixb api (API only). To embed the server directly:
import { createSixb } from "@sixb/core"
import { createSixbServer } from "@sixb/server"
const host = await createSixb({ /* providers */ })
const server = createSixbServer({
host,
port: 3000,
hostname: "0.0.0.0",
browser: {
publicOrigin: "https://api.acme.example",
allowedOrigins: [
{ origin: "https://atlas.acme.example", audience: "atlas" },
{ origin: "https://app.acme.example", audience: "app" },
],
},
})
await server.start()
// API at http://0.0.0.0:3000
// OpenAPI at http://0.0.0.0:3000/docs
await server.stop()
Options
| Option | Type | Default | Description |
|---|---|---|---|
host | SixbHost | (required) | A host from createSixb(). The server never builds one. |
browser | SixbApiBrowserPolicy | (required) | Origin policy: the API's publicOrigin and allowedOrigins. |
port | number | 3000 | TCP port to listen on. |
hostname | string | "0.0.0.0" | Bind hostname. |
quiet | boolean | false | Suppress startup logging. |
The browser policy is load-bearing for security: it drives CORS, rejects disallowed Origin headers up front, and resolves the public origin used to mint auth redirects. Each allowedOrigins entry maps one front-end origin to its auth audience (atlas or app), and each audience can have only one origin. Configured audiences become invitation destinations and application-access boundaries.
Route groups
All JSON routes are prefixed with /api and mirror the runtime's typed APIs; see the linked pages for behavior. The full per-route request/response contract for objects lives in the HTTP reference.
| Group | Representative routes | See |
|---|---|---|
| Objects CRUD | GET /api/objects, GET/PUT /api/objects/:type/:id | Objects |
| Object query | POST /api/objects/query, .../query/count, .../query/exists, .../query/facets | Querying |
| Files | POST /api/files, /api/files/uploads/..., GET .../files/content | Files |
| Telemetry | GET/POST /api/objects/:type/:id/telemetry/:prop, .../history, .../latest | Telemetry |
| Links | GET/PUT/DELETE /api/objects/:type/:id/links/:linkId | Links |
| Actions | GET /api/actions, POST /api/actions/:actionId | Actions |
| Action runs | GET /api/action-runs, GET /api/action-runs/:runId | Actions |
| Ontology | GET /api/object-types, GET /api/object-types/:objectTypeId | Ontology |
| Events (WS) | GET /ws/events | Events |
| Agents | GET /api/agents, /api/agent-threads, .../messages, /api/agent-runs/:runId, GET /ws/agents | Agents |
| Logs | GET /api/logs, GET /ws/logs | Logging |
| Workflows | GET /api/workflows, /api/workflow-runs, /api/workflows/:id/runs | Workflows |
| Interventions | /api/workflow-interventions, .../:id/submit, .../:id/cancel | Interventions |
| Rules | GET /api/rules, GET /api/rule-states | Rules |
| Datasets | /api/datasets, .../versions, .../rows | Datasets |
| Syncs | /api/syncs, /api/sync-runs, /api/syncs/:id/runs | Syncs |
| Pipelines | /api/pipelines, /api/pipeline-runs, /api/pipelines/:id/runs | Pipelines |
| Projections | GET /api/projections, GET /api/projections/:projectionId | Projections |
| Connectors | GET /api/connectors, GET /api/connectors/:connectorId | Connectors |
| Webhooks | POST /api/webhooks/:connectorId/:webhookId, GET /api/webhook-runs | Connectors |
| Auth | /api/auth/session, /auth/sign-in, /auth/callback, /api/auth/... | Auth |
| Project/Status | GET /api/project, GET /api/status, GET /health, GET /ready | — |
Object data routes enforce the caller's grants: reads are filtered, and the object, link, and
telemetry writes require edit:object or append:telemetry and answer 403 without it. File and
auth-administration routes are authenticated-only — neither has a grant family yet. See
Authorization.
Status endpoints have distinct meanings:
| Endpoint | Meaning | Failure behavior |
|---|---|---|
GET /health | The API process is alive. | Returns 200 while the process can serve HTTP. |
GET /ready | Storage is reachable and its schema is current. | Returns 503 when the runtime must leave traffic. |
GET /api/status | Cached outbox, retry, cleanup, and maintenance state. | Reports degraded without removing a healthy API from traffic. |
Files
A fileRef property holds a blob. Uploading it takes one request
when the file is small, and a session when it is not.
| Route | Use |
|---|---|
POST /api/files | one multipart request; the whole file travels through the API |
POST /api/files/uploads | open a session for a large or client-uploaded file |
PUT /api/files/uploads/:uploadId/content | send the content through the API |
POST /api/files/uploads/:uploadId/parts/:partNumber | get a signed URL and upload the part directly to blob storage |
POST /api/files/uploads/:uploadId/complete | finish the session and get the reference |
POST /api/files/uploads/:uploadId/abort | discard it |
Reading back is GET (or HEAD) on the owner's files/content, with a JSON pointer to the
reference as path — on an object it must start with /properties:
curl "$API/api/objects/Invoice/inv-1/files/content?path=/properties/scan" -o scan.pdf
The same route shape hangs off action runs, workflow runs, workflow-run nodes, and agent thread
messages — /api/action-runs/:runId/files/content, /api/workflow-runs/:runId/files/content,
.../nodes/:nodeKey/files/content, and
/api/agent-threads/:threadId/messages/:messageId/files/content.
Pre-0.1 limit — upload sessions are in-memory. Neither
@sixb/pgnor@sixb/sqliteimplementsfileUploadSessions, so every session opened byPOST /api/files/uploadslives in the serving process: it does not survive a restart and is not shared across replicas. Route a session's requests to one instance, supply your own store onhost.storage.fileUploadSessions, or use single-requestPOST /api/files, which is unaffected.
Real-time events
GET /ws/events is a WebSocket stream of domain events — object.created, object.updated, telemetry.appended, link.created, action.requested, and more. Any authenticated principal may connect; each event is filtered per-principal by grants as it streams.
On connect, the server sends a connected control frame. Send a subscribe message to filter the stream — by topic (one domain-event topic such as objects, telemetry, links, or actions), types (specific event types), and/or objectTypeId (one object type) — and unsubscribe to stop:
{ "type": "subscribe", "topic": "objects", "types": ["object.created", "object.updated"], "objectTypeId": "Invoice", "limit": 100 }
The example above streams create and update events for Invoice objects only — useful for a billing dashboard that reacts as invoices are created, sent, or marked paid. The server replies with subscribed and unsubscribed control frames, streams matching events as { "type": "event", "event": ... }, and reports problems as { "type": "error", "message": ... }. For a typed client over this stream, see Client.
Two more WebSocket streams follow the same connect/subscribe shape: /ws/logs for run logs and /ws/agents for live agent runs.
OpenAPI
The server mounts Swagger UI at /docs and serves an OpenAPI document for every route group (tagged Objects, Telemetry, Actions, Workflows, Datasets, Syncs, Pipelines, Projections, Auth, and more). Zod schemas are converted to JSON Schema automatically, so the docs stay in sync with route validation. The generated typed client in packages/client is built from this contract — when routes or schemas change, run bun run generate:client.
Auth, CSRF, and cookies
Auth is enforced centrally. On every request the server resolves the session once and applies the caller's grants to protected domain operations. When auth is disabled explicitly, protected routes use that project configuration rather than bypassing authorization through a privileged runtime.
| Mechanism | Detail |
|---|---|
| Session | /auth/sign-in starts the flow; /auth/callback mints the httpOnly session cookie. |
| CSRF | Double-submit: a sixb_csrf cookie plus a matching x-sixb-csrf request header on mutations. |
| Browser origin | Disallowed Origin headers get a 403 before routing; CORS is restricted to allowedOrigins. |
Browser front-ends call the API with credentials (cookies) and echo the CSRF token in the x-sixb-csrf header on writes. Server-to-server callers authenticate per your auth provider. See Authentication and Authorization for the full model.
Admin UI (atlas) vs custom apps
The API server serves no HTML. Front-ends are separate servers pointed at apiBaseUrl:
- atlas — the built-in admin UI (
@sixb/atlas).sixb devstarts it automatically alongside the API. It is a browser client for the same/apiroutes, scoped to theatlasaudience. - custom app — your own front-end under the project's app directory, served with the
appaudience. See Apps.
Both are ordinary API clients. To build one, consume the typed Client and the Objects HTTP reference.
Related
- Client — the generated typed client over these routes
- Apps — building a custom front-end
- Authentication and Authorization
- Objects HTTP reference