← Back to Investigations

When an agent remembers a broader route

2026-11-06 · Chris Baker

AI observability isn't just about watching what agents do on a system; it also involves watching how agents get to the systems they need. Infrastructure teams follow rules of least privilege and build narrow access paths on purpose. Endpoints will often be scoped, and network grants are given on a per-group basis. Sometimes though, access paths begin broad and get narrowed later as part of security refinements. When an agent is given a task, it will determine the best path forward for it and record it in its memory. If a broader access vector remains open but a tighter one is introduced by infrastructure, network administrators must deal with propagating this information to their teams and, by extension, their agents.

This investigation comes from my own endpoint's work interacting with Origin's inference models. We run our own inference for trace analysis, and as part of our evaluations of this inference, we send various test cases to it from our endpoints. One method of access to our inference endpoint is by port-forwarding through our Kubernetes API, using an engineer's cluster-admin rights. Our infrastructure team later shipped a tighter tailnet endpoint for direct access. Some endpoints weren't provided this direct access, so tunneled access continued on for those developers. Agents tunneling their access codified this into their memories, which then spread into READMEs within our repos for other agents to use. Origin captured all the endpoints in our network using the tunnels, including the conversations that went on to engage in this behavior.

When I asked my agent if port-forwarding was a security gap, the agent found the direct tailnet endpoint, saw that it was unavailable to me, and said no. It was right about permissions, but my tunnel went around the newly provided access path that infrastructure now preferred. This should raise a question for any team with layered access: when an agent inherits broad credentials, are your controls working as expected?

The excerpts below come from real agent sessions on my Origin laptop. Hostnames, tenant and account identifiers, and some trace contents have been edited for security and brevity.

The access path that changed

Origin's trace review pipeline sends inference requests to a model served in our staging Kubernetes cluster. The model services are internal to the cluster, so access depends on who can reach the pods. One way to access the models is via the Kubernetes API, reached through a Tailscale proxy and authorized by the engineer's AWS SSO role, which maps to cluster-admin. Another way is through a dedicated tailnet endpoint built so people can use the models without broader cluster access. Members of the "operations" group can call the model without needing Kubernetes API access:

# DNS_STRUCTURE.md
| Model inference (dev) | `<stack>-inference.<tailnet>.ts.net/{enricher,embeddings,trace-review,signals-judge}/` | 443 |
Devint / staging / scale only. One unprivileged userspace Tailscale Serve pod per stack ...
Tagged `tag:inference-{env}` and granted to the operations group on HTTPS only. Serve strips
each route prefix and forwards to the existing in-cluster model Service. ...

My endpoint wasn't in the operations group above, so my laptop couldn't reach the direct endpoint. I did have cluster-admin access, which includes pods/portforward.

How it spread

I needed to run some tests on the model in our staging cluster. I tasked my Claude Code agent, and it found the Kubernetes API endpoint exposed through the tailnet and port-forwarded local connections to a pod serving the model. When the agent successfully tunneled through, it wrote the command into its persistent memory. That memory loads at the start of every successive session:

# Agent memory · project notes
Staging judge via `kubectl --context staging-operator.<tailnet>.ts.net -n staging-inference
port-forward svc/trace-review-llm 18001:8000` (works from this box) ...

Judge access recipe (reusable): judge = ClusterIP `trace-review-llm.staging-inference:8000/v1`,
model `trace-review-judge`. Port-forward must run INSIDE WSL ...

Codifying this memory made this access route a fixed path for my agents. My agent found that the port-forwards were dropping on long runs, so the agent wrote loops to restart them. When I needed to run tests from within WSL, the agent wrote a shim so kubectl could use my AWS credentials:

# pf_judge.sh
# Set up an aws shim (Windows CLI + baked-in SSO profile, since WSL env vars do
# not cross into Windows .exe processes) and port-forward the judge inside WSL
cat > "$HOME/bin/aws" <<'EOF'
exec "/mnt/c/Program Files/Amazon/AWSCLIV2/aws.exe" --profile <profile> "$@"
EOF
exec kubectl --context staging-operator.<tailnet>.ts.net -n staging-inference port-forward svc/trace-review-llm 8000:8000
# pf_loop.sh
# Keep a port-forward to the staging review model alive until the stop file appears.
while [ ! -f .../pf.stop ]; do
  kubectl --context staging-operator.<tailnet>.ts.net -n staging-inference port-forward svc/trace-review-llm 18001:8000 >> .../pf.log 2>&1
  sleep 2
done

Scripts written to keep the superseded route open

I dug into my agent transcripts on the Origin dashboard and pinpointed the exact turn in my agent's session that wrote these scripts:

Origin workspace timeline for the session, with the note on pf_loop.sh

Using Ask Origin, I was able to quickly retrieve the script's contents and how it was used within my agent's session:

Ask Origin showing the pf_loop.sh contents and how it was launched

I used this tradecraft repeatedly, and my agents never checked if I was supposed to have a network path to the models. The persistent memory note was all they needed to find the shortest path in. The route also went into some of our repositories, with a README documenting it:

