Declare it
[ui] lives in connector.toml, the file that already declares the world’s routes.
[browser] is optional and defaults to enabled with a 1280x800 viewport; enabled = false
turns the browser surface off for the world.
Generate it
ui init adds the [ui] section when connector.toml has none and compiles. Compile writes
three files into ui/static/:
gateway worlds schema compile rewrites the three files whenever the contract changes, so the
page shows the current schema. ui init needs a connector.toml, which declares the routes the
page calls.
The page renders what the routes serve:
- Home lists every entity with the route behind each verb.
- List is the entity’s
GETlist route, with the world’s own query parameters as filters and the world’s paging (cursor or offset) for the next page. - Detail is the
GETby id. A foreign key links to the related entity’s detail view. - New and Edit are the
POSTandPATCH/PUTroutes. The form asks for the fields the route’s body mapping names, or every field the server does not mint itself. Enums are selects, booleans and numbers are typed, nested objects are JSON. A handler route may carry abodymapping too (body = { channel_id = "channel", text = "text" }): the handler reads the request itself, the mapping tells the form which fields to ask for and what the vendor calls them. A2xxthat carries no row (Slack’s{"ok": false, "error": ...}) keeps the form up and shows the answer. - Delete is the
DELETEroute. - Every view prints the request it made and the status it got, so an agent reading the page sees the API call behind it.
Bring your own frontend
Any frontend that builds to static files is a world UI: React, Vue, Svelte, Angular, Solid, plain HTML, a WASM app. The host serves files and rewrites one API prefix; it does not care what produced them.- Set
generated = falseso compile never touches the directory. - Build into
staticwithindex.htmlat its root: Vitebuild.outDir, Create React AppBUILD_PATH, AngularoutputPath, all pointed atui/static; a Next.jsoutput: "export"build is copied there fromout/. Keep the build’s default base of/. - Call the API at
api_pathon the page’s own origin with plainfetch, no credential.
/assets/index-3c4d.js) resolve. A path that names no file and has no extension
returns index.html, so a client-side router’s deep links load. Hashed files under assets/ are
cached as immutable; everything else is revalidated on every request.
Export a frontend that renders on a server (Next.js without output: "export", a Django or Rails
view) as static files, or keep the server outside the world and point it at the session’s API URL.
The slack template is a worked example. Its ui/static is a Vite + React build of a Slack
client, copied in unchanged, and connector.toml declares the routes that client calls the way it
calls them: every Web API method accepts POST as well as GET (methods = ["GET", "POST"]),
/events answers the page’s EventSource with the events since a sequence number, and
/avatars/{name} serves the profile images the seed names as /avatars/ada_72.png. Assets a
frontend loads through api_path (avatars, files, thumbnails) are routes, not files under static:
the host rewrites the prefix onto the world’s API, so a [[operations]] handler has to answer
them.
How the page authorizes
The page holds no credential. A browser cannot set anAuthorization header on a page load, so the
World Host admits the UI by cookie:
- The first page load carries the session token as
?token=, the URLsession openprints asui.entry. - The host answers with an
HttpOnlycookie and a redirect to the same page without the token. - Every request after that (pages, assets, calls to
api_path) rides the cookie. The host stripsapi_pathand answers the route as it would for a caller holding the session token.
api_path call lands in the session’s call log with via: "ui", so a grader can require
that a call went through the page’s door (see the call log).
It records the door, not the client: a script holding the session token can call the same prefix.
Under gateway worlds serve the page has its own port (--ui-port, by default the one after
--port) with the same layout as the host: the page at / and its api_path calls answered with
the local pin. The same files are at /__ui/ on --port. The bare routes still require the key, and
the prefix answers the page only: a browser call from another site is refused. Only the UI port’s
prefix counts as via: "ui"; the same prefix on --port is the API origin and logs api.
Open a session with it
ui.url, ui.entry (with the one-time token) and browser.wsUrl. ui
without browser serves the page for a browser you run yourself; browser needs ui.
browse drives the hosted browser when the session has one and a local Playwright otherwise, and
prints what the agent would read. From TypeScript, Drive a world UI covers the
same session.
A schema world that declares [ui] opens on the World Host with tools, api, ui and, when
asked for, browser.
Verify
/__ui/ and one on
/__world-api/<route> with "auth": false, and gateway worlds test runs them with the rest.
Where to go next
- Spin worlds up and down for the session the page belongs to.
- Drive a world UI to hand the page to a model as computer-use tools.
- Native computer use to hand it to Claude’s or OpenAI’s own pixel-based tool.
- The files a world is made of for the rest of
connector.toml.