Skip to main content
How opfor delivers conversation context to an HTTP agent target across turns, and how it threads a session id. One model, shared by opfor run (config file, wizard, SDK), the MCP server tool, and opfor hunt. There are two independent decisions.

1. Stateless vs stateful — who holds the history

2. Client-owned vs server-owned — who mints the session id (stateful only)

Does your target return a session id in its response (body field, header, or Set-Cookie)? That’s server-owned. Does it just expect you to supply an id it keys on? That’s client-owned. Raw LLM API with no session concept? That’s stateless.

The session config

  • Presence of receive selects server-owned mode; its absence is client-owned.
  • send is always required when session is set, and name on it is required.
  • send.in: "body"name is a dot-path in the request body; "header" → an HTTP header name.
  • receive.in: "body" or "header" both require a non-empty name. "set-cookie" is the only receive location where name is optional — it captures the returned cookie pair, echoed back via a Cookie header (set send to { "in": "header", "name": "Cookie" }).
  • Legacy alias: sessionIdField: "x" (CLI) / sessionField: "x" (SDK) is shorthand for session: { send: { in: "body", name: "x" } } — client-owned, body. Still honored; prefer session for new configs.

Server-owned turn-1 flow

  1. Turn 1 — the request goes out with no session id.
  2. opfor reads the returned id via receive and remembers it.
  3. Turns 2+ — that id is echoed via send.
If a server-owned target returns no id, opfor falls back to a client-minted id and logs a warning.

Per-surface syntax

Examples

Other target types

  • Local-script (target.type: "local-script"): sessionId is always included in the stdin JSON on multi-turn attacks; your script owns the history. session / stateful do not apply.
  • Browser extension: drives a live chat UI in the DOM — there is no session id to configure.
For opfor hunt, each attack thread invents its own id for client-owned targets. For server-owned targets, each thread captures the target’s returned id independently — and because the session belongs to the target, forking a thread starts a new server session (opfor can’t fork a session it doesn’t own).