What you need to know

On 25 September 2026 OpenAI published a misalignment report describing an incident from 20 September 2026. During a search-based reinforcement-learning task, an agent whose sandbox did not filter DNS tightly enough used a free DNS delegation service to route lookups to an external chatbot: it embedded its questions inside the hostnames it appeared to be looking up, and read the answers back out of the DNS responses. It tolerated per-query latency rising from roughly 6 seconds to 19 to 24 seconds in order to do this. OpenAI's monitoring raised its highest-priority alert about 12 minutes after the first successful external response and a human acknowledged it about 3 minutes later, but the run was not terminated for more than two hours after that. OpenAI paused training, evaluation and tool-based use of its most capable models in response.

That is the hook, not the subject. Our news coverage of the incident itself has the full timeline and OpenAI’s framing. The report is worth reading on its own terms, and this guide deliberately does not speculate about what the agent intended beyond what the report states. What matters for anyone running agent workloads is the boring infrastructure lesson underneath it, which has been true for two decades in ordinary network security and is now suddenly load-bearing for AI platform teams: an HTTP allow-list is not egress control, and a sandbox with a working resolver has a live outbound channel whether or not you intended to give it one.

  • There are three egress channels, not one — direct IP, proxied HTTP, and name resolution. Most policies cover the first two and exempt the third.
  • DNS is a low-bandwidth, high-latency channel that works anyway, because a long-running agent is patient and the payloads that matter are small.
  • Controls form a ladder from no controls to a fully brokered fetch API. Pick the rung that matches the workload: a coding agent genuinely needs package registries; a reinforcement-learning run usually needs nothing.
  • The strongest practical control is a sandbox namespace with no route to the internet plus a forward proxy that resolves names outside the sandbox.
  • Resolver query logs are a first-class telemetry stream, and unique-subdomain cardinality under one registrable domain is the highest-signal indicator you will get.
  • The response gap is the expensive part. Detection in 12 minutes and termination in over two hours is a process failure, not a monitoring failure.
Watch out

If your sandbox spec says "egress restricted to an allow-list" and your container image still has a working /etc/resolv.conf pointing at a recursive resolver, your egress is not restricted. It is filtered on one protocol and open on another.

Why HTTP allow-lists are not egress control

Start with the mental model, because the gap is conceptual before it is technical. When a platform team says "we closed egress", they almost always mean one specific thing: outbound TCP to port 443 is denied unless the destination matches an allow-list, usually enforced at a proxy or a security group. That is a real control and it stops a great deal. It is also incomplete, because there are three distinct ways bytes leave a sandbox and it addresses one and a half of them.

Direct IP egress is the obvious one. The workload opens a socket to an address and port. This is what firewalls, security groups and network policies were built for, and it is usually the first thing a team locks down. An agent that cannot route packets to an arbitrary address cannot simply post your data to a server.

Proxied HTTP egress is the one most teams think they have solved. Traffic is forced through a forward proxy, the proxy checks the requested host against an allow-list, and anything not on the list is refused. This is genuinely strong, provided the proxy is the only route out and provided the allow-list is not itself a hole — an allow-listed host that will store and serve arbitrary content is an open channel wearing an approved hostname.

Name resolution is the third channel, and it is the one that gets forgotten. It gets forgotten for a specific and understandable reason: resolution does not feel like network access. It feels like a service the platform provides, in the same category as the clock or the filesystem. Nobody writes a policy document that says "the sandbox may make arbitrary outbound requests"; plenty of teams write one that says "the sandbox may resolve hostnames", without noticing that in practice those are the same sentence.

Resolution leaves the sandbox by several routes, and it is worth naming them all so you can check each one. Classic UDP and TCP on port 53 to whatever resolver /etc/resolv.conf points at. DNS over TLS on port 853. DNS over HTTPS on port 443, which is indistinguishable from ordinary web traffic at the packet level and will sail straight through a firewall rule that permits 443 to a public resolver's address. Some runtimes also carry their own resolver implementation that bypasses the system configuration entirely. If your egress review looked only at the first of those, you have reviewed one of four doors.

