Skip to main content
A world directory holds three authored files and a set of generated ones. The keys below are the ones you edit by hand. 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

All three keys are required. 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

Tool names match [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

When "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.
A link is a manifest edit, so it mints a version and shows up in History. Link worlds together has the mapping grammar, the transform set and the cross-world query.

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