Domain is data

A hub starts as types, not as routes.

Schema is what every host will read: JSON codecs, OpenAPI components, MCP tool arguments, and (later) CLI flags. JsonCodec is how bytes move. Core never parses a user body except through JsonCodec.

one type, three readers · click a host
Schema

Show

id: Int, title: String, remaining: Int

CLI flags

--id 1 (later interpreter)

{"id":1,"title":"Evening bill","remaining":12}

The box office

The running example in this repo is a box office. Three bills. That is enough to host humans, systems, and agents.

The types

final case class Show(id: Int, title: String, remaining: Int) derives Schema, JsonCodec
final case class Party(showId: Int, size: Int) derives Schema, JsonCodec
final case class NotFound(message: String) derives Schema, JsonCodec

Why Schema exists

derives Schema is not documentation theater. Show becomes:

  • the JSON object {"id":1,"title":"Evening bill","remaining":12} on the wire

  • components.schemas.Show in OpenAPI

  • the result schema of get_show for MCP

  • the shape a future CLI would print and parse

Change the type. Every host moves. That is the point of a compiler.

Operations are an AST turns these types into Endpoints.