Colloq GitHub
v0.1 · research prototype · Apache 2.0

One conversation.
Every server keeps its half.

Colloq is a language for systems where several servers must agree on a protocol: an inference gateway and its routers, an agent and its tools, two services that keep drifting apart. You write the exchange once. The compiler gives each side a checked endpoint, and the runtime carries it over shared memory, TCP, QUIC or Iroh.

~/colloq
$ colloq check examples/route.colloqconv.json
valid conversation example.route (2 roles, 11 states)

$ colloq compile examples/route.colloqconv.json
wrote build/route.colloqplan.json
conversation sha256:92c198d0…
plan         sha256:016d5975…

$ colloq run-plan build/route.colloqplan.json --transport quic --wire compact
✓ client and server report the same semantic trace

What it is for

Three servers, one agreement.

Every system below has the same problem: two or more processes must follow the same protocol, including its failures, and today that agreement lives in a client library, a server handler and a schema file that drift apart.

Inference routing

Gateway, router, experts

A request arrives, a router decides between a cached answer and a model, tokens stream back, and the client may cancel mid-stream. Deadlines and cancellation are part of the type, so no side invents its own handling.

choice · stream · cancel · deadline

Agents and tools

Turn order that holds

An agent process and its tool services must agree on who speaks next, what a retry means and when a run is finished. Colloq compiles that order into the plan both sides run. An out-of-turn call is not caught by a policy layer that reads generated code. The endpoint state machine has no such edge.

roles · loops · terminal states

Service pairs

Two services that drift

The classic pair: a handwritten client, a handler and a schema file that disagree after the third change. One conversation replaces all three, and the identity changes when the meaning changes.

projection · content identity

Colloq is not a web framework and not a hosted service. If one process does all the work, you do not need it.

The old way, the new way

Same exchange. One file instead of four.

A gateway asks a router for an answer within 20 ms, and the router replies from cache, from a model, or hands the case to a person. Here is where that agreement lives today, and where it lives in Colloq.

The usual way

4 files, 3 languages, 2 teams
# route.proto — the shape, but not the order
service Router {
  rpc Route(Request) returns (Answer);
}

# client.py — deadline and retry, written here
resp = stub.Route(req, timeout=0.02)
# is a retry safe? the proto does not say

# server.py — the branches, written again
if cached: return Answer(kind="cached", ...)
if score > 0.9: return Answer(kind="infer", ...)
return Answer(kind="review", ...)

# handler.py — "review" handled? nobody checks
  • The order of messages lives in two handwritten files.
  • A new branch compiles on one side and crashes on the other.
  • Timeouts and cancellation are convention, not type.
  • Nothing tells you the two sides still agree.

With Colloq

route.colloq — one file, both sides
conversation Route(request: Request) {
    roles gateway, router

    gateway -> router: request within 20ms

    choice router {
        cached { router -> gateway: CachedAnswer; end }
        infer  { router -> gateway: ModelAnswer;  end }
        review { router -> gateway: ReviewTicket; end }
    }

    on transport.timeout -> timed-out
}
  • The order is the program. Each side is derived from it.
  • A new branch changes the plan identity, so a stale peer is refused.
  • The deadline and the failure are part of the type.
  • Both peers compare identities before the first frame.
ConcernThe usual wayColloq
Where the protocol livesA schema file, plus both implementationsOne conversation
Who checks the orderNobody, until productionThe checker, before you run it
Timeout and cancellationClient conventionDeclared transitions
Failure handlingGeneric exception per serviceA declared state per failure
Detecting a changeVersion numbers and hopeContent identity, refused on mismatch
Changing transportRewrite the plumbingRun the same plan on another transport

How it works

Write the agreement once.
The compiler splits it.

From that one file, the compiler derives a local state machine for each role. Nobody writes this: the router's machine below comes out of colloq project, and the gateway gets the exact mirror of it. Two servers cannot disagree about a protocol neither of them wrote.

build/endpoints/router.colloqendpoint.json — derived, not written
{
  "colloq_endpoint": "0.1.0",
  "conversation": "example.route",
  "role": "router",
  "states": [
    { "op": "receive", "id": "start",
      "from": "gateway", "message": "request",
      "deadline": "20ms", "next": "route",
      "on_failure": { "transport.timeout": "timed-out", … } },

    { "op": "select", "id": "route",
      "branches": { "cached": "reply-cached",
                    "infer":  "reply-infer",
                    "review": "reply-review" } },
    …
  ]
}
  • Typed choices, not guesses

    A branch label is part of the type. A role can only send a transition the plan declares, and the receiver checks it before it advances.

  • Failure is in the language

    A timeout or a reset is a declared transition to a declared state, not a generic exception that each service handles differently.

  • One identity across the cluster

    The conversation and the compiled plan are content addressed. Both peers exchange those identities before frame zero, and a mismatch stops there.

Transports

The meaning survives the wire.

The same compiled plan runs over shared memory, TCP, authenticated QUIC and Iroh. Every transport reconstructs the same semantic trace, so a laptop run and a two-host run mean the same thing.

memory TCP QUIC (mTLS) Iroh (peer identity) reference wire compact wire

Why it exists

Programs that change while they run.

The systems Colloq targets will not be written only by people. Models and evolutionary search already propose changes to routing, placement and whole subgraphs. That is the reason the conversation is typed and content addressed: machine-authored change needs a substrate where meaning is checkable, not a pile of text files where a rename looks like a rewrite.

The graph is the program

Edit structure, not text

The source of truth is a typed, content-addressed graph. Text and visual editors are projections of it. A generator issues typed transactions and fills typed holes, so formatting and names never change the meaning.

Evolution as a transaction

Propose, gate, canary, keep or revert

A candidate passes type and effect checks, then capability and budget checks, then bounded evaluation, then a policy decision, then a canary rollout. Promotion and rollback are steps, not incidents.

Authority never inherits

A child gets no rights by birth

A candidate cannot acquire a capability because its parent had one. Lineage is immutable, gates cannot be written by the candidate itself, and some invariants stay outside the region a generator may edit.

None of this is finished, and the project keeps a scoreboard rather than a slogan: what is derived today, what provenance still lives in git, which limits are drafts, and which gaps are not on the roadmap at all. Read the goal and where it stands.

Why Colloq exists The longer vision

Extensions

The core needs no model.

Colloq is a language and a runtime. Nothing in it depends on a model. One optional binding lets a typed model choose the label at a choice state, with an explicit confidence threshold and a declared escalation branch. Remove it and the conversation still compiles, still runs and still checks. See the optional chooser.

Evidence

Every number here is reproducible.

Colloq publishes a fixed workload, a hand-written baseline and the full report, so a claim can be checked instead of believed. These are the current medians on an Apple M4 over 500 iterations.

1.83 µscompact checked transition
0.125 µsto start two sessions from a plan
51.6 µsplan compilation, once per plan
4transports, one semantic trace

The whole exchange currently costs 1.51× a hand-written Rust pair, down from 1.68× with the compact wire, against a target of 1.25×. The compact encoding is still JSON, so a binary codec is the next measurement. The benchmark exists to make that number move in public.

Read the benchmark

Start with the two-node run.

Clone it, compile one conversation, then run the same plan across two processes with mutual authentication.

quickstart
$ git clone https://github.com/Vantar-AI/colloq
$ cargo run -- check examples/generate.colloqconv.json
$ cargo run -- bootstrap-two-node --out build/two-node