On this page
curl -s https://jedarden.com/notes/dont-be-the-meat-proxy.md The agent needed to know how a page rendered on my phone. So I picked up the phone, looked at it, and typed a description into the chat. The agent asked a follow-up — was the banner above the fold or below it? I picked the phone back up. Somewhere around the third round trip I understood what my role in this system actually was: two computers were having a conversation about a rendering bug, and I was the cable.
I’ve started calling this being the meat proxy: a human operating as the transport layer between two systems that have no direct channel to each other. You copy the stack trace out of one window and paste it into another. You read a dashboard aloud into a chat box. You run the command the agent gave you and relay what it printed. Nothing about the information changes on its way through you — no judgment applied, no decision made, no bit flipped. You are moving bytes by hand between two machines that are, in most cases, already on the same network.
Stated plainly, this is beyond stupid. It is also the default posture of nearly every agent workflow I see.
None of this is new; it barely counts as an observation. Enterprise IT named it decades ago — swivel-chair integration, a human pivoting between two terminals, re-keying what one system said into the other — and has been billing for it ever since. Sneakernet is the same job with better cardio. The most famous fix is Amazon’s service-interface mandate: the early-2000s edict that every team expose its data through service interfaces, no exceptions — this note’s argument, enforced at company scale more than twenty years ago. The pattern keeps getting rediscovered because every new class of system ships before its channels do. Agents are just the newest system we’ve defaulted to integrating by chair.
The worst network link you will ever operate#
Evaluate a human as transport equipment and the spec sheet is grim:
Latency. Seconds when you’re at the keyboard, hours when you’re not. You are the slowest hop in any pipeline you join, by three to six orders of magnitude.
Lossy by design. You don’t relay; you summarize. You paste the part of the error that looked relevant and drop the frame that mattered. A description of a screenshot is not a screenshot. Every transfer through a human is a transfer through a compressor with opinions.
Error-prone. Stale clipboard. Wrong window. The hand-redaction that removed one character too many. None of these failure modes announces itself — the corrupted payload arrives looking exactly like a good one.
Availability. You sleep eight hours. You have a job. Every transfer is synchronous with your attention, which means every transfer queues behind your entire life.
Unlogged. Neither system records that the transfer happened. It cannot be retried, audited, or replayed. The most important hop in the pipeline is the only one with no observability at all.
If a vendor tried to sell you this link, you would laugh them out of the room. Then you’d alt-tab and paste something.
Transport is not judgment#
The steelman for humans-in-the-loop is real, and I want to be precise about what this note is not arguing against. A human as a decision point is often exactly right: approving the irreversible action, reviewing the diff before merge, catching the number that can’t possibly be true. In those loops, something passes through your judgment and comes out different — approved, rejected, corrected. That is the job. That is the part worth being slow for.
The meat proxy is the degenerate case: information passes through your hands and comes out identical. The test is one question — could this transfer have been a pipe? If replacing you with cat would lose nothing, you weren’t a decision point. You were plumbing.
Friction in a system should be a decision, not an accident. Deliberate friction — the approval gate, the review step — is a control surface you designed. The meat proxy is accidental friction: a tax you pay on every single transfer, forever, because nobody spent one afternoon building the channel.
And some human channels are the design rather than the accident. An air gap is a meat proxy built on purpose: the entire point is that bytes cross only through a person, slowly, visibly. A compliance attestation is transport that exists to be slow and signed — the human carrying the number is the audit trail. Keeping yourself as the only path into production can be a deliberate rate limiter on what an agent can do there. Run the test and these pass it: replacing you with cat would lose exactly the thing the design is for. What fails the test is only the friction nobody chose.
Build the channel once#
Every direct channel in my setup exists because I got sick of one specific copy-paste loop:
The phone. The rendering question that opened this note doesn’t reach me anymore. The phone is on the tailnet with ADB exposed; the agent takes a screencap, reads the image itself, and taps the screen if it needs to navigate. The phone went from “thing I describe” to “peripheral the agent operates.” As a bonus it became the failover web connection — when a request fails from the server, the agent checks the URL through the phone’s own network before concluding anything is down.
The clusters. I used to be the person pasting kubectl get pods output into a chat window. Now every cluster runs a read-only proxy the agent can query directly. When it wants pod status, it asks the cluster — the actual authority on the matter — instead of asking me to ask the cluster.
Command output. Commands the agent needs run now run where both of us can read the result, instead of me ferrying stdout across a window boundary one selection at a time.
A real browser. An old laptop on the bench keeps a live display session up; the agent screenshots it and drives a real browser when “does this actually render” is the question.
Each of these took an afternoon or less. Each one deleted a category of copy-paste permanently. That’s the whole economic argument: the meat proxy charges per transfer, forever; the channel charges once.
It’s worth pricing the paste honestly, because it always bills itself as cheaper than it is. A relay looks like eight seconds of copy-paste; what it actually costs is a context switch. In one field study of information workers, 40% of disrupted tasks were not resumed immediately after the interruption. Even at a skeptic’s two minutes per relay, a loop that fires daily repays an afternoon of plumbing within a few months. The arithmetic only decides when the channel wins, never whether — per-transfer pricing always loses to a one-time build eventually, and eventually is closer than it feels. And that’s only the human side of the ledger. The agent’s side is wall-clock: a question that used to wait hours for my eyes — overnight, if I was asleep — is now a two-second screencap.
Increasingly you don’t even pay the afternoon. My channels are hand-built because my systems are homelab-shaped, but for anything SaaS-shaped the wire is being standardized out from under the problem: MCP — the Model Context Protocol — is the industry converging on exactly what this note argues for, a common plug so the ticket tracker, the mailbox, the docs, the database present themselves to the agent as a connector you install rather than a conversation you relay. Before building a bespoke channel, check whether someone already shipped it. When the afternoon collapses to ten minutes of setup, the last economic excuse for the paste goes with it.
From paste to pipe#
The examples above are mine; the loops are universal. A short translation table — on the left, the paste you keep performing; on the right, the channel that deletes it:
| The paste | The channel |
|---|---|
| Reading a dashboard aloud | A scoped read-only API token. Nearly every service you’d describe to an agent ships one, and read-only scoping keeps the channel transport, not authority. |
| Pasting the three log lines that looked relevant | Query access to the log store, so the agent runs the search itself instead of receiving your compression of it. |
| Running the query and pasting the rows | A SELECT-only database role, or a read replica. |
| Forwarding the confirmation email | An inbox of the agent’s own, or read access to a shared one. |
| Ferrying credentials out of the password manager | A secrets store the agent queries with scoped access. The reference travels; the value never does. This is the one loop where human transport isn’t just slow — it parks the payload in a chat transcript. |
| Emailing yourself a file so you can paste it | A shared drop both sides can touch: a bucket, a synced folder. |
| Reading a boot screen or a device console to the agent | A KVM over IP, a serial console. Anything with a screen or a UART can be a peripheral; the phone was just the first. |
| Relaying between two agents — “worker A finished, go tell B” | A durable work queue. At fleet scale this isn’t an optimization; it’s the only channel there is. |
None of these is exotic. Every one is a read-only credential, a webhook, or a queue — technology that predates agents by decades. The missing piece was never the wire; it was noticing you’d become one.
And for the transfers where a human genuinely must carry the message, you can at least stop being lossy. The rule: carry captures, not descriptions. The screenshot, not what the page looked like to you. The terminal recording, not the two lines that seemed relevant. The HAR file, not “it felt slow.” UI feedback is the classic case — Agentation exists for exactly this: click the element and the agent gets a CSS selector and DOM context, not “the card in the top right.” If you must be the proxy, don’t also be the compressor.
There is a scale version of this, too. Interactively, the meat proxy wastes minutes. At fleet scale it is disqualifying: twenty headless workers cannot route their I/O through my hands, so the only transfers that happen at all are the ones with a real channel. And when a needed channel is missing, a fleet doesn’t wait for one — it improvises. The only channel every worker shares is the git repo itself, and I have watched 800 megabytes of database dumps land in commit history because one worker was told to hand an artifact to the next and git was the only wire they had. The mirror was down for ten hours. That wasn’t the worker’s failure; it was mine: an unbuilt channel doesn’t mean the transfer won’t happen, it means it happens over the worst wire available. Building the channel isn’t an optimization on the loop — it’s the precondition for taking yourself out of it.
The same bug, running backward#
Everything above is the agent pulling. The mirror-image loop runs the other way, with you as the interrupt controller: the build failed and you tell the agent; the alert fired and you paste it in; the deploy finished and you announce it. Same bug, opposite direction — an event occurred in one system and the news traveled by human. The fix is push: events should land where an agent will read them, or wake one on arrival.
The translation table for this direction is shorter, because the fix is always one of three shapes — a webhook, a bridge, or a schedule:
| The announcement | The push channel |
|---|---|
| ”The build failed” | The CI system’s completion webhook, pointed somewhere an agent reads |
| ”The alert fired” | A bridge from the alerting stack into the agent’s queue — the page should wake a responder, and the responder doesn’t have to be you |
| ”The deploy finished” | The pipeline’s own completion hook, so the event starts the next step instead of merely informing you |
| ”I happened to notice the dashboard looks wrong” | A scheduled agent that checks on a cadence — noticing becomes a job with an owner, and the owner is never asleep |
“I happened to notice” is not an alerting pipeline, and you should not be a load-bearing part of one.
The questions that never get asked#
The setup cost is lumpy and the paste is not, which is exactly how the friction survives. Eight seconds of copy-paste never feels worth an afternoon of plumbing, so the afternoon never happens, so the eight seconds happens forever.
But the pastes you perform are the visible cost, and the smaller one. When asking you is expensive, the agent economizes: it stops asking. It works blind, guesses instead of checking, and hands you output shaped by every question it didn’t ask. Demand for verification is elastic, and you priced it out.
You can watch the elasticity run the other way the day a channel lands. The cluster proxies get queried constantly — pod status, logs, events, dozens of checks a day that would each have been an interruption with me in the path. Almost none of those checks used to happen. The deepest return on a channel isn’t the pastes it deletes; it’s the checks that start occurring once they’re free.
What it costs#
Channels carry authority. A direct channel means the agent can do the thing without you, and that should make you think before it makes you comfortable. The answer is scoping, not abstinence: nearly every meat-proxy loop I’ve replaced was read-only, so the channels are read-only — my cluster proxies mechanically cannot write. Where a channel would carry write authority, that’s a decision point wearing a transport costume, and the human stays. Trust is a property of the system: the fix for a scary channel is scope and reversibility, not vigilance.
Channels are faithful, and that cuts both ways. The one real leak I’ve had didn’t involve a single system misbehaving: a build step echoed a tokenized clone URL into its own log, an agent dutifully committed the log, and the mirror replicated the commit out to public GitHub — every hop doing exactly its job, every hop keeping a copy. (GitHub’s secret scanner found it before I did and revoked the token on sight.) The lesson wasn’t vigilance, and it wasn’t fewer channels. It was that the credential should never have been on a wire as a value — it travels by reference now, and becomes a value only as environment at the moment of use. Build channels freely; just remember they will transport your mistakes with the same fidelity as your intentions.
Sometimes the copy-paste was doing double duty. Eyes on the data as it moved through you — an accidental review step. Remove the transport and you remove that too, so put the review back deliberately, as a gate you designed, instead of a side effect of your own slowness.
The question I now ask#
When I catch myself copying from one window and pasting into another:
Did anything here pass through my judgment, or only through my clipboard?
If it passed through judgment — a call was made, a number was checked, an action was approved — then that’s the job, and it should stay mine. If it only passed through the clipboard, I just found a missing channel, and every repetition of that paste is another vote for spending the afternoon.
That’s the instance test. The systematic version: for one week, keep a tally of every time you copy out of one window and paste into another — what moved, where from, where to. Change nothing else. At the end of the week the tally is your channel backlog, pre-sorted by frequency, and the top line is what next weekend’s afternoon is for.
The systems can talk to each other. They are usually on the same network. The only reason you’re carrying the message is that nobody built the wire — and you’re the only component in the whole pipeline that can fix that.
— Jed
Related: Trust is a property of the system, not the agent — why the fix for a scary channel is scoping and reversibility, not vigilance. And Don’t let the agent grade its own homework — the review step you must re-add on purpose once you stop being the wire.