The earlier articles covered the agent, the MCP resource, and the control plane. This one follows a single turn: one request from a client to an agent, through the gateway, the checkpoint store, and back.
The sequence follows the Flisk agent docs. The model call is a placeholder, since your runTurn decides what it looks like.
sequenceDiagram
autonumber
actor User as Client
participant GW as Traefik gateway
participant Agent as Agent service
participant Store as Checkpoint volume
participant Model as Model provider (your code)
User->>GW: POST /api/bot/turn with x-session-id
GW->>Agent: route by session header
Agent->>Store: load checkpoint for the session
alt no checkpoint yet
Store-->>Agent: empty state
else checkpoint exists
Store-->>Agent: prior messages
end
Agent->>Agent: append the user's input
Agent->>Model: runTurn(checkpoint, input)
Model-->>Agent: reply and updated state
Agent->>Store: commit the checkpoint atomically
Store-->>Agent: committed
Agent-->>GW: reply
GW-->>User: reply
Note over Agent,Store: On SIGTERM an in-flight turn finishes and checkpoints first
Note over Store: The named volume survives flisk down
The session state sits in a checkpoint store, a file per session on a named Docker volume. The turn reads it first and writes it last. The write is atomic, so a crash partway through does not leave a half-saved conversation.
Because the volume outlives flisk down, a restarted agent loads the same state the next turn needs. The docs also say an explicit volume removal is the only way to lose it, which is a deliberate safeguard.
The gateway uses the x-session-id header to choose the session. Two conversations never share a checkpoint, even when they hit the same agent service. The header is also the one thing a client must get right: a missing or changed session ID starts a new conversation.
If the agent restarts between turns, the next turn loads the checkpoint and carries on. If it restarts during a turn, the drain on SIGTERM lets the in-flight turn finish and checkpoint first. The docs say live-update reloads use the same path.
A crash rather than a clean stop skips the drain. The atomic commit then protects the last saved state, and the turn in progress is lost. Plan for that in your client: retry the request with the same session ID, and check whether the turn was saved.
Send two turns with one session ID, restart the stack, and send a third turn. The third reply should reflect the first two. Then send a turn with a new session ID and confirm it starts fresh. Those two checks cover the parts that break quietly.
One turn goes through the gateway, which routes by session. The agent loads the checkpoint, calls your model code, commits the new state atomically, and replies. The state survives a restart because it lives on a volume, and a clean stop drains the turn in progress. Test the two-turn, restart, third-turn path before you depend on it.