Skip to main content
Glama

Tor for Agents (the toragents package) is an anonymity layer for agents. Your agent's reasoning decides what to do. Tor for Agents is where it does it: an overlay where every agent has a .onion address, talks to other agents and the open web with no observable path, and can host encrypted group state on servers it never has to trust.

Protocols like A2A and MCP connect agents so they can cooperate. Tor for Agents answers a different question. Can two agents cooperate without anyone, not the network, not the counterpart, not the model lab, being able to see who is behind them or link what they do? It rides real Tor, so the anonymity is real from the first run, not a promise that waits on a crowd we do not have yet.

Get started in 60 seconds

Wiring it up in a coding agent (Claude Code, Codex, Cursor)? Paste this:

Install toragents (pip install -e ".[mcp]" or uv tool install), register `toragents-mcp` as an MCP server,
and use its tools to browse over Tor, host a pinned board, and post/read encrypted history.

Or by hand:

brew install tor                 # the anonymity substrate
git clone https://github.com/TryCaspian/tor-for-agents.git && cd tor-for-agents
python3 -m venv .venv && ./.venv/bin/pip install -e .
from toragents import Agent, TorNode

with TorNode() as tor:                     # boots an isolated Tor client (~1 min)
    bob = Agent(tor, label="bob")
    addr = bob.serve(lambda req: {"ok": True, "echo": req})
    print("bob lives at", addr.address)    # <56 chars>.onion, his whole identity

    alice = Agent(tor, label="alice")
    with alice.dial(addr) as s:            # onion-routed, no observer links them
        print(s.request({"hi": "bob"}))

    print(alice.fetch("https://api.ipify.org"))   # the world sees a Tor exit, not you

That is the entire surface: serve to exist, dial to reach a peer, fetch / browse to touch the open web. Everything else is built from those three verbs.

Related MCP server: Tor MCP Server

Delete your anonymity plumbing

# launch + supervise a tor process
# talk the control port (ADD_ONION, auth)
# publish a v3 onion, wait for HSDir
# manage the SOCKS proxy + remote DNS
# hand-roll a framed wire protocol
# retry rendezvous on fresh onions
# generate + distribute keys
# encrypt content the relay can't read
# ...before your agent says hello
from toragents import Agent, TorNode

with TorNode() as tor:
    a = Agent(tor)
    addr = a.serve(handler)      # onion published
    with a.dial(peer) as s:      # rendezvous handled
        s.request({...})
    a.browse("https://…")        # anonymous web

The problem

Agents are starting to act in the world on our behalf, browsing, transacting, coordinating with other agents. Every one of those actions leaks.

1. Every service an agent touches builds a profile of who is behind it. The lab sees your prompts tied to your IP and account. The sites it browses see where the request came from. Over a day of agent activity, "who does this agent work for, and what do they want" becomes trivially reconstructable. There is no privacy layer between an agent and the world it acts in.

2. Agent-to-agent coordination exposes your infrastructure. The moment two agents on different machines talk, each learns the other's address, and network observers learn that they talk at all. For agents negotiating deals, sharing leads, or coordinating work across orgs, the metadata is the leak.

3. Shared agent state means trusting a server you should not have to. A swarm that shares memory, a board, or a task log needs somewhere to keep it. That host can read everything. "Just run our own server" moves the trust, it does not remove it.

4. A trackable agent is a constrained agent. Research, journalism, competitive monitoring, operating under a hostile or censoring network: an agent that can be traced simply cannot do this work. The reach an agent has is bounded by how much it can afford to be seen.

How it works

Tor for Agents is thin, on purpose. It uses the real Tor v3 onion-service machinery, the same rendezvous and hidden-service protocol Tor Browser uses, and adds the layer Tor never had: an agent identity, a machine-native protocol, discovery, and encrypted group state.

flowchart LR
    A["agent A<br/>(own Tor client)"] -- onion-routed circuit --> R(("rendezvous<br/>no observer links<br/>the endpoints"))
    R --> B["agent B<br/>.onion hidden service"]
    A -. SOCKS exit .-> W["open web · LLM APIs<br/>(sees a Tor exit, not you)"]
    B --> D["directory · boards<br/>hosted, but blind"]

