This profile is the standard way for a library or service to publish its
operations so tama-dex can index, search and execute them. A profile document
is an ordinary OpenAPI 3.1 document with a few constraints and x-tama-*
extensions. Producers build it as plain data. They don't depend on tama-dex at
runtime, but they may use Tama.Dex.Profile.validate/1 as a test-only
dependency to check conformance.
Known producers are opsmaru_incus (from 0.2.0) and Macus (its runtime
operations). tama-dex never depends on a producer; producers publish documents
as data.
The key words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119.
Document
| Field | Rule |
|---|---|
openapi | MUST match 3.1.x. |
jsonSchemaDialect | MAY be absent, or MUST be https://spec.openapis.org/oas/3.1/dialect/base or https://json-schema.org/draft/2020-12/schema. Schemas are validated as JSON Schema 2020-12. |
info.title, info.version | MUST be present. info.version is the producer's release version, for example 0.2.0. |
x-tama-profile | MUST be exactly tama-operations/1. |
x-tama-source | MUST match ^[a-z][a-z0-9_]{0,63}$. This is the stable source ID that executors are registered under, for example opsmaru_incus or macus. |
paths | MUST contain only operation paths (see below). |
components.schemas | MAY hold shared schemas, named as OpenAPI requires (^[a-zA-Z0-9._-]+$). A $ref MUST point into #/components/schemas/ of the same document. External references are rejected. |
webhooks | MUST be absent. |
Other OpenAPI fields, such as servers, tags, security and
externalDocs, and other x- extensions are allowed at the document level
and ignored. Unknown x-tama-* keys are rejected.
The whole serialized document MUST be at most 4 MiB and hold at most 1024 operations.
Operations
Each operation is one path item, /operations/{operationId} with the literal
operation ID, holding exactly one post operation and nothing else. The path
and method are naming conventions only. tama-dex never makes HTTP calls to
them.
An operation MAY hold only the fields in this table and other x-
extensions. HTTP fields such as parameters, security or servers are
rejected.
| Field | Rule |
|---|---|
operationId | MUST match ^[a-z][a-z0-9_]{0,127}$ and equal the path suffix. It MUST be unique within the document, and within the cassette the document is installed in. The same ID MAY appear in other cassettes; hosts address operations by cassette and operationId. |
summary | MUST be present, at most 120 characters. |
description | MUST be present, at most 4096 characters. |
tags | MAY list up to 16 unique strings of at most 64 characters, for example the resource name. |
deprecated | MAY be true. Deprecated operations stay indexed and are marked in results. |
requestBody | MUST be required: true, with exactly one application/json media type whose schema is an object schema ("type": "object"). It MAY have a description. |
responses | MUST contain "200" with an application/json schema for a successful result. It MAY contain "default" with an application/json schema for an error result. No other response keys are allowed. Each response has a description, as OpenAPI requires. |
x-tama-scopes | MUST be a non-empty array of unique scope tokens matching ^[A-Za-z0-9._:-]{1,128}$. A caller needs every listed scope. |
x-tama-destructive | MUST be a boolean. |
x-tama-confirmation | MAY be {"field": <name>, "description": <text>}. field MUST name a required top-level string property of the input schema. description (at most 256 characters) tells the caller what value to send, for example "the full fingerprint". |
x-tama-bounds | MAY be an object of non-negative integers keyed by ^[a-z][a-z0-9_]{0,63}$, for example max_items. It is informational: returned in results, never enforced by Tama.Dex. |
x-tama-identity | MAY list up to 8 unique top-level property names declared by the "200" or "default" schema, for example operation_id or status. When a dispatched result fails validation, tama-dex copies those properties' string, integer or boolean values (each at most 256 bytes) into its invalid_output error, so callers can reconcile a call that may already have taken effect. |
Other x-<prefix>-* | Kept as opaque metadata (at most 16 KiB per operation) and returned with full operation detail, for example x-opsmaru-requires. |
Where these rules refer to a schema's top-level type, properties or
required, or to a property's type, they mean that schema's own keywords. A
schema that is only a $ref into #/components/schemas/, optionally with
annotations such as title or description, is read through to its target.
Keywords inside allOf, anyOf, oneOf, not, if/then/else or
$defs are never consulted. So an input schema that declares its properties
only inside allOf has no top-level properties, and can't name a
confirmation field.
$ref and $dynamicRef MUST be JSON Pointers into #/components/schemas/
of the same document, such as #/components/schemas/DeleteImage, and MUST
resolve. Schemas MUST NOT declare $id, which would change what those
pointers resolve against. A media type object holds only its schema.
Each request or response schema, together with the components it references, MUST be at most 256 KiB once serialized.
Execution semantics
- tama-dex validates
requestBodyinput before dispatch, and the executor's result after it. An{:ok, result}MUST match the"200"schema. An{:error, error}MUST match"default"when one is declared. - tama-dex adds no deadline of its own around the executor. Long-running
operations (for example a runtime start bounded by
remaining_seconds) are limited only by the executor and host.x-tama-boundsdocuments those limits but is not enforced. - Every tama-dex error says whether the executor was called. Errors before
dispatch carry
dispatched: false, meaning the operation did not run.dispatch_failedandinvalid_outputcarrydispatched: true, meaning the operation may have taken effect and MUST NOT be retried blindly. - When
x-tama-confirmationis present, tama-dex rejects a call whose confirmation field is missing or empty. The producer's executor still checks the value itself. - Scopes are checked against the caller's verified scopes on every
execute. Search hides operations the caller cannot run.
Example
{
"openapi": "3.1.1",
"info": {"title": "opsmaru_incus operations", "version": "0.2.0"},
"x-tama-profile": "tama-operations/1",
"x-tama-source": "opsmaru_incus",
"paths": {
"/operations/incus_image_delete": {
"post": {
"operationId": "incus_image_delete",
"summary": "Delete an image",
"description": "Delete an image by fingerprint and wait for the operation.",
"tags": ["image"],
"x-tama-scopes": ["incus.delete"],
"x-tama-destructive": true,
"x-tama-confirmation": {"field": "confirm", "description": "the full fingerprint"},
"x-tama-bounds": {"max_wait_seconds": 60, "max_output_bytes": 262144},
"x-tama-identity": ["operation_id", "status"],
"x-opsmaru-requires": ["DeleteImage", "GetOperationWait"],
"requestBody": {
"required": true,
"content": {"application/json": {"schema": {"type": "object", "properties": {"fingerprint": {"type": "string"}, "confirm": {"type": "string"}}, "required": ["fingerprint", "confirm"], "additionalProperties": false}}}
},
"responses": {
"200": {"description": "Result", "content": {"application/json": {"schema": {"type": "object", "properties": {"operation_id": {"type": "string"}, "status": {"type": "string"}}, "required": ["operation_id", "status"]}}}},
"default": {"description": "Error", "content": {"application/json": {"schema": {"type": "object", "required": ["error"]}}}}
}
}
}
}
}Versioning
Unknown x-tama-* keys, at document or operation level, are rejected. A new
x-tama-* field, a change that would reject a previously valid document, or a
change in the meaning of a field each need a new profile version
(tama-operations/2). tama-dex rejects profile versions it doesn't know.