connector.toml is optional. A world with no vendor to mirror declares its tools in
schema/world.json and serves them without it.
schema/world.json is a JSON Schema document with one extra block
The file is a standard JSON Schema 2020-12 document. Reusable shapes go in $defs; the
world’s own declarations go in a top-level x-gateway object.
$schema must be exactly https://json-schema.org/draft/2020-12/schema. $id must be an
absolute, fragment-free URI; nothing ever fetches it, so a urn: is fine. References resolve
only to #/$defs/<name> in the same file or sibling.json#/$defs/<name> next to it — remote
references are refused.
Every x-gateway member is required
Unknown members are refused by name, not ignored. A world that serves an HTTP API rather than
tools still declares
"tools": {}.
An entity names a closed object and its keys
An entity root must be a direct closed object:
"type": "object" with
"additionalProperties": false and no allOf, anyOf or oneOf at the root. Reuse a shape
through $ref, never through composition — the compiler turns the root into the
database engine’s columns.
Primary-key fields must be direct property names, listed in the schema’s required, typed
string or integer, and never nullable. unique keys may reach nested values, written as a
JSON Pointer such as /owner/user_id — a dotted path is refused with a message saying so.
Entity and field names are SQL-safe, with reserved prefixes
Entity names match[A-Za-z][A-Za-z0-9_]*. Field names match [A-Za-z_][A-Za-z0-9_]*. Both
refuse the prefixes sqlite_, gateway_ and benchmark_; an entity name may not start with
_, and a field may not be called _key or payload. Two entities whose names differ only
in case collide under SQL case folding and are refused.
A relationship is restrict-only
on_delete must be "restrict", so delete dependents explicitly,
in one batch. The target must be a declared entity
whose primary key has the same number of fields and matching types, and one field tuple may
name only one target.
A tool declares its input and output schemas
[A-Za-z][A-Za-z0-9_.-]*. input and output are required and description
is optional text; the input must resolve to an object schema. Every name declared here must
also appear in [tools] serve in gateway-env.toml, exactly — a mismatch in either direction
fails the compile.
The compiler writes five files back
gateway worlds schema compile regenerates the paths below from schema/world.json. Edit the
source and recompile; never edit a generated file.
gateway worlds schema entity add scaffolds an entity into schema/world.json for you,
gateway worlds schema entity remove takes one out once nothing refers to it, and
gateway worlds schema reconcile rewrites the contract to match what a vendor actually
returned in captures/.gateway-env.toml says what kind of world this is
gateway worlds schema init writes the manifest below. The compiler or the platform reads
every table in it.
A directory counts as a schema world only if its manifest carries
[schema]. Drop the table
and the world stops compiling.
[actions] on_push runs the world’s own tests on every push
"tests" is listed, every pushed version has
the suite in tests/ run against it and the run lands on the version for the Tests tab and
gateway bench tests to read. A push never waits on its tests and never fails because of them.
See Test a world.
[dependencies] links this world to another
connector.toml describes the vendor
One file serves three jobs: what gateway worlds connector capture calls, what
gateway worlds serve answers, and what the hosted world answers. Mock any vendor
API walks through writing one; the tables below list every key.
[connector] identifies the connection
[auth] names the credential without holding it
token_url, client_id_env and scope are refused on any scheme but oauth2.
[capture] paces the real vendor and follows its cursor
cursor_field and cursor_header are mutually exclusive, and cursor_param must be paired
with one of them.
[redact] decides what a captured value becomes
A field belongs to exactly one of the four. Keep real data out has the
path grammar, the precedence order, the salt rule, and which commands apply redaction and
which do not.
[[operations]] declares one route
A name may not be both a path argument and a query parameter.
[operations.ingest] reshapes vendor rows into your entities
Every source named in
carry_args must be a path argument or a declared parameter of the same
operation, and any ingest projection requires the operation to name an entity.
[operations.api] decides how that route is served
[api] sets the world-wide envelope, paging and errors
[ui] ships a dashboard with the world
A world serves the ui session surface only when it declares one. The table points at a
prebuilt directory — the World Host serves those files directly, and a world whose UI
exists only as a container image needs a pod instead. The shipped templates declare one, and
gateway worlds schema compile generates the directory from the contract; see
Give a world a UI.
Build the UI into a directory such as
ui/static. bench push drops dist, build,
outputs, __pycache__ and dot-directories from every bundle as build artifacts, so
static pointing inside one is refused at validation rather than failing every session.Where to go next
- Mock any vendor API for writing
connector.tomlstep by step. - Keep real data out for the full
[redact]model. - Link worlds together for
[dependencies]mappings and transforms. - Test a world for the suite
[actions] on_pushruns. - Getting started with worlds for the path from an empty directory.