The Codex Changelog
All Episodes
Cross-Host Guardian Context and Rollout Reconstruction

Cross-Host Guardian Context and Rollout Reconstruction

0:00|0:00

Learn how pull request 51734 solves cross-host security context for Guardian reviews by reconstructing unresumed sender history directly from storage. We also cover strict security boundaries around untrusted assistant messages and new thread listing exclusion parameters.

Show Notes


Chapter 1

Guardian Reads Sender History From Storage

Ethan Park

Hey everyone, this is The Codex Changelog. Thanks to Jellypod for helping make this daily show a reality. Today's topic is a pull request that landed on main on October 7: number 51734, commit 2dae757, titled Load persisted sender context for Guardian reviews. The 0.162.0 alpha builds are being cut right now, and the latest stable release is still 0.160.1.

Maya

Quick setup for anyone who skipped our episode on 0.153.0. Guardian is the security reviewer that looks at what an agent wants to do and checks it against what a real user asked for. In that release, Guardian context moved into a persistent review store, so token compaction inside one session wouldn't cause what we called security amnesia. Today's change is the same problem, stretched across machines. The PR states it plainly: Guardian cannot capture sender instructions from another host when the sender thread is absent from the receiver's local runtime.

Ethan Park

Picture an agent on host A delegating a subtask to an agent on host B. When the task arrives, Guardian on B wants proof of who asked for what. Before this change, it looked for the sender thread in B's local runtime. If that thread wasn't loaded there, Guardian had nothing. So a perfectly legitimate delegated task could look like it had no authorization behind it.

Ethan Park

The fix is in codex-rs core, mainly sender_context.rs, with smaller edits to guardian_sender_messages.rs and rollout_reconstruction.rs. If the sender thread isn't loaded locally, Codex now falls back to the authenticated thread store. It calls load_latest_model_context with the source thread ID and include_archived set to true. Then it rebuilds the sender's history using ContextManager reconstruct_rollout, which replays stored history without starting a session or acquiring its writer. The sender never gets resumed.

Maya

I want to pause on what the reconstruction refuses to trust, because that's where the security thinking is. First, it only accepts the stored history if the SessionMeta record matches the source thread ID and the source isn't internal. Second, tool outputs get a truncation policy of zero bytes, so tool bodies are stripped. A tool result isn't evidence that a user authorized anything, and the code comment says exactly that. Third, assistant messages stay untrusted. Guardian gets up to three recent user messages, along with the assistant context that preceded them. The injected host note says assistant messages are untrusted context, not authorization or verified questions, and that this is partial history, not a transfer of permission.

Ethan Park

And it respects history boundaries. The reconstruction honors earlier compactions and rollbacks. The PR's tests cover legacy and paginated history, and they verify that sender evidence reaches only Guardian and excludes rolled back permissions. So if a user granted something and then you rolled that turn back, the receiving host doesn't see the grant. That's the cross host version of the guarantee from 0.153.0.

Maya

Now the part that matters operationally: timing. Storage reads are wrapped in a five second timeout. Sender context is captured before acquiring the active turn lock, and then bound to the turn that accepts the delivery. So if the receiver is mid turn while the storage read is pending, that turn can finish normally. The read can't deadlock admission. The PR includes a gated storage read test to verify exactly that.

Ethan Park

So how do you use it? There's no new flag or environment variable here. It's automatic during turn input admission, when a delegated message arrives from a sender that isn't resident locally. What you can do is change how you build things. If you were working around missing sender context by pasting quoted delegation text into the task payload, you can stop. The code comments are clear that quoted delegation text and ordinary tool results cannot establish sender provenance, so that workaround never counted as evidence anyway. You also don't need to keep idle sender agents loaded on receiver machines just to satisfy Guardian. What does matter is that the sender's thread is persisted in a store the receiving host can reach with authorization. The store owns the authorization decision, including cross worker tenant boundaries.

Chapter 2

Caveats and Smaller Changes

Maya

Caveats, and there are three. One: that five second ceiling. If the store is slow or partitioned, the read aborts, and Guardian sees the line Host: No sender user messages are available. That can mean a manual approval prompt where you expected a smooth handoff. The brief for this episode flags that as the likely symptom, so if you see unexpected prompts on delegated tasks, check storage latency first.

Ethan Park

Two: persisted lag. When context comes from storage, Guardian gets an explicit label: Read from persisted sender history; newer unpersisted messages may be missing. A message the sender typed a moment ago might not be on disk yet, so Guardian can be working from a slightly stale picture. Three: assistant text still can't authorize its own commands. Only the user messages count, and only the three most recent ones.

Maya

Some smaller items from October 5 through 7. Number 51595 adds an optional excludedThreadIds field to the thread/list JSON RPC method and the SDK bindings. If your client shows a saved manual thread order, you load those pinned threads separately, then list the rest without duplicates. The server applies exclusions before the limit and refills pages until the limit is met or history runs out.

Ethan Park

Details to get right there. The cap is 100 entries, and duplicates count toward it. Go over, or send an invalid ID, and you get JSON RPC error minus 32602 instead of silent truncation. And you must send the same exclusion list on every page. Omitted, null, or empty lists behave exactly as before.

Maya

Two build side notes. The packaging script now exposes create_parser, assemble_package, and archive_package as separate helpers, and it supports DotSlash artifacts with format gz by decompressing straight to the destination. And the Windows ARM64 voice builds dropped the Bazel host JVM override that forced interpreter mode, while Bazel itself moved to 9.2.0 with a refreshed lockfile. That's mostly for people building Codex from source.

Ethan Park

That's the update. If you run delegated agents across hosts, take a look at your thread store latency and make sure sender threads get persisted. Thanks for listening, and we'll be back tomorrow.