{
"x-comment": "Generated from openapi/tensor-wasm-api.yaml. Do NOT edit by hand -- edit the YAML and re-export this JSON (see scripts/regen-openapi-json.{sh,ps1}). The openapi_json_yaml_sync regression test enforces path-key parity at CI time.",
"openapi": "3.1.0",
"info": {
"title": "TensorWasm HTTP API",
"description": "REST surface of a Craton TensorWasm serverless Wasm node. Prose\ndocumentation, error semantics, and the full middleware stack live in\ncrates/tensor-wasm-api/API.md.\n",
"version": "0.3.7",
"license": {
"name": "Apache-2.0",
"identifier": "Apache-2.0"
},
"contact": {
"name": "Craton Software Company",
"email": "security@craton.com.ar",
"url": "https://github.com/craton-co/craton-tensor-wasm"
}
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Local development server"
}
],
"security": [
{
"BearerAuth": []
}
],
"tags": [
{
"name": "lifecycle",
"description": "Function deploy / delete"
},
{
"name": "invoke",
"description": "Synchronous and asynchronous invocations"
},
{
"name": "jobs",
"description": "Async-job poll API"
},
{
"name": "ops",
"description": "Operator-facing endpoints (health, metrics)"
},
{
"name": "kernels",
"description": "Kernel registry (roadmap feature #3, B6.4). The routes under this\ntag are only mounted when the gateway binary is built with\n`--features kernel-registry-api`; on the default build they are\nabsent and a request to them 404s at the router. When the feature\nis on but `TENSOR_WASM_API_KERNEL_HMAC_KEY` is unset the handlers\nreturn `503 kernel_registry_not_configured`. See\ncrates/tensor-wasm-api/src/kernels.rs and\ndocs/KERNEL-REGISTRY.md.\n"
},
{
"name": "openai-compat",
"description": "OpenAI-compatible inference gateway (T41, v0.4). The two routes\nunder this tag accept the public OpenAI REST request shapes so\noff-the-shelf SDKs (Python `openai`, Node `openai`, LangChain,\nLlamaIndex, ...) can target the gateway URL without modification.\nThe handlers resolve the request's `model` field against an\nenv-configured `model -> function_uuid` map\n(`TENSOR_WASM_API_OPENAI_MODEL_MAP`) and dispatch through the\nshared executor. See `docs/OPENAI-COMPAT.md` for the wire\ncontract and the operator configuration knob.\n"
},
{
"name": "snapshot",
"description": "Snapshot save / restore (M5). `/snapshot/save` captures a deployed\nfunction into an HMAC-SHA256-signed snapshot blob and returns it;\n`/snapshot/restore` verifies the signature and decodes the\nblob's provenance. Both routes consume the operator-configured\n`TENSOR_WASM_API_SNAPSHOT_HMAC_KEY`; when it is unset they return\n`503 snapshot_signing_not_configured`. The routes sit on the\nprotected stack (bearer auth + per-tenant scope + per-tenant\nownership). See crates/tensor-wasm-api/src/routes.rs and\ncrates/tensor-wasm-api/src/config.rs.\n"
}
],
"paths": {
"/healthz": {
"get": {
"tags": [
"ops"
],
"summary": "Liveness probe",
"operationId": "healthz",
"security": [],
"responses": {
"200": {
"description": "Service is alive",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Health"
}
}
}
}
}
}
},
"/metrics": {
"get": {
"tags": [
"ops"
],
"summary": "Prometheus text exposition",
"operationId": "metrics",
"description": "Open by default so Prometheus scrapers and k8s tooling can hit it\nwithout a bearer token. Operators may opt into a bearer-token gate\nby setting `TENSOR_WASM_API_METRICS_TOKEN`; when that env var is set\nthe endpoint requires `Authorization: Bearer <token>` and returns\n`401` otherwise.\n",
"security": [],
"responses": {
"200": {
"description": "Prometheus 0.0.4 text exposition",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/functions": {
"post": {
"tags": [
"lifecycle"
],
"summary": "Deploy a new Wasm module",
"operationId": "createFunction",
"parameters": [
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateFunctionRequest"
}
}
}
},
"responses": {
"200": {
"description": "Function deployed",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateFunctionResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/functions/{id}": {
"delete": {
"tags": [
"lifecycle"
],
"summary": "Delete a deployed function",
"operationId": "deleteFunction",
"parameters": [
{
"$ref": "#/components/parameters/FunctionId"
},
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"responses": {
"204": {
"description": "Function removed",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/functions/{id}/invoke": {
"post": {
"tags": [
"invoke"
],
"summary": "Invoke a function synchronously",
"operationId": "invokeFunction",
"parameters": [
{
"$ref": "#/components/parameters/FunctionId"
},
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvokeRequest"
}
}
}
},
"responses": {
"200": {
"description": "Invocation complete",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvokeResult"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"504": {
"$ref": "#/components/responses/InvokeTimeout"
}
}
}
},
"/functions/{id}/invoke-async": {
"post": {
"tags": [
"invoke"
],
"summary": "Invoke a function asynchronously",
"operationId": "invokeFunctionAsync",
"parameters": [
{
"$ref": "#/components/parameters/FunctionId"
},
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvokeRequest"
}
}
}
},
"responses": {
"202": {
"description": "Job accepted",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvokeAsyncResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/functions/{id}/invoke-stream": {
"post": {
"tags": [
"invoke"
],
"summary": "Invoke a function and stream the response (SSE or chunked).",
"description": "Streaming counterpart of `/functions/{id}/invoke`. The response\nshape is selected from the request's `Accept` header:\n\n* `Accept: text/event-stream` — Server-Sent Events. Each chunk\n the guest emits via the `wasi:tensor/host.emit-chunk` host\n function is rendered as one `event: chunk` frame. The stream\n terminates with an `event: done` frame on success or an\n `event: error` frame on guest failure / deadline-elapsed.\n* Otherwise — `Content-Type: application/octet-stream`,\n `Transfer-Encoding: chunked`. Each guest chunk is forwarded\n verbatim as one HTTP chunk frame, followed by the same\n terminal `event: done` / `event: error` line so clients on\n either negotiation outcome detect end-of-stream uniformly.\n\n### v0.4 wiring (T34)\n\nThe route is wired end-to-end through\n`tensor_wasm_wasi_gpu::StreamingContext`. Guest emits land on a\n`tokio::sync::mpsc::Receiver<Vec<u8>>` the gateway drains into\nthe response body. The cooperative-deadline path (T36) routes\na deadline-elapsed signal into a final `event: error` with\n`{\"reason\":\"deadline_elapsed\"}` so SSE clients can distinguish\nit from a generic trap. See `docs/STREAMING.md`.\n",
"operationId": "invokeFunctionStream",
"parameters": [
{
"$ref": "#/components/parameters/FunctionId"
},
{
"$ref": "#/components/parameters/TenantHeader"
},
{
"name": "Accept",
"in": "header",
"required": false,
"description": "Set to `text/event-stream` to receive SSE framing. Any\nother value (or absence) selects the\n`application/octet-stream` chunked-transfer branch.\n",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"description": "Arbitrary JSON payload, same shape as the synchronous\ninvoke endpoint. The v0.x executor ignores the value.\n"
}
}
}
},
"responses": {
"200": {
"description": "Streaming response. The body framing depends on the\nnegotiated content type — see the operation summary.\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"text/event-stream": {
"schema": {
"type": "string",
"description": "Server-Sent Events stream. Each emitted chunk is\none `event: chunk` frame; the stream ends with an\n`event: done` frame on success or an `event: error`\nframe on failure / deadline-elapsed.\n"
}
},
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary",
"description": "Raw chunked-transfer bytes — one HTTP chunk per\nguest-emitted chunk, followed by a final terminal\n`event: done` / `event: error` line.\n"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/v1/completions": {
"post": {
"tags": [
"openai-compat"
],
"summary": "OpenAI-compatible text completions (T41 wired)",
"description": "Accepts the public OpenAI `POST /v1/completions` request shape\nand dispatches to the deployed function the gateway operator\nhas aliased to the requested `model` (via\n`TENSOR_WASM_API_OPENAI_MODEL_MAP`). Returns the standard OpenAI\n`text_completion` envelope on success, or\n`404 model_not_found` when the model alias is unknown.\n\nWhen `stream: true`, returns `text/event-stream` with one\n`data: { ... }` SSE frame per emitted chunk and a terminal\n`data: [DONE]\\n\\n` line.\n\nThe `X-TensorWasm-Tenant` header is not honoured on this\nroute, because OpenAI SDKs never send it. Tenant resolution\ncomes from the bearer token's scope (see\n`docs/OPENAI-COMPAT.md`).\n",
"operationId": "openaiCompletions",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CompletionsRequest"
}
}
}
},
"responses": {
"200": {
"description": "Completion result (or SSE stream when `stream:true`).",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CompletionsResponse"
}
},
"text/event-stream": {
"schema": {
"type": "string",
"description": "SSE stream. Each guest chunk lands as one `data: { ... }`\nframe whose JSON carries the OpenAI delta shape; the\nstream terminates with a literal `data: [DONE]\\n\\n` line.\n"
}
}
}
},
"400": {
"description": "Malformed request body (OpenAI envelope).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OpenAiError"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"description": "The requested `model` is not configured in the gateway's\n`TENSOR_WASM_API_OPENAI_MODEL_MAP`. Carries OpenAI envelope\n`code: \"model_not_found\"`.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OpenAiError"
}
}
}
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/v1/chat/completions": {
"post": {
"tags": [
"openai-compat"
],
"summary": "OpenAI-compatible chat completions (T41 wired)",
"description": "Accepts the public OpenAI `POST /v1/chat/completions` request\nshape (system / user / assistant `messages` array) and\ndispatches to the function the operator has aliased to the\nrequested `model`. The `messages` array is concatenated into a\nsingle role-tagged prompt string before dispatch (`system:\n...\\nuser: ...\\nassistant:`).\n\nReturns the standard OpenAI `chat.completion` envelope on\nsuccess, or `404 model_not_found` when the alias is unknown.\nStreaming framing matches `/v1/completions`, except the JSON\ndelta uses the chat shape (`choices[0].delta.content`) and the\nobject kind is `chat.completion.chunk`.\n",
"operationId": "openaiChatCompletions",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatCompletionsRequest"
}
}
}
},
"responses": {
"200": {
"description": "Chat completion (or SSE stream when `stream:true`).",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatCompletionsResponse"
}
},
"text/event-stream": {
"schema": {
"type": "string",
"description": "SSE stream. Each guest chunk lands as one `data: { ... }`\nframe whose JSON carries the OpenAI chat delta shape; the\nstream terminates with a literal `data: [DONE]\\n\\n` line.\n"
}
}
}
},
"400": {
"description": "Malformed request body (OpenAI envelope).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OpenAiError"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"description": "The requested `model` is not configured in the gateway's\n`TENSOR_WASM_API_OPENAI_MODEL_MAP`. Carries OpenAI envelope\n`code: \"model_not_found\"`.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/OpenAiError"
}
}
}
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/jobs/{id}": {
"get": {
"tags": [
"jobs"
],
"summary": "Poll an async-invocation job",
"operationId": "getJob",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"responses": {
"200": {
"description": "Current job status",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/JobRecord"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"429": {
"$ref": "#/components/responses/RateLimited"
}
}
}
},
"/snapshot/save": {
"post": {
"tags": [
"snapshot"
],
"summary": "Capture a deployed function into a signed snapshot blob",
"description": "Captures the deployed Wasm module bytes of the named function into\na zstd-compressed, HMAC-SHA256-signed snapshot blob and returns it\nbase64-encoded. The blob round-trips through `POST\n/snapshot/restore`.\n\nAuthorization: protected stack (bearer auth + tenant scope). The\nhandler runs `authorize_tenant` against the bearer token's scope\n(an out-of-scope token gets `403 tenant_scope_denied` before any\nlookup) and then a per-resource owner check against the function's\n`tenant_id` (a wildcard-scoped caller from another tenant cannot\nsnapshot tenant A's function).\n\nSigning uses `TENSOR_WASM_API_SNAPSHOT_HMAC_KEY`; when it is unset\nthe route returns `503 snapshot_signing_not_configured` rather than\nemitting an unsigned blob.\n\nNOTE: this captures the function's *deployed module bytes* — the\nworking save/restore-of-bytes layer with end-to-end HMAC signing.\nCapturing a *live running instance's* linear / GPU memory needs an\nexecutor capture hook that does not exist yet; requesting that\ncapability surfaces `501 not_implemented`. See\ncrates/tensor-wasm-api/src/routes.rs.\n",
"operationId": "snapshotSave",
"parameters": [
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SnapshotSaveRequest"
}
}
}
},
"responses": {
"200": {
"description": "Snapshot captured and signed; blob returned base64.",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SnapshotSaveResponse"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"501": {
"$ref": "#/components/responses/NotImplemented"
},
"503": {
"$ref": "#/components/responses/SnapshotSigningUnavailable"
}
}
}
},
"/snapshot/restore": {
"post": {
"tags": [
"snapshot"
],
"summary": "Verify and decode a signed snapshot blob",
"description": "Accepts a base64-encoded snapshot blob previously produced by\n`POST /snapshot/save`, verifies its HMAC-SHA256 signature against\n`TENSOR_WASM_API_SNAPSHOT_HMAC_KEY`, and returns the\nHMAC-authenticated provenance (captured tenant, instance id,\nuncompressed size, wire version).\n\nHardening: the reader always requires a signature\n(`require_signature`) so a stripped-trailer downgrade to an\nunsigned v2 blob is refused;\n`TENSOR_WASM_API_SNAPSHOT_REQUIRE_SIGNATURE` is the operator\nsurface for this posture. A 256 MiB decompression cap bounds\nrestore-time memory pressure.\n\nAuthorization: protected stack (bearer auth + tenant scope). After\nHMAC verification recovers the snapshot's captured tenant, the\nhandler enforces that it equals the caller's resolved tenant — a\ncross-tenant restore is `403 tenant_scope_denied`. A wrong /\nmissing key or a tampered blob is `403 snapshot_signature_invalid`\n(a single opaque rejection, not a decode oracle).\n\nNOTE: this verifies and decodes the snapshot *envelope*.\nReconstituting a *live running instance* from the captured memory\nneeds an executor restore hook that does not exist yet; requesting\nthat capability surfaces `501 not_implemented`.\n",
"operationId": "snapshotRestore",
"parameters": [
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SnapshotRestoreRequest"
}
}
}
},
"responses": {
"200": {
"description": "Snapshot signature verified; provenance returned.",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SnapshotRestoreResponse"
}
}
}
},
"400": {
"description": "`invalid_json` (malformed request body) or `invalid_base64`\n(the `snapshot_b64` field is not valid base64). Carries the\nnative `{error:{kind,message}}` envelope.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"description": "`tenant_scope_denied` (the bearer token is not scoped to the\nrequested tenant, or the snapshot was captured for a different\ntenant) or `snapshot_signature_invalid` (HMAC verification\nfailed, the blob is unsigned, or it is malformed). Carries the\nnative `{error:{kind,message}}` envelope.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"501": {
"$ref": "#/components/responses/NotImplemented"
},
"503": {
"$ref": "#/components/responses/SnapshotSigningUnavailable"
}
}
}
},
"/kernels": {
"post": {
"tags": [
"kernels"
],
"summary": "Publish a signed kernel manifest + PTX text",
"description": "FEATURE-GATED: this route is only mounted when the gateway is\nbuilt with `--features kernel-registry-api`; the default build\ndoes not register it (request 404s at the router). When the\nfeature is on but `TENSOR_WASM_API_KERNEL_HMAC_KEY` is unset the\nhandler returns `503 kernel_registry_not_configured`.\n\nPublishes a signed `KernelManifest` plus its PTX source. The\nregistry re-verifies that `BLAKE3(ptx_text)` matches\n`manifest.digest` and that the manifest's HMAC-SHA256 signature\nverifies under the server's configured key before persisting.\n\nAuthorization is stricter than the other routes: in addition to\n`bearer_auth` + `tenant_scope`, the caller's bearer token must\nappear in the `TENSOR_WASM_API_KERNEL_PUBLISH_TOKENS` allowlist\n(the **kernel-publish** scope). Dev mode (empty\n`TENSOR_WASM_API_TOKENS`) rejects every publish with\n`403 kernel_publish_disabled_in_dev_mode`. See\ncrates/tensor-wasm-api/src/kernels.rs.\n",
"operationId": "publishKernel",
"parameters": [
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublishKernelRequest"
}
}
}
},
"responses": {
"201": {
"description": "Kernel published; canonical name/version echoed back.",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublishKernelResponse"
}
}
}
},
"400": {
"description": "`digest_mismatch` (BLAKE3 of `ptx_text` did not match\n`manifest.digest`) or `invalid_request` (other registry\nrejection). Carries the native `{error:{kind,message}}`\nenvelope.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"description": "`kernel_publish_disabled_in_dev_mode` (gateway in dev mode),\n`kernel_publish_scope_required` (token not in\n`TENSOR_WASM_API_KERNEL_PUBLISH_TOKENS`), `bad_signature`\n(HMAC verification failed), or `publisher_not_allowed`\n(manifest publisher not in the registry allowlist).\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"409": {
"description": "`already_registered` — a manifest with the same\n`name@version` is already present.\n",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"413": {
"$ref": "#/components/responses/PayloadTooLarge"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"503": {
"$ref": "#/components/responses/KernelRegistryUnavailable"
}
}
},
"get": {
"tags": [
"kernels"
],
"summary": "List registered kernel manifests (PTX omitted)",
"description": "FEATURE-GATED: only mounted under `--features\nkernel-registry-api`. Returns `503\nkernel_registry_not_configured` when the feature is on but\n`TENSOR_WASM_API_KERNEL_HMAC_KEY` is unset.\n\nLists manifests in unspecified order; PTX text is omitted (use\n`GET /kernels/{name}/{version}` to fetch source). Any\nauthenticated tenant may list. `limit` is clamped to 1000\nserver-side; the response echoes the effective `offset` /\n`limit`.\n",
"operationId": "listKernels",
"parameters": [
{
"$ref": "#/components/parameters/TenantHeader"
},
{
"name": "offset",
"in": "query",
"required": false,
"description": "Index of the first manifest to return; defaults to 0.",
"schema": {
"type": "integer",
"minimum": 0
}
},
{
"name": "limit",
"in": "query",
"required": false,
"description": "Maximum manifests in the returned page; defaults to 100,\nclamped to 1000 server-side.\n",
"schema": {
"type": "integer",
"minimum": 1
}
}
],
"responses": {
"200": {
"description": "Page of manifests with the effective pagination echoed.",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ListKernelsResponse"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"503": {
"$ref": "#/components/responses/KernelRegistryUnavailable"
}
}
}
},
"/kernels/{name}/{version}": {
"get": {
"tags": [
"kernels"
],
"summary": "Resolve a kernel manifest + PTX text",
"description": "FEATURE-GATED: only mounted under `--features\nkernel-registry-api`. Returns `503\nkernel_registry_not_configured` when the feature is on but\n`TENSOR_WASM_API_KERNEL_HMAC_KEY` is unset.\n\nResolves a single manifest plus its PTX source by\n`name`/`version`. Any authenticated tenant may resolve. Path\nsegments are accepted verbatim; clients must percent-encode any\nreserved characters in `name` or `version`.\n",
"operationId": "resolveKernel",
"parameters": [
{
"name": "name",
"in": "path",
"required": true,
"description": "Kernel name segment.",
"schema": {
"type": "string"
}
},
{
"name": "version",
"in": "path",
"required": true,
"description": "Kernel version segment.",
"schema": {
"type": "string"
}
},
{
"$ref": "#/components/parameters/TenantHeader"
}
],
"responses": {
"200": {
"description": "Resolved manifest and its PTX text.",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ResolveKernelResponse"
}
}
}
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"429": {
"$ref": "#/components/responses/RateLimited"
},
"503": {
"$ref": "#/components/responses/KernelRegistryUnavailable"
}
}
}
}
},
"components": {
"securitySchemes": {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"description": "Bearer token from the TENSOR_WASM_API_TOKENS allowlist. Each\nentry may carry a `:tenant=...` clause restricting the token to\na subset of tenants; see API.md `Per-tenant scopes`. An empty\nallowlist puts the gateway in dev mode (auth disabled).\n"
}
},
"parameters": {
"FunctionId": {
"name": "id",
"in": "path",
"required": true,
"description": "Server-assigned function identifier (UUIDv4).",
"schema": {
"type": "string",
"format": "uuid"
}
},
"TenantHeader": {
"name": "X-TensorWasm-Tenant",
"in": "header",
"required": false,
"description": "Tenant scope (u64). Defaults to 0 if absent. Mandatory when the\ngateway runs with `TENSOR_WASM_API_REQUIRE_TENANT=1`.\n",
"schema": {
"type": "integer",
"format": "int64",
"minimum": 0
}
}
},
"headers": {
"XRequestId": {
"description": "Per-request UUID stamped by the audit middleware. Operators can\ncorrelate audit records, structured logs, and trace events by\nthis id.\n",
"schema": {
"type": "string",
"format": "uuid"
}
},
"XTraceId": {
"description": "W3C `trace-id` (32 lowercase hex chars) of the current span.\nEmpty string if no trace context is active.\n",
"schema": {
"type": "string"
}
},
"RetryAfter": {
"description": "Per RFC 9110 §10.2.3, integer seconds until the next request is\nlikely to succeed. Always at least 1.\n",
"schema": {
"type": "integer",
"minimum": 1
}
}
},
"responses": {
"BadRequest": {
"description": "Validation failure",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"Unauthorized": {
"description": "Missing or unrecognised bearer token",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"Forbidden": {
"description": "Bearer token is not scoped to the requested tenant",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"NotFound": {
"description": "Function or job id is unknown",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"PayloadTooLarge": {
"description": "Request body exceeded the 64 MiB cap enforced by axum's\n`DefaultBodyLimit::max`. Often rendered as a bare 413 with no\nbody when the limit is hit during streaming.\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
}
},
"RateLimited": {
"description": "Per-token QPS + burst exceeded. The `Retry-After` header carries\nthe wait in integer seconds (RFC 9110 §10.2.3).\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
},
"Retry-After": {
"$ref": "#/components/headers/RetryAfter"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"InternalError": {
"description": "Underlying wasmtime or host failure",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"InvokeTimeout": {
"description": "Invocation exceeded its per-call deadline",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"KernelRegistryUnavailable": {
"description": "Kernel registry routes are mounted but the backing registry is\nnot configured: `TENSOR_WASM_API_KERNEL_HMAC_KEY` is unset, so\nthe handler returns `503 kernel_registry_not_configured`. (The\npublish path can also surface `kernel_registry_storage_error`\nhere on a backend I/O failure.)\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"NotImplemented": {
"description": "`not_implemented` — the requested capability needs executor\nsupport that is not wired yet (full live-instance snapshot /\nrestore of linear + GPU memory). The HMAC envelope layer of the\n`/snapshot/*` routes is fully wired; only the live-instance\nreconstitution returns this. Clients should NOT retry.\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
},
"SnapshotSigningUnavailable": {
"description": "`snapshot_signing_not_configured` — the `/snapshot/*` routes are\nmounted but `TENSOR_WASM_API_SNAPSHOT_HMAC_KEY` is unset, so the\ngateway has no key to sign or verify snapshot blobs. The failure\nmode is configuration, not load; a client should NOT retry but\nsurface the error to an operator who can set the key.\n",
"headers": {
"X-Request-Id": {
"$ref": "#/components/headers/XRequestId"
},
"X-Trace-Id": {
"$ref": "#/components/headers/XTraceId"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiErrorEnvelope"
}
}
}
}
},
"schemas": {
"FunctionId": {
"type": "string",
"format": "uuid",
"description": "Server-assigned function identifier (UUIDv4)."
},
"JobId": {
"type": "string",
"format": "uuid",
"description": "Server-assigned job identifier (UUIDv4)."
},
"Health": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"ok"
]
}
},
"required": [
"status"
],
"additionalProperties": false
},
"CreateFunctionRequest": {
"type": "object",
"description": "Body of `POST /functions`. Mirrors the\n`tensor_wasm_api::routes::CreateFunctionRequest` Rust struct.\n",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "Tenant-supplied display name (free-form, non-empty)."
},
"wasm_b64": {
"type": "string",
"minLength": 12,
"description": "Base64-encoded Wasm module bytes (standard alphabet,\npadded). Decoded value must validate as a complete Wasm\nmodule via `wasmparser::validate`.\n"
}
},
"required": [
"name",
"wasm_b64"
],
"additionalProperties": false
},
"CreateFunctionResponse": {
"type": "object",
"properties": {
"id": {
"$ref": "#/components/schemas/FunctionId"
}
},
"required": [
"id"
],
"additionalProperties": false
},
"InvokeRequest": {
"type": "object",
"description": "Body of POST /functions/{id}/invoke (and the async sibling).\nBoth fields are optional — an empty body or `{}` is the canonical\nno-args case, matching the pre-args wire contract. Unknown\ntop-level fields are ignored for forward compatibility; malformed\nJSON surfaces as 400 invalid_json.\n",
"properties": {
"export": {
"type": [
"string",
"null"
],
"description": "Optional export-name override. When omitted (or null), the\nserver tries `_start` first and falls back to `main`, matching\nthe WASI command convention.\n"
},
"args": {
"type": "array",
"default": [],
"description": "Argument list forwarded to the executor's `call_export_with_args`\npath. Each element is converted to the closest-fitting wasm value\ntype: integers in [-2^31, 2^31) become i32, larger integers become\ni64, non-integer numerics become f64. Strings / arrays / null /\nbooleans are rejected as 400 invalid_args. f32 cannot be selected\nfrom JSON unambiguously — write a guest wrapper that demotes from\nf64 if you need it.\n",
"items": {
"oneOf": [
{
"type": "integer",
"format": "int64"
},
{
"type": "number",
"format": "double"
}
]
}
}
},
"additionalProperties": false
},
"InvokeResult": {
"type": "object",
"properties": {
"function_id": {
"$ref": "#/components/schemas/FunctionId"
},
"result": {
"description": "Invocation result. For exports returning `()` this collapses\nto the literal string `\"ok\"` (legacy back-compat envelope).\nFor exports with a non-empty result list this is a JSON\narray of return values — one element per wasm result slot,\nwith i32/i64 rendered as JSON integers and f32/f64 as JSON\nnumbers.\n"
}
},
"required": [
"function_id",
"result"
],
"additionalProperties": false
},
"InvokeAsyncResponse": {
"type": "object",
"properties": {
"job_id": {
"$ref": "#/components/schemas/JobId"
}
},
"required": [
"job_id"
],
"additionalProperties": false
},
"JobStatus": {
"type": "string",
"enum": [
"pending",
"completed",
"failed"
],
"description": "Lifecycle state of an async invocation. `pending` jobs have no\nresult yet; `completed` and `failed` jobs carry a `result`\npayload (the InvokeResult shape and the ApiErrorBody shape,\nrespectively).\n"
},
"JobRecord": {
"type": "object",
"properties": {
"id": {
"$ref": "#/components/schemas/JobId"
},
"function_id": {
"$ref": "#/components/schemas/FunctionId"
},
"status": {
"$ref": "#/components/schemas/JobStatus"
},
"result": {
"description": "Present once `status` transitions out of `pending`. For\n`completed` jobs this is the InvokeResult JSON object;\nfor `failed` jobs it is the ApiErrorBody JSON object.\n"
},
"created_unix_ms": {
"type": "integer",
"format": "int64",
"minimum": 0
}
},
"required": [
"id",
"function_id",
"status",
"created_unix_ms"
],
"additionalProperties": false
},
"ApiErrorBody": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Stable machine-readable identifier.",
"enum": [
"invalid_json",
"invalid_name",
"invalid_base64",
"invalid_wasm",
"invalid_args",
"missing_export",
"missing_tenant",
"unauthorized",
"tenant_scope_denied",
"not_found",
"instance_not_found",
"body_too_large",
"rate_limited",
"invoke_timeout",
"wasmtime",
"internal",
"kernel_registry_not_configured",
"kernel_registry_storage_error",
"kernel_publish_disabled_in_dev_mode",
"kernel_publish_scope_required",
"publisher_not_allowed",
"bad_signature",
"digest_mismatch",
"already_registered",
"invalid_request",
"not_implemented",
"snapshot_signing_not_configured",
"snapshot_signature_invalid",
"capacity_exhausted",
"tenant_capacity_exhausted",
"module_too_large",
"module_memory_too_large",
"epoch_ticker_not_running"
]
},
"message": {
"type": "string",
"description": "Human-readable description (not part of the contract)."
}
},
"required": [
"kind",
"message"
],
"additionalProperties": false
},
"ApiErrorEnvelope": {
"type": "object",
"properties": {
"error": {
"$ref": "#/components/schemas/ApiErrorBody"
}
},
"required": [
"error"
],
"additionalProperties": false
},
"SnapshotSaveRequest": {
"type": "object",
"description": "Body of `POST /snapshot/save`. Mirrors the\n`tensor_wasm_api::routes::SnapshotSaveRequest` Rust struct.\n",
"properties": {
"function_id": {
"$ref": "#/components/schemas/FunctionId"
}
},
"required": [
"function_id"
],
"additionalProperties": false
},
"SnapshotSaveResponse": {
"type": "object",
"description": "Body of a successful `POST /snapshot/save`. Carries the full\nHMAC-SHA256-signed snapshot blob, base64-encoded.\n",
"properties": {
"function_id": {
"$ref": "#/components/schemas/FunctionId"
},
"snapshot_b64": {
"type": "string",
"description": "Base64-encoded signed snapshot blob (standard alphabet,\npadded). The exact bytes to hand back to `POST\n/snapshot/restore`; the HMAC trailer is part of these bytes.\n"
},
"signed": {
"type": "boolean",
"description": "Whether the blob carries an HMAC signature. Always `true` on\nthis path — the route is only reachable with a key configured.\n"
}
},
"required": [
"function_id",
"snapshot_b64",
"signed"
],
"additionalProperties": false
},
"SnapshotRestoreRequest": {
"type": "object",
"description": "Body of `POST /snapshot/restore`. Mirrors the\n`tensor_wasm_api::routes::SnapshotRestoreRequest` Rust struct.\n",
"properties": {
"snapshot_b64": {
"type": "string",
"minLength": 1,
"description": "Base64-encoded signed snapshot blob (standard alphabet,\npadded) previously returned by `POST /snapshot/save`.\n"
}
},
"required": [
"snapshot_b64"
],
"additionalProperties": false
},
"SnapshotRestoreResponse": {
"type": "object",
"description": "Body of a successful `POST /snapshot/restore`. Reports the\nHMAC-authenticated provenance recovered from the snapshot\nmetadata.\n",
"properties": {
"tenant_id": {
"type": "integer",
"format": "int64",
"minimum": 0,
"description": "Tenant the snapshot was originally captured for."
},
"instance_id": {
"type": "string",
"description": "Instance id stamped into the snapshot metadata at capture\ntime, rendered as the `I#<n>` display form.\n"
},
"total_uncompressed_bytes": {
"type": "integer",
"format": "int64",
"minimum": 0,
"description": "Total uncompressed payload bytes recorded in the metadata."
},
"version": {
"type": "integer",
"minimum": 0,
"description": "Snapshot wire-format version that verified (`3` for the signed\nenvelope this gateway writes).\n"
}
},
"required": [
"tenant_id",
"instance_id",
"total_uncompressed_bytes",
"version"
],
"additionalProperties": false
},
"KernelManifest": {
"type": "object",
"description": "Signed kernel manifest. Mirrors the\n`tensor_wasm_jit::registry::KernelManifest` Rust struct\n(`#[non_exhaustive]`, so additional fields may appear in future\nrevisions). `digest` and `signature` are fixed 32-byte arrays\nserialized by serde as JSON arrays of 32 integers (0-255); the\nHMAC `signature` tag is public by design (it authenticates\nauthorship, the signing key is the secret).\n",
"properties": {
"name": {
"type": "string",
"description": "Stable kernel name (e.g. `matmul.f32`)."
},
"version": {
"type": "string",
"description": "SemVer-style version (e.g. `1.0.0`)."
},
"sm_version": {
"type": "integer",
"minimum": 0,
"description": "CUDA compute capability the PTX targets (e.g. 80 for sm_80)."
},
"digest": {
"type": "array",
"description": "BLAKE3 hash of the PTX text (32 bytes).",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 32,
"maxItems": 32
},
"signature": {
"type": "array",
"description": "HMAC-SHA256 tag over the canonical signed bytes (32 bytes).",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 32,
"maxItems": 32
},
"published_unix_ms": {
"type": "integer",
"format": "int64",
"minimum": 0,
"description": "Wall-clock publish timestamp (Unix millis)."
},
"publisher": {
"type": "string",
"description": "Publisher identifier (typically a tenant id or signing-key id)."
}
},
"required": [
"name",
"version",
"sm_version",
"digest",
"signature",
"published_unix_ms",
"publisher"
]
},
"PublishKernelRequest": {
"type": "object",
"description": "Body of `POST /kernels`. Mirrors the\n`tensor_wasm_api::kernels::PublishKernelRequest` Rust struct.\n",
"properties": {
"manifest": {
"$ref": "#/components/schemas/KernelManifest"
},
"ptx_text": {
"type": "string",
"description": "PTX source. The server computes BLAKE3 over the UTF-8 bytes\nand requires a match with `manifest.digest` before the HMAC\nsignature check.\n"
}
},
"required": [
"manifest",
"ptx_text"
],
"additionalProperties": false
},
"PublishKernelResponse": {
"type": "object",
"description": "Body of a successful `201 Created` from `POST /kernels`. Echoes\nthe canonical key so the client can confirm what was stored.\n",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"name",
"version"
],
"additionalProperties": false
},
"ListKernelsResponse": {
"type": "object",
"description": "Body of `GET /kernels`. Mirrors\n`tensor_wasm_api::kernels::ListKernelsResponse`. PTX text is\nomitted from listed manifests.\n",
"properties": {
"manifests": {
"type": "array",
"items": {
"$ref": "#/components/schemas/KernelManifest"
}
},
"offset": {
"type": "integer",
"minimum": 0,
"description": "Effective offset the handler used (echoes the query param)."
},
"limit": {
"type": "integer",
"minimum": 0,
"description": "Effective limit the handler used after server-side clamping\n(max 1000).\n"
}
},
"required": [
"manifests",
"offset",
"limit"
],
"additionalProperties": false
},
"ResolveKernelResponse": {
"type": "object",
"description": "Body of `GET /kernels/{name}/{version}`. Mirrors\n`tensor_wasm_api::kernels::ResolveKernelResponse`.\n",
"properties": {
"manifest": {
"$ref": "#/components/schemas/KernelManifest"
},
"ptx_text": {
"type": "string",
"description": "PTX text whose BLAKE3 matches `manifest.digest`."
}
},
"required": [
"manifest",
"ptx_text"
],
"additionalProperties": false
},
"CompletionsRequest": {
"type": "object",
"description": "Body of `POST /v1/completions`. Mirrors the public OpenAI\ncompletions REST contract; every field is optional at the wire\nlayer so SDKs that omit knobs still parse cleanly.\n",
"properties": {
"model": {
"type": "string",
"description": "Model identifier. The gateway operator pre-aliases this to\na deployed function UUID via\n`TENSOR_WASM_API_OPENAI_MODEL_MAP`. Unknown ids surface as\n`404 model_not_found`.\n"
},
"prompt": {
"description": "Prompt text. Accepts either a single string or an array of\nstrings; arrays are joined with newlines before dispatch.\n"
},
"max_tokens": {
"type": "integer",
"minimum": 1
},
"temperature": {
"type": "number"
},
"stream": {
"type": "boolean",
"description": "When true the response is `text/event-stream` with one\n`data: { ... }` SSE frame per emitted chunk and a terminal\n`data: [DONE]\\n\\n` line.\n"
},
"echo": {
"type": "boolean"
},
"n": {
"type": "integer",
"minimum": 1
},
"user": {
"type": "string"
}
},
"additionalProperties": true
},
"ChatMessage": {
"type": "object",
"description": "One entry in the `messages` array of `POST /v1/chat/completions`.",
"properties": {
"role": {
"type": "string",
"description": "One of `system`, `user`, `assistant`, `tool`."
},
"content": {
"description": "Message content. Either a plain string or an array of\ncontent parts (multimodal). For v0.4 only text parts are\nconsumed; image / audio parts are silently dropped.\n"
},
"name": {
"type": "string"
}
},
"additionalProperties": true
},
"ChatCompletionsRequest": {
"type": "object",
"description": "Body of `POST /v1/chat/completions`. Mirrors the public OpenAI\nchat-completions REST contract.\n",
"properties": {
"model": {
"type": "string"
},
"messages": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ChatMessage"
}
},
"max_tokens": {
"type": "integer",
"minimum": 1
},
"temperature": {
"type": "number"
},
"stream": {
"type": "boolean"
},
"n": {
"type": "integer",
"minimum": 1
},
"tools": {
"description": "OpenAI tool-calling array. Accepted on the wire for forward\ncompatibility but not yet honoured; v0.5 wires.\n"
},
"user": {
"type": "string"
}
},
"additionalProperties": true
},
"CompletionsChoice": {
"type": "object",
"description": "One entry in the `choices` array of a non-streaming\n`/v1/completions` response.\n",
"properties": {
"text": {
"type": "string"
},
"index": {
"type": "integer",
"minimum": 0
},
"finish_reason": {
"type": [
"string",
"null"
],
"enum": [
"stop",
"length",
"content_filter",
null
]
},
"logprobs": {
"type": "null"
}
},
"required": [
"text",
"index"
]
},
"ChatCompletionsChoice": {
"type": "object",
"description": "One entry in the `choices` array of a non-streaming\n`/v1/chat/completions` response.\n",
"properties": {
"index": {
"type": "integer",
"minimum": 0
},
"message": {
"type": "object",
"properties": {
"role": {
"type": "string",
"enum": [
"assistant"
]
},
"content": {
"type": "string"
}
},
"required": [
"role",
"content"
]
},
"finish_reason": {
"type": [
"string",
"null"
],
"enum": [
"stop",
"length",
"content_filter",
null
]
}
},
"required": [
"index",
"message"
]
},
"CompletionsUsage": {
"type": "object",
"description": "Token-count block. v0.4 ships zeros across the board because\nthe gateway does not yet wire a tokenizer; v0.5 lands a real\ncounter (see `docs/OPENAI-COMPAT.md`).\n",
"properties": {
"prompt_tokens": {
"type": "integer",
"minimum": 0
},
"completion_tokens": {
"type": "integer",
"minimum": 0
},
"total_tokens": {
"type": "integer",
"minimum": 0
}
},
"required": [
"prompt_tokens",
"completion_tokens",
"total_tokens"
]
},
"CompletionsResponse": {
"type": "object",
"description": "Non-streaming `/v1/completions` response. The `object` field\ncarries the literal `\"text_completion\"`.\n",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"text_completion"
]
},
"created": {
"type": "integer",
"format": "int64",
"description": "Unix seconds at which the response was generated."
},
"model": {
"type": "string",
"description": "Echoes the request's `model` field."
},
"choices": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CompletionsChoice"
}
},
"usage": {
"$ref": "#/components/schemas/CompletionsUsage"
}
},
"required": [
"id",
"object",
"created",
"model",
"choices",
"usage"
]
},
"ChatCompletionsResponse": {
"type": "object",
"description": "Non-streaming `/v1/chat/completions` response. The `object`\nfield carries the literal `\"chat.completion\"`.\n",
"properties": {
"id": {
"type": "string"
},
"object": {
"type": "string",
"enum": [
"chat.completion"
]
},
"created": {
"type": "integer",
"format": "int64"
},
"model": {
"type": "string"
},
"choices": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ChatCompletionsChoice"
}
},
"usage": {
"$ref": "#/components/schemas/CompletionsUsage"
}
},
"required": [
"id",
"object",
"created",
"model",
"choices",
"usage"
]
},
"OpenAiErrorBody": {
"type": "object",
"description": "Inner body of the OpenAI error envelope. The four-field shape\n(`message`, `type`, `param`, `code`) is what off-the-shelf\nOpenAI SDKs parse; this is distinct from the gateway's native\n`ApiErrorBody`.\n",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"description": "OpenAI-conventional error category."
},
"param": {
"type": [
"string",
"null"
]
},
"code": {
"type": [
"string",
"null"
]
}
},
"required": [
"message",
"type"
]
},
"OpenAiError": {
"type": "object",
"description": "Top-level OpenAI error envelope. `code: \"model_not_found\"`\nsurfaces with status `404`; `code: \"openai_invalid_request\"`\nwith `400`; `type: \"server_error\"` with `500`.\n",
"properties": {
"error": {
"$ref": "#/components/schemas/OpenAiErrorBody"
}
},
"required": [
"error"
]
}
}
}
}