Introduction

OpenGuardrails (OGR) is an open guardrails contract for AI agents. The whole protocol fits in one sentence: at the two moments an integration can still refuse — before a request reaches the model, and after the response arrives but before the agent acts on it — it forwards the raw provider body as a GuardEvent to POST /v1/evaluate and gets back a Verdictallow or block, with findings saying what was found and where, and redaction spans when content must be transformed in place.

One endpoint, one recipe. There is deliberately no SDK layer: the API is the integration surface, and every plugin this project ships is written against it. Integrating your own agent is two POSTs added to a loop you already have:

while True:
    step_id = uuid.uuid4().hex                    # binds this call's 2 events
    body = {"model": "gpt-5", "messages": messages, "tools": TOOLS}
    if blocked(evaluate("step/request", step_id, body)):   # ① before the model
        break
    resp = call_llm(body)                                  # your code, unchanged
    if blocked(evaluate("step/response", step_id, resp)):  # ② before acting
        break
    ...                                           # execute tool calls, loop

The quickstart is the complete, runnable version — evaluate() is a single requests.post, fail-open by default, and sends the optional session_hint (one opaque id per conversation) so sessions are declared instead of inferred.

The layer model

OGR's foundational concept: agent traffic modeled the way the layered network model models packets. An integration sees one event at a time, the way a firewall sees one IP packet; the runtime reassembles everything above it and reads everything below it out of the payload.

#OGR layerOne unit is
L6Sessionone conversation
L5Turnone instruction → quiescence
L4Stepone model call, two events paired by step_id
L3Eventone GuardEventthe only layer on the wire
L2Callone tool call the model asked for
L1Execone real execution — named by the model, not carried

You already have words for this traffic — the network stack, tracing spans, or the SDK you built on. They line up:

#OGRNetworkOTel GenAIOpenAI Agents SDKClaude Agent SDKLangGraph
L6Sessionsession tableconversation.idSession idsession_idthread
L5Turnflowinvoke_agentone Runner.run()one query()one invoke()
L4Steptransportchat spangeneration_span ⚠️one round trip ⚠️model node
L3Eventthe packetspan start / endspan start / endAssistantMessagearound invoke()
L2Calllinkexecute_toolfunction_spantool_useToolNode
L1Execphysicalthe host commandthe tool fn
Agenthostagent.idAgentagent · subagentthe graph

⚠️ Both agent SDKs call an L4 step a "turn" — one loop iteration, which is what max_turns counts. An OGR turn is the instruction episode above it. The full mapping covers handoffs, subagents, super-steps, and what to send as session_hint.

The ledger is the runtime's job, not the wire's. Everything above the event is derived server-side: sessions by conversation-prefix chaining (re-attached across context compaction), turns by instruction boundaries and idle timeout, step numbering by arrival. An integration keeps no loop state for OGR — it is an API key, eight required fields, and one endpoint. And the agent is an endpoint, not a layer — addressed by the identity four-tuple every event carries, the way hosts are addressed by packets.

The full treatment — the entity axis, the firewall vocabulary, why six layers and not OSI's seven — is the layer model.

One recipe, two vantage places

The same two POSTs serve a developer instrumenting their own agent loop and a gateway proxying model traffic. Both forward the raw provider body they hold, both mint a step_id per model call, both declare nothing else. The only difference is who fills the identity four-tuple: an agent asserts its own; a gateway asserts its authenticated caller's, read off the request — see gateway integration.

What the contract standardizes

  • GuardEvent — eight required fields: the step half (kind), the pairing id (step_id), the identity four-tuple, the payload shape (llm_protocol), and the raw provider body (payload) — plus three optional: integration, connection, session_hint.
  • Verdict — the runtime's decision: allow | block, plus findings, modifications.spans, and unjudged.
  • Composition — how a runtime merges many detectors' verdicts into the one decision it enforces.
  • The Runtime API — the HTTP binding: evaluate, heartbeat, health. Fail-open by default when the runtime is unreachable.

Where to go next

  • Quickstart — the minimal integration, end to end: the four-tuple, fail-open, and streaming with a held-back tail.
  • Gateway integration — guard every agent behind your LLM gateway without touching their code: the install, the identity headers, and what to strip at the edge.
  • API reference — every endpoint, error, and field.
  • Plugins — ready-made integrations for gateways and agent harnesses.

OGR is Apache-2.0 and governance-neutral. Detectors compete on a neutral benchmark; you compose the winners.