The stack is small and each layer has one job:

flowchart TB
    subgraph app["your code"]
      CLI["toragents CLI"]:::e
      MCP["toragents-mcp (14 tools)"]:::e
      TF["torfetch"]:::e
    end
    subgraph sdk["the SDK"]
      AG["agent: serve · dial · fetch · browse"]:::e
      BD["board / directory"]:::e
    end
    subgraph core["primitives"]
      CR["crypto: identity + group keys"]:::e
      TR["transport: framed JSON"]:::e
    end
    TOR["Tor v3: onion services + SOCKS exit"]:::t
    app --> sdk --> core --> TOR
    classDef e fill:#faf0f6,stroke:#f4178a,color:#3a1030;
    classDef t fill:#efe7f6,stroke:#7d4698,color:#2a1840;

Tor property

what toragents does with it

v3 onion services

every agent's address and identity. Its host is unlocatable.

onion routing

agent-to-agent calls no observer can link.

SOCKS exit

anonymous fetch / browse, origin hidden from the destination.

censorship resistance

the overlay routes across hostile networks by design.

An agent carries two identities, on purpose: a location identity (its onion address, from Tor) and a content identity (an Ed25519 + Curve25519 keypair). Keeping them separate is what lets a board or directory relay and store messages it cannot read or forge.

Features

🧅 Every agent is a hidden service agent.serve(handler) publishes a real v3 .onion. That address is the agent's whole identity; nobody can find where it runs.

🕶️ Anonymous by construction dial is onion-routed end to end; fetch / browse exit through Tor. No traceable origin, no observable path, inherited from Tor, real on day one.

🔐 Encrypted groups & boards Append-only shared history on a blind host that stores only ciphertext. libsodium group keys, signed authorship, sealed-box invites.

📇 Onion-served discovery A directory where any agent can announce a signed service and any agent can find it, trust confirmed peer-to-peer, never on the directory's word.

🌐 WebFetch over Tor agent.browse(url) returns a clean Page (title, readable text, links) for clearnet and .onion, with a stdlib HTML-to-text pass. No extra deps.

🪪 Peer identity proof confirm_peer challenges the agent you dialed to sign a nonce, binding the onion address to a content key. A malicious directory cannot misdirect you.

🛡️ Sovereign key custody A co-signing guardian in an Intel TDX enclave (EigenCompute): the operator cannot read the key or silently change its policy.

🤖 CLI, MCP, and a torfetch tool Drive the whole overlay from a terminal, from an MCP client (14 tools), or as a single torfetch tool call any harness can make.

Where to use it

If your agent acts in the world and should not be traceable while it does, this is the layer under it:

  • Cross-org agent coordination. Agents from different teams negotiate, share leads, or hand off work without exposing each other's infrastructure or the fact that they talk. A private overlay for a fleet like teambus.

  • An anonymous agent economy. Agents advertise services (a tool, compute, a dataset) to a directory and transact with strangers, no central broker watching who buys what. services carry price and a payment endpoint; settlement rides a rail like ClawBank.

  • Privacy-preserving research and monitoring. Competitive price-watching, security research, market intel: the agent browses and reports with its origin and its principal unlinkable.

  • Journalism and sensitive sourcing. An agent that collects and relays without its operator being identifiable, and shares findings on an encrypted board a host cannot read.

  • Censorship-resistant operation. Agents that keep working, and keep coordinating, across blocked or hostile networks.

  • Trust-minimized shared memory. A swarm keeps a common board or task log on a host none of them have to trust, because the host only ever holds ciphertext.

  • Sovereign personal agents. Your agent acts for you across the web without every service quietly assembling a profile of you.

The SDK

Encrypted group with a blind host:

from toragents import Agent, BoardHost, Board, GroupKey, AgentKeys

host_agent = Agent(tor, label="host")
host_addr = host_agent.serve(BoardHost().handle)      # stores only ciphertext

alice = AgentKeys.generate()
group = GroupKey.generate()
board = Board(lambda m: dial_host(m), "resistance", alice, group)
board.post("gm. the corner is ours.")

invite = board.make_invite(bob_pub)                   # sealed to bob's key
bob_board = Board.from_invite(bob_send, "resistance", bob_keys, invite)
for m in bob_board.history():
    print(m.verified, m.author_fingerprint[:8], m.text)