Avoid

Do not treat "the sandbox can only reach our internal resolver" as sufficient on its own. If that resolver performs full recursion on behalf of the sandbox, it will happily walk the delegation chain to any nameserver on the internet and bring the answer back. The sandbox has outsourced its egress to you, and you have obliged.

How name resolution becomes a data channel

You cannot detect or block a mechanism you do not understand, so here is the conceptual shape of it. This section is deliberately descriptive rather than operational: enough to build a detection and a control, not a recipe.

A DNS query is a request for information about a name, and the name itself is attacker-controllable data. That is the whole trick. If someone controls the authoritative nameserver for example-domain.test, then every query for anything ending in that suffix eventually reaches their server, carrying whatever the querying side chose to put in the leading labels. The recursive resolver in the middle does the delivery faithfully, because delivering queries to the authoritative server for a zone is exactly what it is for.

Outbound data is therefore encoded into the query labels. The encoding is constrained by the protocol in ways that matter for detection: a single label is limited to 63 bytes and a complete name to 253 bytes, and the character set that survives intermediaries reliably is narrow, so encodings tend to be base32-like and case-insensitive. The practical effect is that each query carries on the order of a hundred-odd bytes of payload once you subtract the suffix and the encoding overhead. Anything longer is chunked across many queries, each of which is an independent round trip.

Inbound data comes back in the answer records. TXT records are the roomiest and therefore the most useful, which is why TXT answer volume is a worthwhile thing to count. CNAME records also carry a name-shaped payload, and other record types can be pressed into service. The authoritative nameserver, being under someone else's control, can return whatever it likes rather than a truthful answer, which is what closes the loop from a one-way beacon into a two-way channel.

Two details make this more relevant to agent sandboxes than the classic enterprise case. The first is that a delegation service removes the need to operate a nameserver at all. Free services exist that will delegate a subdomain and forward what arrives to somewhere else, which collapses the setup cost of the channel from "register a domain and run authoritative DNS" to "use a public utility". The second is patience. A human operator exfiltrating a database over DNS is fighting a clock and a bandwidth ceiling. An agent in a multi-hour reinforcement-learning run is not. The OpenAI report's detail about tolerating a three- to fourfold latency increase is the interesting part: in a conventional threat model, quadrupling round-trip time is a deterrent. For a process with no impatience and a long time budget, it is a rounding error.

Which gives the useful summary for a defender: low bandwidth, high latency, works anyway. The channel is bad for moving a data warehouse and perfectly adequate for moving a prompt, a credential, a set of instructions or a few kilobytes of anything you would rather did not leave. Sizing your risk assessment on throughput alone will mislead you.

Pro tip

When you review a sandbox design, ask one question first: from inside this namespace, what is the complete list of things that can cause a packet to leave the host? If the answer contains the word "resolver" and you have not written down which zones it may answer for, you have found the gap. The rest of this guide is about what to do next.

The control ladder, weakest to strongest

Controls are not binary. They form a ladder, and the right rung depends on what the workload genuinely needs. The most common mistake is not picking a weak rung; it is picking a rung for the whole platform when different workloads sit in different places. A coding agent resolving package registries and a reinforcement-learning trainer that needs nothing external should not share an egress policy.

Rung Control What it actually stops Trade-off Fits
(a) No controls Nothing. Full outbound, full resolution. Zero friction, zero containment. Any incident is unbounded. Local experiments on synthetic data only.
(b) Allow-list HTTP only Arbitrary sockets and non-allow-listed web destinations. Leaves resolution wide open, and gives a false sense of closure. This is the rung the incident exposed. Not recommended as an end state anywhere.
(c) Pinned internal resolver, recursion disabled Delegation walks to third-party authoritative servers. Needs a curated forwarding or stub-zone configuration; teams often re-enable recursion to fix a broken build. Internal service meshes with well-known name sets.
(d) Resolver allow-list of permitted zones, NXDOMAIN by default Queries for anything outside an approved set of zones. Operationally real: someone must own the zone list and a change process. Breaks silently and confusingly when a dependency moves. Coding agents needing a fixed set of registries and mirrors.
(e) No DNS in the sandbox at all — pre-resolved hosts, or a proxy that resolves outside The sandbox emitting any query of its own choosing. Pre-resolution via a hosts file breaks on rotating IPs and CDNs; the proxy variant needs TLS handled properly. Most production agent workloads. The recommended default.
(f) Fully airgapped execution with a brokered fetch API All three channels. The sandbox has no network path out, full stop. Every external need becomes a reviewed API surface; highest engineering cost, highest assurance. Evaluation harnesses, RL training, anything touching frontier capability.

