The connector, by hand
Hidden services
Your node at a .anyone address with no domain and no open port, and a client that reaches it.
A connector is ordinarily a clearnet host with a domain and a TLS certificate. It can also be a hidden service: a node reachable only inside the Anyone Protocol overlay, at a .anyone address that public DNS cannot resolve and no certificate authority can certify. Anyone is a Tor-derived network; its daemon is called anon, and its addresses end in .anyone rather than .onion. A node at such an address needs no domain, no public IP and no port opened on your router, which is how a node can run from a laptop behind a home router.
This page has both halves: running a node as a hidden service, and paying a node that is one. Every spaceturtle node is a hidden service, and spaceturtle does the operator half for you; this page is for doing it by hand.
Running one
You need a running node from Run a node, with a settlement block so that it can be paid, and an anon daemon beside it. The daemon publishes a .anyone address and forwards connections arriving there to the node's client edge. The node never talks to the daemon; you copy the address into the node's config by hand.
- 01Run anonFour lines of config. Wait for
Bootstrapped 100%. - 02Read the address
cat hs/hostname. It is a 56-character key. Keep the directory. - 03Tell the nodeThree TOML keys, then restart. Wait two or three minutes, then check from outside.
1. Run the daemon
anon is published by Anyone Protocol at github.com/anyone-protocol/ator-protocol/releases; the client downloads the same anon-live-<platform>.zip. Give it a config file of its own:
AgreeToTerms 1 SocksPort 127.0.0.1:9050 HiddenServiceDir /var/lib/anon/hs HiddenServicePort 80 127.0.0.1:3000
AgreeToTerms 1or the daemon exits at once. It is the single most common reason a first start fails.HiddenServicePort 80 <ip>:<port>forwards the hidden service's port 80 to the node'sclient_edge_addr, the portGET /ilpanswers on. Virtual port 80 is what lets the address be written ashttp://<address>.anyone/ilpwith no port. Use an IP address, not a hostname: the target is resolved when the config is parsed, and an unresolvable name kills the daemon. In Docker Compose, that means the connector container's address on the compose network, not its service name.HiddenServiceDirmust be on a persisted disk or volume. The daemon writes the address and its private key there on first start. Lose the directory and you get a new address, and every counterparty that wrote the old one down dials a node that is gone.SocksPortis the daemon's default and the node needs it only to dial other hidden services. Leave it on.
Start it and wait for Bootstrapped 100% in its log, which takes about a minute. Then read the address:
cat /var/lib/anon/hs/hostname
6z666pmpzho3pxj3nxlh4q3imhbfjifanreo2kunc36rie4j3omsh3id.anyone
2. Tell the node
Three keys in connector.toml. peer_expose is a root-level key and must come before the first [table] header; without it the node's self-description keeps "peerCarriages": [] and the endpoint you publish is not dialable.
peer_expose = "http" # root level, above every table [node] addresses = ["g.your.node"] http_endpoint = "http://<56-char-address>.anyone/ilp"
The http:// scheme is allowed here without any opt-in, because a v3 address is the public key the circuit authenticates to, so an endpoint at that host authenticates itself. A host that merely contains the suffix, such as anyone.example, is refused as plaintext. Restart the node and confirm the local self-description shows the endpoint and "peerCarriages": ["http"]:
curl -s http://127.0.0.1:3000/ilp | jq '.httpEndpoint, .peerCarriages'
3. Check it from outside
The daemon says nothing when it has published the address, and the first minute or two after Bootstrapped 100% a dial usually times out. Wait two or three minutes, then ask through the daemon's SOCKS port:
curl --socks5-hostname 127.0.0.1:9050 http://<address>.anyone/ilp | jq .ilpAddresses npx toon-client describe http://<address>.anyone --socks socks5h://127.0.0.1:9050 --password-file pw
A 200 with your own self-description is the proof. From there a client anywhere on the overlay can open a channel toward your node (the gasless deposit takes about ten seconds through the overlay rather than three) and pay it, exactly as on the clearnet. In a test on 2026-10-13 a connector in Docker plus one anon daemon went from nothing to a paid 200 through the overlay in about twenty minutes.
If your node dials other hidden services
To peer with, or forward to, another .anyone node, give the connector the daemon's SOCKS port. The key is root-level, and the scheme must be socks5h (the h makes the daemon resolve the name; a plain socks5:// is refused by name, because nothing local can resolve a hidden-service address):
socks_proxy = "socks5h://127.0.0.1:9050"
There is nothing else to configure: which dials use the proxy is read off each endpoint's host. A host ending in .onion or .anyone goes through the proxy; everything else does not.
The connector repository's onion endpoint bring-up is the fleet-grade runbook behind this section, with the compose sidecar, the restart drills and the failure modes.
Paying one
Paying such a node needs one extra thing: a running anon daemon, and its SOCKS5h port.
you ──► anon (SOCKS5h) ──► the overlay ──► connector.anyone ──► app
▲
└── chain RPC goes here too, not around it
Nothing else changes. Sealing, pricing, vouchers, channels and refusals are identical; the packet does not know what carried it.
From the CLI
Nothing, if you let it: point toon-client at a .anyone address and it starts a daemon for you.
export TOON_CONNECTOR=http://<address>.anyone npx toon-client describe --password-file pw
toon: http://<address>.anyone is a hidden service; starting a local anon daemon toon: downloading anon-live-linux-amd64.zip (v0.4.10.2)… toon: verified; extracting toon: starting anon v0.4.10.2 on 127.0.0.1:41337 (…/anon) toon: anon is listening; building circuits on demand toon: proxying through socks5h://127.0.0.1:41337
Every line of that goes to stderr, never stdout, so --json output stays exactly one parseable document. The first dial through a fresh daemon takes about forty seconds while circuits are built. Already running your own daemon? Name it, and nothing is downloaded and no process is spawned:
npx toon-client send --socks socks5h://127.0.0.1:9050 --body 'hello'
export TOON_SOCKS=socks5h://127.0.0.1:9050 # same thing, for every command
A clearnet connector starts no daemon and downloads nothing. Note that in client 5.0.0 describe opens the keystore even though it signs nothing, so in a script pass --password-file.
The managed daemon
An operator is entitled to know what would run on their machine.
| Release | v0.4.10.2, pinned, on the stable live channel. |
| Platforms | linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64. |
| Verification | sha256 against a checksum recorded in the client's repository, checked before the binary is ever executed. A platform with no pinned checksum is refused, not downloaded and trusted. |
| Cache | $XDG_CACHE_HOME/toon/anon/<version>/, falling back to ~/.toon/anon/<version>/. Keyed by version, so a new pin never silently reuses an old binary. |
What a hidden service does and does not buy
A hidden-service endpoint hides where the node is reachable, and nothing else. The chain still sees the channel's deposit and settlement, and the voucher still names the paying key.
Written from connector/docs/operators/onion-endpoint-bringup.md, toon-client/docs/hidden-service.md, and a first-run test of 2026-10-13.