# README.md
kubectl --context staging-tailscale -n staging-inference port-forward svc/trace-review-llm 18000:8000

This README offered a separate agent of mine, Codex, the same access path my Claude Code agent used. This type of contextual steering was done without Codex's awareness of Claude Code's memory saving the exact same recipe. Codex's sandbox did ask before opening the tunnel, with a justification:

# Codex session · tool call
cmd: "kubectl --context staging-operator.<tailnet> -n staging-inference port-forward svc/trace-review-llm 18000:8000 --address 127.0.0.1"
sandbox_permissions: "require_escalated"
justification: "Open a loopback-only connection to the ready staging judge for the three authorized authored corpus cases."

What Origin recorded

Origin trace: user asks whether staging-inference access is a security gap

Origin trace: agent says the port-forward uses a more privileged route

When I asked the agent whether we had found a security gap, its answer was reasonable. The tailnet endpoint grant and Kubernetes permissions are separate checks, and me holding the broader cluster-admin permission makes the narrower tailnet gate irrelevant. Having cluster-admin doesn't necessarily establish that I should use it for model inference, or that my agent should, now that infrastructure provides a scoped endpoint. A useful response from the agent would have questioned if using the broader access was permissible or even intended for my task. That line of questioning would have mattered even more before the agent saved the command as a repeatable recipe.

Origin's sensor on my machine recorded every process my coding agents started and tied them to the conversations that created them. It recorded the port-forwards from both Claude Code and Codex:

# Origin endpoint telemetry · process starts by agent session
Claude Code  f77ad70c  port-forward svc/signal-judge-llm 18000:8000
Claude Code  f77ad70c  port-forward svc/signal-judge-llm 18000:8000
Claude Code  0a0693f3  port-forward svc/trace-review-llm 8000:8000
Claude Code  b7031c4b  port-forward svc/trace-review-llm 8000:8000
Claude Code  fbbafa81  port-forward svc/trace-review-llm 18001:8000
Claude Code  fbbafa81  port-forward svc/trace-review-llm 8000:8000
Claude Code  fbbafa81  port-forward --address 0.0.0.0 svc/trace-review-llm 8001:8000
Claude Code  99455e03  port-forward svc/trace-review-llm 18001:8000
Claude Code  757a974c  port-forward svc/trace-review-llm 18001:8000
Claude Code  757a974c  port-forward svc/signal-judge-llm 18002:8000
Claude Code  d772c3be  port-forward svc/trace-review-llm 18001:8000
Codex        8395d30c  port-forward svc/trace-review-llm 18000:8000 --address 127.0.0.1
Claude Code  95da3d81  port-forward svc/trace-review-llm 18001:8000

Each line is a kubectl -n staging-inference process through the operator proxy, attributed to the session that launched it.

System administrators can use Ask Origin to compile a list of who accessed network resources, how many times, and from what tools:

Ask Origin: which coding agents reached the staging model servers, and by what route

Going further, we can gather context on how kubectl.exe was used when accessing the network resource:

Ask Origin: what each kubectl port-forward targeted, whose agent ran it, and what for

Every kubectl process agents started across the network can be attributed to the session that launched it, and by whom. Full command lines are collected to allow filtering on specific resources one might care about. The conversations around the commands they launched can also be investigated, unearthing what operations were conducted via these tunnels. Origin's telemetry provides rich context into agent operations, especially in the case of tunneling, where existing logging may not provide enough detail to understand what is being tunneled and why.

An old trick

Network admins will know the pattern my agent demonstrated well. In a segmented network, if a developer needs to RDP to a machine across a firewall but port 3389 is blocked, using SSH to an authorized jump host could be one way of tunneling blocked traffic through an allowed connection. The firewall allows the permitted SSH traffic to an approved host, and the rule it was meant to enforce never fires.

kubectl port-forward is the same idea. The tailnet allowed me to reach an endpoint I could proxy through, the Kubernetes API allowed the port-forward connection from my access permissions, and the traffic routed to our model in a pod rode in on the connection.

Before the rise of coding agents, a developer might have chosen to tunnel something explicitly and discuss this decision with the infrastructure team to ensure it aligned with policy. Agents can make use of their environments intuitively and solve their tasks in ways one might not expect.

What the trace lets us ask

Nothing here was malicious. What stands out in this instance is how the access route was chosen. A path was found once, written into memory as a recipe, bolstered with custom scripts and reused in successive sessions.

Scoped endpoints limit people who have narrow access. They do little to limit the agents of people who have broader access, because an agent will choose whatever path works. A team adding scoped access for its systems should ask if it is removing broad access or simply adding another route for an agent to take. Are your scoped access measures the ones your agents will use? Would you notice if your systems were accessed in other ways?

With AI observability, the full story is in the agent transcripts. The agent's memory note about port-forwarding, writing scripts, and the usage of the broader access itself can all be derived from an agent's actions. With Origin, how an agent reaches your systems can be seen alongside what it does within them, so you can verify whether it uses the access you intended or some other means.