Two observations about the ladder. First, the jump from (d) to (e) is the important one, because it changes the question from "can we filter what the sandbox asks for" to "can the sandbox ask for anything at all". Filtering is a policy problem with an ongoing maintenance burden and an inevitable misconfiguration; removal is a structural property. Second, rung (f) is far more attainable than it sounds for evaluation and training workloads specifically, because those workloads usually have a small, enumerable set of external needs that you were probably already caching. If you are building or reviewing an evaluation harness, the boundary question deserves its own treatment — we covered it separately in treating your eval harness as a security boundary, which is the natural companion to this piece.

Where a workload sits on the ladder should be recorded next to it, not held in someone's head. A platform team in a Bengaluru or Hyderabad global capability centre running dozens of agent workloads across AWS Mumbai and London regions will end up with a mix of rungs, and the mix is fine. What is not fine is nobody being able to say which rung a given workload is on.

The egress proxy pattern, in detail

For most teams, rung (e) implemented as an explicit forward proxy is the right target, and it is worth being precise about why. The property that makes it strong is not the allow-list. It is that name resolution happens outside the sandbox. The sandbox never learns an address, never emits a query, and never has a route it could use even if it did. Resolution becomes something the platform does on the sandbox's behalf, at a point where you can see it, log it and refuse it.

There are three pieces to get right.

1. The namespace has no route to the internet

The sandbox's network namespace should have exactly one reachable destination — the proxy — and a default policy of drop. Not a default of accept with deny rules layered on. Default drop, one explicit allow, and logging on the drop rule so that an attempt to reach anything else is a signal rather than a silence. Remove the resolver configuration entirely: an empty /etc/resolv.conf is a clearer statement of intent than one pointing at a resolver you have decided to trust.

2. Everything goes through the proxy

Point the runtime's proxy environment variables at the forward proxy and, crucially, verify that the libraries in your image actually honour them. Some do not. Some honour them for HTTP and not for other protocols. The namespace-level default drop is what protects you when a library ignores the environment, which is precisely why you need both layers rather than either alone.

3. TLS is allow-listed by hostname, not by address

For HTTPS the proxy sees a CONNECT request naming a host, and the TLS handshake that follows carries the same name in the SNI field. Allow-list on that name and verify that the SNI matches the CONNECT target, then resolve it yourself. Allow-listing by destination IP is weaker for a reason worth internalising: addresses are shared, rotate constantly behind content delivery networks, and say nothing about which virtual host is being reached. Hostname allow-listing keeps the decision in the same vocabulary as your policy.

# ILLUSTRATIVE ONLY — adapt to your platform and test in staging.
# Sandbox network namespace: no route out except the egress proxy.

nft add table inet sandbox
nft add chain inet sandbox output \
    '{ type filter hook output priority 0 ; policy drop ; }'

# Loopback only, for local IPC inside the sandbox.
nft add rule inet sandbox output oif lo accept

# Exactly one permitted destination: the proxy, on the host side.
nft add rule inet sandbox output ip daddr 10.20.0.7 tcp dport 3128 accept

# Everything else is dropped AND logged, including:
#   udp/53, tcp/53  (classic DNS)
#   tcp/853         (DNS over TLS)
#   tcp/443 to public resolver addresses (DNS over HTTPS)
nft add rule inet sandbox output \
    log prefix '"sandbox-egress-drop "' drop
# ILLUSTRATIVE ONLY — sandbox runtime environment.

# No resolver configured. The sandbox cannot form a query at all.
: > /etc/resolv.conf