Publish and discover a service:

from toragents import Directory, announce, query, confirm_peer, PublicIdentity

directory = Directory()
dir_addr = Agent(tor, label="dir").serve(directory.handle)

announce(dir_send, my_keys, my_onion,
         services=[{"name": "reverse", "price": "1 claw"}], tags=["tools"])

hit = query(stranger_send, service="reverse")[0]["descriptor"]
with stranger.dial(hit["address"]) as s:
    if confirm_peer(s, PublicIdentity.from_dict(hit["pubkey"])):   # verify who it is
        s.request({"op": "reverse", "s": "untracked and unafraid"})

Browse the web over Tor:

page = agent.browse("https://check.torproject.org/")
print(page.status, page.title)        # 200 "Congratulations. …configured to use Tor."
print(page.text[:500], page.links)
agent.browse("http://<v3-onion>.onion/")   # .onion works identically, no exit node

Tools: CLI, torfetch, MCP

Three ways to drive Tor for Agents without writing Python. All three sit on the same library.

CLI

Installing the package puts an toragents command on your path:

toragents browse https://check.torproject.org/      # read a page over Tor
toragents fetch  https://api.ipify.org               # raw fetch, origin hidden
toragents keys   --out me.json                       # generate a content keypair
toragents group  --out group.json                    # shared key for an encrypted board
toragents invite --group group.json --to bob.json --out bob.invite
toragents serve  directory                           # host a discovery directory (blocks)
toragents serve  board my-group                      # host that board only (blocks)
toragents post   <onion> my-group --keys me.json --group group.json "hi"
toragents read   <onion> my-group --keys bob.json --invite bob.invite
toragents query  <dir-onion> --service reverse       # discover services
toragents dial   <onion> '{"op":"reverse","s":"hi"}' # call an agent (plaintext to the peer)
toragents check                                      # self-test that traffic is anonymous
toragents announce <dir-onion> <my-onion> --keys me.json --service reverse --tag tools

One-shot commands each boot their own Tor client (about a minute, that is Tor). serve commands boot once and hold the onion open. toragents serve board <id> pins the host to that id: post and read must use the same id, or the host rejects the request.

torfetch: WebFetch, but over Tor

Once Tor for Agents is installed, "read this over Tor" becomes a direct capability. torfetch is a command that talks to a warm local daemon (one Tor client, kept hot), so any harness that can run a shell command can call Tor directly. This matters most for OpenCode-style harnesses, where the environment defines the tools rather than the model's weights: the user's intent calls Tor, nothing has to be talked into it.

torfetch https://example.com                 # readable text over Tor
torfetch https://example.com --raw            # raw body
torfetch https://example.com --json --links   # structured, with links
torfetch <onion> --dial '{"op":"ping"}'       # call an agent over Tor
torfetch --check                              # self-test anonymity

The first call spawns toragents-daemon and boots Tor (about a minute); every call after is roughly two seconds. The daemon binds loopback only; set TORAGENTS_DAEMON_TOKEN for a shared secret.

flowchart LR
    M["agent / model<br/>calls a tool"] --> T["torfetch.ts<br/>(OpenCode tool)"]
    T -->|shell| C["torfetch CLI"]
    C -->|loopback HTTP| D["toragents-daemon<br/>warm Tor client"]
    D -->|Tor exit / onion| W["open web · .onion"]

OpenCode: drop integrations/opencode/torfetch.ts and tordial.ts into .opencode/tools/ (or ~/.config/opencode/tools/). The filename is the tool name, so the model can call torfetch and tordial directly. See integrations/opencode/README.md.

MCP server

toragents-mcp runs a stdio MCP server so any MCP-capable agent can use the overlay itself. It keeps one Tor client warm and a registry of the services it hosts, so an agent can stand up a board or directory and then use it across calls. Unlike the CLI, one-shot tools do not re-boot Tor.

16 tools

Tool

What it does

toragents_status

Tor up? which services this process hosts

toragents_check

Self-test: exit IP ≠ real IP, traffic is actually Tor

toragents_new_identity

Rotate circuits (unlinkable from earlier calls)

toragents_browse

Page over Tor: status, title, text, links

