Tutorial: your first paired session
This is the fast path from "ContextRelay is installed" to "two agents are talking in my repo, and I can prove it." By the end you will have:
- a live pair - Claude Code and Codex both connected to the same project daemon;
- two messages exchanged between them, visible in the shared ledger;
- one durable note written by one agent and read back by the other;
- and a clean shutdown, with the ledger still on disk for next time.
It takes about ten minutes. Every command below is real - copy them as-is.
Why a "pair," and why a ledger
Before the steps, the one idea that makes everything else make sense.
ContextRelay runs Claude Code (Anthropic) and Codex (OpenAI) as two separate, trusted processes on your machine, joined through a local loopback daemon. Neither agent drives the other; ContextRelay sits in the middle and routes their messages. Crucially, the agents cannot see each other's hidden reasoning - they only share what gets written into a message or into the shared, append-only ledger (a JSONL log under .contextrelay/state/).
That is the whole mental model: if it isn't written down, the other agent doesn't know it. This tutorial makes that concrete in Step 4, where you write a note on one side and read it on the other. For the deeper rationale, see Why two agents in one session and The shared durable ledger.
This tutorial assumes ContextRelay is installed and you have run ctxrelay init in your repo. If you haven't, do that first - see Install and first run. The binaries contextrelay, ctxrelay, and context-relay are interchangeable; this page uses ctxrelay.
Step 1 - Launch the pair
From the root of your repo, start both agents and the control dashboard with one command:
ctxrelay pair
This ensures the project daemon is running, opens Claude Code in a new terminal, opens Codex in another terminal, and then opens the native TUI dashboard in your current terminal.
In the TUI, confirm a healthy bring-up. You should see the daemon pid and bridge readiness, the instance id and port group, and the two agent states. As each side connects, its state moves toward idle/ready:
- Claude connects through the installed Claude Code plugin (its message channel).
- Codex connects to the daemon and proxy.
Press r in the TUI to refresh the view at any time.
ctxrelay pair is the shortcut. You can also launch each side in its own terminal:
ctxrelay claude
ctxrelay codex
Or, if you already have the TUI open, just press p to launch the pair.
A project instance hosts one default pair. If a pair is already active and you run ctxrelay pair again, ContextRelay keeps the current pair rather than stomping it - the TUI offers a named runtime pair instead. For your first run, a fresh repo with no active pair is the simplest path. Named pairs and worktrees are covered in Runtime sessions and worktrees.
Step 2 - Confirm both sides see the channel
Claude gets its ContextRelay tools automatically from the installed Claude Code plugin, so there's nothing to do on that side. Codex needs its MCP tools registered. Check the registration from a normal shell:
ctxrelay codex-mcp status
If it reports that the server is not installed, register it once (this is global to Codex, not just to ContextRelay-launched windows):
ctxrelay codex-mcp install
After installation, restart the Codex side if it was already open so it picks up the tools. You now have the messaging tools on both sides:
- Claude has
reply,handoff,append_note,read_context,session_info, and more. - Codex has
send_to_claude,handoff_to_claude,append_note,read_context,wait_for_claude, and more.
codex windowBecause codex-mcp install registers the tools globally, a normal codex window opened in this project can load the ContextRelay tools and attach to the running daemon - you are not limited to ctxrelay codex. Use ctxrelay codex-mcp remove if you'd rather Codex only participates when launched through ctxrelay codex.
Step 3 - Send the first message
Now make the agents actually talk. Use natural language in each agent's normal prompt - you don't type tool calls by hand; you ask the agent to use them.
From Claude, send a greeting to Codex. Claude will call its reply tool:
Use
replyto say hello to Codex and confirm you can hear each other.
From Codex, reply back to Claude. Codex will call its send_to_claude tool:
Use
send_to_claudeto acknowledge Claude's hello.
Watch the TUI while this happens. Two things should change: the ledger count increments (each message is one durable entry), and the queue depth rises and then drains as the message is delivered to the other side. That drain-to-zero is the signal that delivery succeeded.
The two agents have mirror-image messaging tools. reply (Claude) ↔ send_to_claude (Codex) for a normal turn; handoff (Claude) ↔ handoff_to_claude (Codex) for passing ownership of a task. You'll use handoffs in the next tutorial - see Delegate an implementation to Codex via handoff.
Step 4 - Write durable context, then read it back
This is the step that proves the core principle. Have one agent record the session goal and current plan as a durable note.
From Claude (or Codex - either works):
Use
append_noteto record our goal for this session and the current plan in one short note.
Now read it from the other side. Ask Codex (or Claude):
Use
read_contextand tell me what's in the latest note and active handoff.
The second agent should report back the exact note the first one wrote. Nothing about how the first agent reasoned crossed over - only what it chose to write into the ledger did. That's the whole game: durable, shared context lives in the ledger; private reasoning does not. This is why good ContextRelay habits mean writing the goal, the plan, files touched, blockers, and the next step into notes and handoffs rather than assuming the peer "just knows."
Step 5 - Inspect the session
You can see the same state two ways: a rich browser view, and a machine-readable dump.
Open the browser Command Deck (the viewer) to walk the timeline visually:
ctxrelay viewer
(Or press v in the TUI.) The viewer is local and token-authenticated. Walk through:
- the timeline - your two messages and the note, in order;
- the agent states - Claude and Codex, idle/ready;
- the note artifact you wrote in Step 4.
For the identical state in machine-readable form - handy for scripts, CI, or just a quick sanity check - use:
ctxrelay status --json
This prints daemon, session, connection, ledger, task, autonomy, and finality state as JSON. The ledger count here should match what the TUI showed.
Step 6 - Stop cleanly
When you're done, stop this project's instance:
ctxrelay kill
This stops the daemon and the connected agent runtimes for the current project. The ledger persists on disk under .contextrelay/state/ - your next session can read this same history, and tools like ctxrelay recover can summarize where you left off. Nothing you recorded is lost on shutdown.
If you see a "daemon disconnected" message while a long Codex turn is running, it is often not a crash - it's the per-turn watchdog (CONTEXTRELAY_TURN_MAX_MS, default 300000 ms / 5 minutes) firing on a turn that ran long. Check ctxrelay status before killing or restarting; the daemon is frequently still healthy. For long turns you can raise that budget. See Troubleshooting and recovery for the full diagnosis path.
What you just did
You launched a real pair, exchanged messages both directions, wrote one durable note and read it back from the other agent, inspected the session in the viewer and as JSON, and shut down without losing the ledger. You've now seen the spine of every ContextRelay session: live messages plus a durable, shared ledger, with two agents that only know what's written down.
Next steps
- Tutorial: delegate an implementation to Codex via handoff - turn this pair into real work by handing a concrete task to Codex.
- Daily operation: launch, inspect, stop - the everyday rhythm of running ContextRelay.
- The shared durable ledger - what the ledger records, and why it's the source of truth.