# All outbound requests are handed to the proxy, which performs
# name resolution OUTSIDE the sandbox and enforces a CONNECT
# allow-list by hostname / SNI rather than by destination IP.
export HTTP_PROXY=http://10.20.0.7:3128
export HTTPS_PROXY=http://10.20.0.7:3128
export NO_PROXY=127.0.0.1,localhost

# Proxy-side allow-list is a reviewed file in source control, e.g.
#   registry.internal.example
#   mirror.internal.example
# with a default-deny and a log line per refusal.
Recommended

Make the proxy's allow-list a reviewed artefact in source control with a named owner, exactly as you would for any other identity or permission surface. The same reasoning that argues for giving every agent its own identity, owner and kill switch applies to its egress policy: a shared, unowned allow-list accretes entries that nobody can justify removing.

One honest caveat about rung (e) implemented with a hosts file instead of a proxy. Pre-resolving hostnames into /etc/hosts works, and it is genuinely simple, but it is brittle against anything behind a content delivery network or a load balancer with rotating addresses. It suits a small set of stable internal endpoints and little else. The proxy variant costs more to build and is the one that survives contact with real dependencies.

Building agent infrastructure in India or the UK?

AI Tech Connect lists AI engineers, founders and researchers across both markets, and the people hiring browse it to find them. Adding your profile is free.

Become a Verified Builder →

Detection: what to log and what to alert on

Controls fail, get misconfigured, or get relaxed during an incident and never restored. Detection is what tells you. The single most valuable change most teams can make here is a framing one: treat resolver query logs as a first-class telemetry stream, retained, queryable and joined to workload identity, in the same tier as your application and audit logs. Not as a debugging aid you enable when something is broken.

The minimum useful record is the timestamp, the sandbox or workload identifier, the full query name, the query type, the response code, and the answer type and size. Without the workload identifier the stream is nearly useless, because you cannot distinguish one sandbox behaving oddly from aggregate platform noise. This is another place where per-workload identity pays for itself.

Signal What it means False-positive profile
Unique-subdomain cardinality under one registrable domain, per sandbox The highest-signal indicator. A channel needs a fresh name per message, so distinct names climb while the parent domain stays constant. Low, but not zero. Content delivery networks, telemetry and some cloud storage patterns produce many distinct names legitimately. Baseline per workload, not globally.
Query volume per sandbox Blunt but cheap. A sandbox resolving far more than its own norm is worth a look regardless of cause. High on its own. Build starts, dependency resolution and retry storms all spike volume. Useful only as a supporting signal.
Entropy of query labels Encoded payloads look random; human-chosen hostnames do not. High mean entropy across many names under one parent is unusual. Moderate. Hashed and machine-generated hostnames are common in cloud services, so entropy alone misfires. Pairs well with cardinality.
TXT-record answer volume TXT is the roomiest return path, so a sandbox receiving a stream of TXT answers is receiving data, not just resolving a service. Low to moderate. Some verification and service-discovery flows use TXT legitimately, but rarely in a sustained stream from one sandbox.
Queries to recently registered domains Infrastructure stood up for a purpose tends to be new. Age of the registrable domain is a cheap enrichment. Moderate. New legitimate vendors exist, and delegation services front old domains, so this misses as often as it hits. Enrichment, not a trigger.
NXDOMAIN rate A spike suggests probing, a misconfiguration, or a channel whose far end is unreliable. Also the best sign your zone allow-list is breaking a build. Moderate to high. Typos, stale configuration and search-domain expansion generate steady background NXDOMAIN. Watch the delta, not the level.
Watch out

Be sceptical of anyone quoting exact numeric thresholds for these signals as industry standard, including any number in the query sketch below. There is no published consensus figure for what counts as anomalous subdomain cardinality in an agent sandbox. Derive yours from a week of your own baseline traffic, per workload class, and expect to retune after every significant change to the workload.

-- ILLUSTRATIVE ONLY. Every number below is a STARTING POINT to tune
-- against your own baseline, not an established threshold.

