My Claude Code harness post gets the questions. The follow-up is usually “can I do the same thing with Codex?” Yes. Here’s how, and where it bites.
Disclosure: I work at Solo.io, which created kagent (now an open-source project) and sells Solo Enterprise for kagent. This post is built from the public Solo Enterprise for kagent harness docs as of 2026-10-30. kagent 1.0 is alpha, so field names can still move.
TL;DR
spec.codex: {}on a Harness runs Codex as the agent loop, on Agent Substrate.- The ModelConfig must use
openAI.apiFormat: responses, and the key must be entitled to the model. - MCP servers must be Streamable HTTP. No structured output, no token passthrough.
Why run Codex in the cluster
Codex on a laptop is one process that holds your key, your shell and your session. On kagent, the session runs in a sandboxed Actor that can suspend to object storage and resume, the model key sits behind the egress gateway instead of in the agent’s environment, and every edit to the agent produces a new digest-pinned revision. Sessions already running keep the revision they started with. More on that split in what a cloud-native agent harness is .
The four resources
ModelConfig (model + key), Harness (runtime + infra), AgentTemplate (behavior), Agent (the pairing). This is straight from the docs. It assumes the kagent-openai Secret from the OpenAI setup guide and a kagent-default WorkerPool.
apiVersion: api.kagent.dev/v1alpha3
kind: ModelConfig
metadata:
name: codex-model-config
namespace: kagent
spec:
apiKeySecret: kagent-openai
apiKeySecretKey: OPENAI_API_KEY
model: gpt-5.2-codex
provider: OpenAI
openAI:
apiFormat: responses
---
apiVersion: api.kagent.dev/v1alpha3
kind: Harness
metadata:
name: codex-harness
namespace: kagent
spec:
codex: {}
substrate:
workerPoolRef:
name: kagent-default
snapshotPolicy:
location: s3://ate-snapshots/kagent/
---
apiVersion: api.kagent.dev/v1alpha3
kind: AgentTemplate
metadata:
name: codex-template
namespace: kagent
spec:
modelConfig:
name: codex-model-config
systemPrompt: You are a concise, helpful coding assistant.
---
apiVersion: api.kagent.dev/v1alpha3
kind: Agent
metadata:
name: codex-agent
namespace: kagent
spec:
template:
ref:
name: codex-template
harnessRef:
name: codex-harness
I left workload.image off the Harness on purpose. Omit it and you get the digest-pinned Codex image that ships with your kagent release. Override it per Harness, or install-wide with config.harnessImages.codex in Helm. Either way it must be pinned by sha256.
Gotchas
| Gotcha | What happens |
|---|---|
ModelConfig not apiFormat: responses | Rejected. Only baseUrl is allowed alongside it |
| Key not entitled to the model | Turn fails with Codex execution failed |
| Bedrock | Only OpenAI gpt-* model IDs, only region setting, and an AWS_BEARER_TOKEN_BEDROCK key (IAM keys rejected) |
| SSE MCP server | Rejected. Use STREAMABLE_HTTP |
tokenPassthrough, defaultHeaders, tls | Rejected |
| Structured output | Rejected |
Setting CODEX_HOME, OPENAI_API_KEY or AWS_* creds in spec.env | Every Agent on that Harness goes Compatible=False |
| Subagents | Can’t carry their own tools or skills, and must share provider + creds |
| File attachments in UI chat | kagent harness only |
The env one catches people who try to “just pass the key.” Don’t. Keys belong in the ModelConfig Secret, and the gateway injects them.
Check it
kubectl describe agent codex-agent -n kagent
kubectl get agent codex-agent -n kagent -o jsonpath='{.status.workloadImage}'
ResolvedRefs false usually means a missing WorkerPool. Compatible false means one of the gotchas above. Ready false means Substrate capacity isn’t up yet. The Harness itself never reports status, so stop staring at kubectl get harness.
Telemetry
With tracing on and captureContent: true, Codex prompts and replies land on the invoke_agent span, and tool arguments and results land on execute_tool. maxCaptureBytes (default 16384, max 65536) caps each one. Default is off, and that’s right: anyone who can read your traces can read what you capture. To flip it for just this Harness, set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT to SPAN_ONLY or NO_CONTENT in spec.env.
Codex vs Claude harness
| codex | claude | |
|---|---|---|
| Providers | OpenAI (responses), Bedrock gpt-* | Anthropic, Bedrock |
| MCP | Streamable HTTP only | Binds whole server, ignores subsets |
| Raw API body capture | No | Yes (captureRawApiBodies) |
| Structured output | No | No |
All four side by side: kagent harnesses compared .