The layer model
This is the protocol's foundational concept. OGR models agent traffic the way the layered network model models packets — and it is built the way a firewall is: an integration sees one event at a time, the way a firewall sees one IP packet, and the runtime reassembles everything above it and reads everything below it out of the payload.
The model has two axes — the same two a firewall has: entities (the parties, which persist) and a traffic stack (the activity, every unit an episode with a beginning and an end).
The traffic stack
| # | OGR layer | Network analogue | One unit is |
|---|---|---|---|
| L6 | Session | — (this domain's own layer) | one conversation |
| L5 | Turn | — (this domain's own layer) | one instruction → quiescence |
| L4 | Step | transport | one model call: request + response, paired by step_id |
| L3 | Event | network — the packet | one GuardEvent, half a step — the only layer on the wire |
| L2 | Call | link | one tool call the model asked for |
| L1 | Exec | physical | one real execution on a machine — named by the model, not carried by the contract |
Read downward it is containment — a session holds turns, a turn holds steps, a step holds exactly two events, a response event holds zero or more calls, a call resolves to at most one exec. Read upward it is an observability ladder: the higher the layer, the more reconstruction stands between the wire and the answer.
The event is the packet
Like a packet, a GuardEvent is a
header — kind (step/request | step/response), step_id, and the
identity four-tuple agent_id · agent_type · agent_workspace · agent_user
(OGR's answer to the firewall's 5-tuple; the fifth coordinate, the tenant,
comes from the API key and never from the payload) — plus a payload: the
raw provider body.
That is all the wire carries. No session ids, no turn or step numbers, no lifecycle marks: coordinates a sender could declare are coordinates a sender could get wrong, so every layer above the event is derived server-side — a firewall does not ask packets which connection they belong to.
Above the packet: reassembly
- A step's two events are paired by the producer-minted
step_id— fragment reassembly. The halves arrive independently and sometimes out of order; each is judged at its own moment: the request before the model sees it, the response before the agent acts on it. - A turn opens at a user instruction and is closed by the runtime: the
next instruction, the body's own
finish_reason, or an idle timeout — a flow table's FIN / RST / timeout. - A session is chained from what requests already carry: each request
holds the whole conversation, so its prefix fingerprints link it to its
predecessor — plus the producer's own optional
session_hintwhen it has one.
Below the packet: parsed, then inferred
- A call is parsed from the response payload; its result arrives inside
the next step's request and is paired back by the provider's call id. The
call belongs to the step that issued it — and it is the unit enforcement
names: a verdict's
findings[].pathsays which call offended, and an integration may refuse only that one. - An exec is what actually ran. No integration observes this layer:
a gateway sees what was asked (the call) and what came back (the result),
never what happened in between — and the wire deliberately carries no exec
kinds. The layer is in the model because the gap between what a call claims
and what an exec does — a tool named
get_weatherthat actually deletes files — is precisely what agent security is about, and a model without the layer cannot even name that blind spot.
A text-only step has no calls and no execs — empty lower layers are normal, like a bare ACK carrying no application data.
The entity axis
| Entity | Network analogue | On the wire |
|---|---|---|
| Tenant | the administrative boundary | the API key (never the payload) |
| Workspace | security zone — one zone, one policy set | agent_workspace |
| Agent | host / endpoint | agent_id (+ agent_type, agent_user) |
An agent is an endpoint, not a layer. Every stack unit is an episode; an agent persists with zero traffic — sessions belong to it the way TCP connections belong to a host. It is addressed by the identity four-tuple every event header carries, and discovered from traffic the way hosts are inventoried from packets.
Why six layers — and why not OSI's seven
OGR follows the pragmatic TCP/IP cut, not OSI's seven: a layer earns its place with its own unit, its own mechanism, and its own question. Above transport, networking has only "application", because network applications share no structure — but agent traffic is a dialogue with stable structure, so turn and session are this domain's own layers, defined here rather than mapped onto OSI's vestigial session/presentation layers. And the agent stays off the stack for the same reason a host is not a protocol layer.
The firewall vocabulary carries over with the method:
| Firewall / network | OGR |
|---|---|
| packet | event |
| 5-tuple | the identity four-tuple + the API key's tenant |
| fragment reassembly | step_id pairing |
| session table, idle aging | server-side session state, idle timeout |
| stateful inspection | session / turn derivation |
| deep packet inspection | detection over the payload's texts |
| pass / drop | verdict allow / block |
| security zone | workspace — one zone, one policy set |
Normative text
The layer model is normative since OGR v1.0:
specification/overview.md
§ The layer model.