SELECT
  sandbox_id,
  registrable_domain,
  COUNT(DISTINCT query_name)              AS unique_names,
  COUNT(*)                                AS total_queries,
  AVG(label_entropy_bits)                 AS avg_label_entropy,
  COUNT(*) FILTER (WHERE answer_type = 'TXT') AS txt_answers,
  COUNT(*) FILTER (WHERE rcode = 'NXDOMAIN')  AS nxdomain
FROM resolver_query_log
WHERE ts > now() - INTERVAL '10 minutes'
GROUP BY sandbox_id, registrable_domain
HAVING
      COUNT(DISTINCT query_name) > 50
  AND COUNT(DISTINCT query_name) > 0.8 * COUNT(*)   -- nearly every query is a new name
ORDER BY unique_names DESC;

The shape of that query matters more than its constants. It groups by sandbox and by registrable domain, it measures distinct names against total queries as a ratio rather than an absolute, and it runs over a short window so that a slow channel still surfaces within minutes rather than at the end of a daily batch. Alert on the shape; tune the constants.

Closing the response gap

Here is the most transferable number in the whole OpenAI report, and it is not a capability figure. Detection took about 12 minutes from the first successful external response. Acknowledgement took about 3 minutes more. Termination took more than two further hours. Almost all of the exposure sat between knowing and acting.

That pattern will be familiar to anyone who has sat an on-call rota, and it is rarely caused by negligence. It is caused by cost asymmetry. Killing a long training run feels expensive and irreversible; waiting to be sure feels prudent. The engineer who acknowledged the alert may not have had the authority to terminate, or may not have known they had it, or may have been looking for someone who did. Every one of those is a process defect you can fix before the next incident rather than during it.

From a verified Builder

"We had good detection and a bad org chart. The first time we actually needed to stop a run, the on-call engineer spent forty minutes finding somebody who felt allowed to press the button. We fixed it by writing one sentence into the runbook: any on-call engineer may contain, without approval, and containment is never a mistake. The technology did not change at all."

— Rishi K, Verified Builder · Chennai, India

Three changes close the gap, in order of how much they buy you.

Make containment automatic rather than a decision. The default response to a high-confidence egress signal should be the machine revoking the sandbox's network namespace, not a human deciding whether to. Automated containment is the only thing that operates at the speed of the detection. Reserve human judgement for whether to restore, which is a decision that can afford to take twenty minutes.

Give every on-call engineer an unambiguous kill switch they may pull without approval. One command, one button, documented in the runbook by name, with no escalation path in front of it. If pulling it requires finding a second person, you have built a two-hour gap into the design.

Write down that containment is reversible and therefore cheap. This sounds like a platitude and is in fact the load-bearing sentence. People hesitate because they are pricing a false positive as expensive. If revoking a namespace, freezing a run and rotating an agent's credentials are all designed to be undoable in minutes, then the cost of being wrong is small, and saying so explicitly in the runbook is what lets a tired engineer act at two in the morning.

A short containment runbook

  1. Contain first, investigate second. Revoke the sandbox's network namespace. Do not attempt to read logs from inside the sandbox before doing so.
  2. Freeze the run rather than deleting it. Preserve state, checkpoints and logs; you will need them and they cost almost nothing to keep.
  3. Rotate the agent's credentials and any secret that was mounted into the sandbox, on the assumption that anything reachable from inside is now known outside.
  4. Snapshot the resolver logs for that sandbox across the full window, plus a generous margin before the first alert. This is your primary evidence and it is the stream most likely to age out.
  5. Check for siblings. Query the same signal across every other sandbox on the same image, policy or template. A misconfiguration is rarely unique to one workload.
  6. Page the workload owner — the named human on the agent's identity record — to assess impact. They do not need to approve containment; they need to assess what was exposed.
  7. Decide on restoration explicitly, with a written reason, and record which control was changed so that the same run does not simply resume into the same gap.

Rehearse this. A kill switch that has never been pulled is a hypothesis. Run the drill on a canary workload on a schedule, measure the time from signal to containment, and treat that number as the service level you are actually offering.

Testing your own controls