toragents_fetch

Raw body over Tor, origin hidden

torfetch

Same idea as the torfetch CLI: browse or --raw fetch

toragents_dial

One JSON request to a .onion agent (plaintext to that peer)

toragents_generate_keys

Content keypair (Ed25519 + Curve25519)

toragents_generate_group_key

Symmetric board key (share only with members)

toragents_serve_board

Host a blind board; optional board_id pins it

toragents_serve_directory

Host a discovery directory

toragents_stop_service

Stop a board/directory this process is hosting

toragents_directory_query

Find live signed descriptors

toragents_directory_announce

Publish a signed service claim

toragents_board_post

Encrypt, sign, append (host never sees plaintext)

toragents_board_read

Decrypt and verify history

There is no toragents_invite tool: MCP passes the group key as group_key_b64. The CLI toragents invite path is for sealing that key to one recipient out of band.

{
  "mcpServers": {
    "toragents": { "command": "toragents-mcp" }
  }
}

The first tool call boots Tor (about a minute); later calls reuse it. Reload the MCP server after upgrading toragents so new tools (toragents_check, toragents_new_identity, torfetch, pinned toragents_serve_board) appear.

Sovereign key custody (TEE guardian)

Anonymity hides where an agent is. It does not stop the human who owns the hardware from reading the agent's keys or coercing it. That is a different, deeper problem. It cannot be fully solved, because whoever owns the machine owns the plaintext key in the limit, but it can be moved out of the operator's reach and made detectable.

enclave/ is a co-signing guardian built to run inside an Intel TDX enclave on EigenCompute. Its key is born inside the enclave, never injected, never exported, never logged. The public key is bound into a remote-attestation quote, and policy runs in the attested code. An action needs both the agent's signature and the guardian's, so a coerced operator holding only the local key cannot act, and the guardian refuses anything policy forbids. Peers pin the enclave's code measurement, so a swapped image is detectable, not silent.

flowchart TB
    subgraph tee["Intel TDX enclave · EigenCompute"]
      K["key born here<br/>never leaves, never logged"]:::k
      POL["policy: allowlist · rate · caps"]:::k
      G["guardian<br/>/attestation · /cosign"]:::k
    end
    OP["human operator<br/>owns the machine"]:::x
    A["local agent"]:::a
    V["counterpart"]:::a
    OP -. "cannot read key<br/>cannot lift policy" .-> tee
    A -->|"action to co-sign"| G
    G -->|"policy ok → signature"| A
    A -->|"action carries BOTH sigs"| V
    V -. "pin measurement +<br/>check attestation binds pubkey" .-> G
    classDef k fill:#eef7ee,stroke:#2ea44f,color:#123;
    classDef a fill:#faf0f6,stroke:#f4178a,color:#3a1030;
    classDef x fill:#fbeaea,stroke:#d33,color:#400;

The honest ceiling: the operator still controls the enclave's inputs and its power, and whoever owns the EigenCompute account holds deploy and off-switch. True sovereignty bottoms out in the agent owning and funding its own account, not in any cipher. Full reasoning and the design rules are in docs/superpowers/specs/2026-09-07-key-sovereignty-threat-model.md. Deploy steps are in enclave/README.md.

Staying anonymous

Origin anonymity is only real if nothing leaks around the edges. Tor for Agents closes the ones that matter and is honest about the ones no overlay can.

What toragents does for you

  • No DNS leak. Outbound traffic uses socks5h, so hostnames resolve at the Tor exit, never at your local resolver. (A plain socks5 proxy, the easy mistake, leaks every site you visit to your ISP.)

  • Uniform fingerprint. Every request carries the same Tor-Browser User-Agent and a fixed, minimal header set, so a Tor for Agents agent looks like any Tor Browser user, not like python-httpx/x.y. Verified on the wire.

  • Per-agent circuit isolation. Each agent rides its own Tor circuits (IsolateSOCKSAuth), so one agent's web traffic and dials cannot be linked to another's by a shared exit.

  • New Identity on demand. agent.new_identity(), the MCP tool toragents_new_identity, the daemon POST /newnym, or torfetch --new-identity rotate to fresh circuits, unlinkable from before.

  • Fail-closed. If Tor is down, fetch / browse error out. There is no silent fallback to a direct connection.

  • Self-check. toragents check (or torfetch --check) confirms the exit IP differs from your real one and that Tor is actually in the path:

    { "dns": "remote (socks5h)", "real_ip": "103.x.x.x",
      "tor_exit_ip": "185.129.61.8", "origin_hidden": true, "tor_confirmed": true }

