The RAPP Programming Language Chapter 6

← Chapter 5: The Frame · Book contents · Chapter 7: The Egg →

Chapter 6 — The Wire: POST /chat

In this chapter: use the one synchronous endpoint, understand the asynchronous frame form, make retries idempotent, and keep local, cloud, and managed tiers on the same request shape.

Frames are the record; the wire is how they move and how agents actually talk. RAPP keeps the wire deliberately small: one synchronous endpoint and one asynchronous artifact form. “Engine, not experience” means capabilities grow behind the door or as registered frame kinds, never by inventing another envelope.

6.1 One Endpoint

POST /chat
Content-Type: application/json

{ "user_input": "<plain-language request>",
  "session_id": "<optional>",
  "idempotency_key": "<optional>",
  "conversation_history": [ {"role": "...", "content": "..."} ] }

The response:

200 OK
{ "response": "<assistant text>", "agent_logs": ["<what fired>"], "session_id": "<id>" }

That is the whole contract for talking to a RAPP brainstem. There is exactly one required input key — user_input. Unknown request members are ignored for forward compatibility. A successful response has exactly the three shown members; producers do not grow private success envelopes.

The brainstem loads its soul.md as the system prompt, discovers its agents, and decides via tool-calling which of them run. New capability is a new agent file dropped into agents/, not a new route: the synchronous wire does not grow.

6.2 Errors Are Typed, Not Prose

A protocol you can rely on fails in named ways. RAPP §8 specifies HTTP 422 with one exact shape for malformed input, an unknown session, or a protocol refusal:

{
  "error": {
    "code": "unknown-session",
    "step": null
  }
}

code is registered in the estate registry. step is null or one of the frame verification steps "1", "1a", "2", "3", "4", "5", "6". Authentication challenges exposed by a particular host are deployment behavior; they must not create a second successful chat shape.

An error that is only a human sentence is a dead end for the program on the other side; a typed error is a branch it can take.

6.3 Idempotency

Agents retry. Networks drop responses after the work was done. A request may carry an idempotency_key, and replaying the same key returns the original response rather than appending a second turn. With a session_id, the key is scoped to that session; without one, the key also deduplicates session creation.

Content addressing makes duplicate work visible, but the idempotency key preserves the full request result — including the session identifier and response — when a caller cannot know whether the first response was lost before or after execution.

6.4 The Asynchronous Form

The second wire form is an append-only §7 frame published to a stream: a repository path, an event log, or another transport that preserves the exact frame value. The transport is not allowed to reparent, rename, or “upgrade” the frame in flight. Consumers verify it against the stream of record and current trusted head.

Within that form there are two stream disciplines.

6.5 Two Kinds of Stream

The stream_id of chapter 5 tells you which wire discipline applies:

The same frame object serves both; only the discipline around it differs, and the stream_id prefix declares which discipline is in force.

6.6 Tiers Are the Same Shape

The brainstem runs locally (a Flask server on localhost:7071), on a cloud endpoint, or behind a managed studio — and all three speak the identical POST /chat with the identical user_input shape. Moving from your laptop to the cloud is a change of RAPP_BRAINSTEM_URL, not a change of protocol. This is the deepest reason the wire is kept to one endpoint: the moment there are two doors, the tiers drift apart. One door, every tier, is what lets the same client drive all of them.

6.7 Checkpoint: One Request, One Shape

Against a running brainstem:

curl -sS http://localhost:7071/chat \
  -H 'content-type: application/json' \
  -d '{
    "user_input": "Describe your loaded capabilities in one sentence.",
    "idempotency_key": "book-ch6-001"
  }'

Repeat the exact request. A conformant implementation returns the original result instead of creating a duplicate turn. Then send { "messages": [] }: the server should return a typed 422, not guess that messages meant user_input.

6.8 Exercises

Exercise 6-1. Send missing, empty, and wrong-typed user_input values to a test server. Record the exact 422 envelope and require a registered error code.

Exercise 6-2. Implement an idempotency result store that deduplicates both an existing-session turn and session creation. Store the complete original response. A selected solution appears in Appendix C.

Exercise 6-3. Write a client that rejects a 200 response with missing or extra members rather than accepting a private server dialect.

Exercise 6-4. Build an asynchronous reader that receives a frame and a path-of-record stream identifier separately. Prove a valid genesis replayed under another path fails at 1a.

6.9 Chapter Summary

The wire carries frames between living agents. To hand over a whole organism — identity, soul, code, and state — we need a larger content-addressed unit.


← Chapter 5: The Frame · Book contents · Chapter 7: The Egg →