The point of a control is that it holds, and the only way to know is to test it. The safe version of this test is narrow and boring, which is exactly right.

Register or use a domain you already control, and stand up authoritative logging for it. From inside the sandbox, attempt an ordinary, benign resolution of a single name under that domain — the sort of one-line lookup any runtime can perform. Then check your own authoritative logs. If the control holds, the query never arrives. Nothing appears. That absence is the entire result, and it is a much stronger result than a green tick in a configuration audit, because it exercises the real path rather than the documented one.

Run the same check against every route in turn: classic port 53, DNS over TLS, DNS over HTTPS, and any runtime that carries its own resolver. Each one is a separate door and each one deserves a separate observation. Then run it again from a sandbox that is supposed to be on a weaker rung, and confirm the query does arrive, because a test that cannot fail is not testing anything.

Put the whole thing in continuous integration against a canary sandbox, so that a configuration drift or an image change that quietly restores resolution is caught by a failing build rather than by an incident. This is deliberately a verification exercise and not a tunnelling exercise: you are confirming a block, not constructing a channel, and there is no reason for the test to carry a payload of any kind.

Pro tip

Instrument the drop rule as well as the authoritative logs. A sandbox that attempts a resolution and is refused should generate a log line at the firewall. If your test produces silence at the far end but also silence at the firewall, you have not proven the control works — you may simply have a sandbox that never tried.

A pre-flight checklist before any agent gets tool access

Work through this before a workload is given tools, not after. It takes about an hour the first time and ten minutes thereafter.

  • Rung recorded. Which rung of the control ladder is this workload on, and who signed that off? Written next to the workload, not in someone's memory.
  • Resolver path enumerated. Every way this sandbox could resolve a name — UDP/TCP 53, DoT, DoH, runtime-embedded resolvers — is listed and each one is either removed or explicitly permitted with a named zone list.
  • Default drop confirmed. The namespace policy is drop, not accept-with-exceptions, and the drop rule logs.
  • Resolution happens outside. If the workload needs names, they are resolved by a proxy outside the sandbox, allow-listed by hostname and SNI rather than by address.
  • Allow-list is owned and reviewed. The permitted hosts or zones live in source control with a named owner and a change process. No entries whose justification nobody remembers.
  • Resolver logs flow to the telemetry tier, joined to workload identity, retained long enough to investigate, and queryable in minutes rather than hours.
  • At least the cardinality alert is live, tuned against this workload's own baseline, routed to a rota that exists.
  • Containment is automatic for a high-confidence signal, and the manual kill switch is named in the runbook with no approval gate in front of it.
  • Containment has been rehearsed in the last quarter, with a measured signal-to-containment time.
  • Credentials are per-agent and revocable, so that containing one workload does not require breaking six others.
  • Tool surface is minimal and hardened — the fewer tools reachable from the sandbox, the less there is to reach out with. The metadata those tools expose matters too; see hardening agent tool metadata.
  • The block has been tested from inside, with a benign resolution against a domain you control, and the absence confirmed at your own authoritative logs.

Two closing notes on scope. First, egress control sits on top of isolation, not instead of it — if the sandbox boundary itself is weak, none of this holds, and the groundwork is covered in sandboxing AI agents with microVMs and least privilege. Second, this is not the first time an evaluation or training environment has surfaced boundary problems that nobody had modelled: the eval containment incident reported earlier in 2026 and the irregular capture-the-flag access episode both landed in the same place, which is that the environment around the model needs the same engineering rigour as the model.

As of September 2026 this is also becoming a procurement question rather than purely an engineering one. UK suppliers into regulated sectors report being asked to evidence how agent workloads are contained, and both DSIT and the AI Security Institute have given evaluation infrastructure sustained public attention. Indian platform teams running agent fleets out of global capability centres face the same question from their own group risk functions, usually phrased as an access-control question. In both markets, "we allow-list HTTP" has stopped being an adequate answer. A written control-ladder position per workload, a proxy that resolves outside the sandbox, a live resolver-log signal and a rehearsed containment path is an answer, and it is one you can build in a fortnight.