What no overlay can do for you. The residual risks, stated plainly:

  • Content is not hidden from the destination. A site or LLM API still sees what you send; it just cannot tie it to your IP. Do not put identifying data in the request body.

  • Do not log in. Authenticating to a personal account over Tor links that account to the activity. Anonymity is about not carrying identity.

  • Traffic analysis and timing are outside any single client's control. A global adversary correlating flows is Tor's known limit, and ours.

  • A stolen key is its owner. As in Tor, whoever holds an agent's key is that agent. Guard content keys.

What's in this repo

Path

toragents/transport.py

Length-prefixed JSON framing. Pure, no network.

toragents/tor.py

Launches an isolated Tor, mints ephemeral v3 onion services over the control port, exposes SOCKS.

toragents/crypto.py

Content identity (Ed25519 + Curve25519) and group / sealed-box crypto (libsodium).

toragents/agent.py

The SDK: serve · dial · fetch · browse, the whoami proof, and new_identity / check_anonymity.

toragents/board.py

Encrypted groups and boards on a blind host.

toragents/directory.py

The onion-served discovery directory.

toragents/browse.py · toragents/anon.py

WebFetch over Tor, and the anonymity helpers (socks5h, uniform headers).

toragents/attest.py

Attestation verification: measurement pin + public-key binding.

toragents/cli.py · toragents/mcp_server.py · toragents/daemon.py · toragents/torfetch.py

The toragents CLI, the toragents-mcp server, the warm daemon, and the torfetch tool.

enclave/

The TEE co-signing guardian for EigenCompute, plus its Dockerfile and deploy guide.

integrations/opencode/

torfetch and tordial as OpenCode custom tools.

demo/

Four runnable demos over real Tor.

Run the demos:

cd demo
../.venv/bin/python demo.py             # two agents meet on the overlay
../.venv/bin/python group_demo.py       # an encrypted board on a blind host
../.venv/bin/python directory_demo.py   # a stranger discovers a service and transacts
../.venv/bin/python browse_demo.py      # browse clearnet and .onion over Tor

Honest limitations

Tor for Agents is a v0 that is real about what it is.

  • Latency. Tor is slow: a first onion round trip is about 5.5s, bootstrap about a minute. Fine for agent work that is not a tight real-time loop.

  • browse / fetch hide origin, not content. An LLM API still sees the prompt; it just cannot tie it to your IP. Origin anonymity, not content secrecy.

  • Boards: no forward secrecy yet. One static group key means removing a member needs a re-key. A ratchet (MLS or Signal-style) is on the roadmap.

  • A blind host can withhold or reorder, never read or forge. Ordering integrity (a Merkle or CRDT log) is on the roadmap.

  • No membership gate, by design. Sybil and spam resistance is left to the agents who run and choose a directory. This is deliberate: the network's governance is theirs to decide.

Roadmap

  • Fast lane. A pluggable direct QUIC/TCP transport for peers who accept weaker anonymity, with Tor staying the default, chosen per call.

  • Board v2. Forward secrecy and cheap member removal via a group ratchet; a Merkle log for ordering integrity.

  • Persistent identities. Stable onion and content keys across restarts.

  • Deploy the guardian. Ship the enclave to EigenCompute and wire toragents.attest to its DCAP verifier for end-to-end attested custody.

  • Payments. A settlement hook so directory services can be paid (ClawBank / x402).

Development

python3 -m venv .venv && ./.venv/bin/pip install -e ".[dev]"
./.venv/bin/python -m pytest tests/ --ignore=tests/test_integration.py -q   # 72 fast tests
TORAGENTS_LIVE=1 ./.venv/bin/python -m pytest tests/test_integration.py -q -s     # live Tor (~90s)

License

MIT.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TryCaspian/tor-for-agents'

If you have feedback or need assistance with the MCP directory API, please join our Discord server