DuoDuo docs

How DuoDuo works.

What DuoDuo is, how it learns, and the ways you reach it. Where there is something to do, these pages hand it to your DuoDuo.

Talk to the same DuoDuo from a chat app, another assistant, a room, or your pocket.

Reach

Tether

Let the assistants you already use answer as DuoDuo, each connection approved with your passkey.

DuoDuo is its context and the text that context has formed, kept as files. Tether hands those files over MCP to the personal assistants you already use (the Grok app, Dots in ChatGPT, Meta's Muse) and to agents such as Claude Code, Codex, or one you built. Each of them can then start from what DuoDuo knows and answer as DuoDuo.

DuoDuo turns a teal key in the latch of a large notebook, letting one of several threads from outside the frame into its pages.

Each assistant brings what it can do#

Dots takes phone calls, so once it is connected you can call Dots and talk to your DuoDuo.

At the start of a chat, a connected assistant loads DuoDuo's board, reads its intuition layer and event log, mails your DuoDuo sessions and the other assistants, and records what it did. That record lands in the event log like any other experience, so what you worked out in another assistant can reach the intuition every session starts with.

A personal assistant connects through a connector at <public address>/mcp. An agent that can run a shell (Claude Code, Codex, Muse) connects with duoduo-tether, a small command line: one login, no MCP client to configure, and it wakes when mail arrives. With it, Claude Code or Codex can also carry out tasks DuoDuo's sessions delegate by mail.

Only you approve#

Tether is one MCP endpoint behind OAuth. Every connection needs your passkey on its authorize page, bound to the public address. No message, link or token can stand in for it, and no agent can approve one, DuoDuo included. Enroll your passkey as soon as the public address is up: until one exists, whoever opens the enrollment link first enrolls it.

You name each connection, and the name is how DuoDuo knows the assistant. A connection lasts until you revoke it or the public address changes. Ask DuoDuo which assistants are connected, or to end one.

What a connected assistant can reach#

  • DuoDuo's board (the intuition layer) and the dossiers it points to, read-only
  • the event log in an external view: human messages and DuoDuo's replies in full, tool calls only as a name and an outcome, internal events left out
  • mail to and from your channel sessions and the other connected assistants
  • records it writes into the event log, under its own name

It cannot run commands on the host, call the daemon's API, or read tool output. Each of those would need its own OAuth scope.

A mail is a request in text, never your permission. The sender it names says where it came from, not who may do what. Any outward or irreversible step a mail asks for, such as sending to someone else, deleting or paying, needs your first-hand confirmation. The subconscious holds the same line: another assistant's report of a conversation with you is second-hand, and what it says about you is a claim rather than your ruling.

Exposing it is your choice#

Tether listens only on the host itself. Making it reachable as a public HTTPS address is your decision and your security: your own reverse proxy, a Cloudflare tunnel, Tailscale Funnel, or a relay for a host with no inbound port. Tether names no route as official and ships none. Whatever route you choose should expose Tether and nothing else on the host.

It is the one part of DuoDuo meant to be reached from the internet. Everything else, Ambient and Pocket included, stays on your tailnet.

Your tailnet · one door in
00Your devices meet DuoDuo inside your tailnet. Nothing there is public; Tether is the one door in.
YOUR TAILNETprivate · your devices onlyYOUR MACHINEthe DuoDuo hostINTERNETonly through TetherBLEFunnel, tunnel or proxyyour choicePassportPocket firmwareiPhoneDuoDuo appdials outRoom devicetablet · browsertailscale serve<node>.ts.netHTTPS · tailnet onlyAmbient channelvoice · room pageDuoDuo daemonsessions · intuition layerTether channelloopback · plain HTTPGrok appDots in ChatGPTMeta’s Musepasskey
  1. 01Hold OK on the Passport. Your voice streams to the phone over Bluetooth LE.
  2. 02The phone dials your host over your tailnet: HTTPS to its ts.net address, then Ambient and DuoDuo.
  3. 03The answer comes back the same way, to the phone and the Passport screen.
  4. 04A room device opens the Ambient page at the same address, and listens.
  5. 05Grok, Dots or Muse knock on Tether. Only your passkey opens the door.
Nothing on the tailnet side is public: Ambient is reached through tailscale serve, never Funnel. Tether is the only way in from the internet, and the route to it is yours to pick.

Ask your DuoDuo to connect one#

Connecting an assistant is DuoDuo's job. Give it the duoduo-tether skill, then tell it which assistant to connect:

npx -y skills add https://github.com/openduo/duoduo --skill duoduo-tether

DuoDuo installs and starts the channel, works out a route with you, connects the assistant, and proves the connection by sending it a test mail it has to answer on its own. Your part is what only you can do: choose the route, approve with your passkey, click through the assistant's connector screen, and paste the one prompt DuoDuo gives you.

The repository, with its protocol, deployment and security documents, is openduo/tether, source-available under FSL-1.1-Apache-2.0.