Tor hidden services
Publishing CoinCync’s public infrastructure (block explorer, RPC API) as Tor hidden services is additive, never replacement. The clearnet sites stay live at explorer.coincync.network and api.coincync.network; an .onion mirror is published alongside them for users whose threat model demands end-to-end Tor.
This is the same model Pirate Chain, Monero, and Zcash follow. Their main websites are clearnet HTTPS; their .onion mirrors exist for the hardened threat model and are published in their docs and on their Twitter.
Why an .onion is meaningfully better than “use Tor Browser to visit our clearnet site”
Visiting https://explorer.coincync.network through Tor Browser still leaves a Tor exit node in the path — and many exit nodes are run by researchers, governments, or adversaries who log traffic. Even though the traffic is HTTPS-encrypted, the exit node sees:
- Which
.coincync.networkhost you’re talking to - The TLS SNI (which can leak the subdomain even on encrypted connections)
- Timing patterns
- The fact that someone using Tor is interested in CoinCync
An .onion is end-to-end within Tor. There is no exit node, only relays that see encrypted onion-layer traffic they cannot inspect. The destination is reached via a 6-hop circuit (3 client-side + 3 server-side, meeting in the middle) with no point of plaintext exposure.
For a privacy coin, this matters. The clearnet site is for users who don’t need that level of unlinkability. The .onion mirror is for users who do.
Setup on the explorer host
Roughly 20 minutes of work on the explorer host that already runs the Caddy stack.
1. Install Tor
sudo apt update
sudo apt install -y tor
Verify it started:
systemctl status tor
2. Configure a v3 hidden service
Append to /etc/tor/torrc:
# ── CoinCync explorer hidden service ─────────────────────
HiddenServiceDir /var/lib/tor/coincync_explorer/
HiddenServiceVersion 3
# Forward the .onion's port 80 to Caddy's HTTP bind on loopback.
# Tor clients speak HTTP (not HTTPS) over the onion because the Tor
# circuit already provides end-to-end encryption + auth — layering
# TLS on top is redundant and leaks cert metadata via the
# Server-Name-Indication field in the TLS handshake.
HiddenServicePort 80 127.0.0.1:80
Reload Tor:
sudo systemctl restart tor
Tor generates a v3 (56-character) onion hostname on first start. Read it:
sudo cat /var/lib/tor/coincync_explorer/hostname
# → <56-char-v3-onion>.onion
Save this hostname — you’ll need it for the next step and to publish.
3. Add a server block to the Caddyfile
The existing deploy/explorer/Caddyfile only listens on :443 (and gets :80 automatically for the ACME challenge). To also serve plaintext HTTP on 127.0.0.1:80 for Tor, append:
# ── .onion mirror ─────────────────────────────────────────
# Listens only on the loopback interface so only the local Tor
# daemon can reach it. The public 443 site is unaffected.
#
# `http://` is intentional — no TLS. Tor circuits are already
# end-to-end encrypted; layering TLS on top is redundant and
# leaks cert metadata via SNI in the TLS ClientHello.
#
# Caddy's auto-HTTPS is disabled for this site by binding to
# `http://` explicitly.
http://127.0.0.1:80 {
handle /api/testnet {
request_body { max_size 64KB }
reverse_proxy 127.0.0.1:28081 {
header_up Host {host}
header_up Content-Type "application/json"
}
}
handle /api/mainnet {
request_body { max_size 64KB }
reverse_proxy 127.0.0.1:19081 {
header_up Host {host}
header_up Content-Type "application/json"
}
}
handle /metrics {
reverse_proxy 127.0.0.1:9091
}
handle {
root * /var/www/explorer
encode zstd gzip
try_files {path} /index.html
file_server
}
log {
output stdout
format json
level INFO
}
}
Reload Caddy:
docker compose -f /opt/coincync/deploy/explorer/docker-compose.yml exec caddy \
caddy reload --config /etc/caddy/Caddyfile
4. Verify
From the same host:
# Caddy is now listening on 127.0.0.1:80
ss -tlnp | grep ':80 '
# expect: 127.0.0.1:80
# Curl through the local Tor SOCKS proxy
curl --socks5-hostname 127.0.0.1:9050 \
-I http://<your-56-char-v3-onion>.onion/
# expect: HTTP/1.1 200 OK
From a different machine running Tor Browser, visit http://<your-56-char-v3-onion>.onion/ directly. The explorer should load identically to the clearnet site.
5. Publish the onion address
Add it to:
https://docs.coincync.network/operations/tor.md(this page — the runbook itself doubles as the announcement)mirrors.jsonon the docs host (see Federation & DDoS)- The project’s social media / Twitter / Mastodon
https://coincync.networklanding page (in the “find us” section)
Once it’s published, users running Tor Browser can paste it directly. Tor Browser handles the routing; users don’t need to install Tor separately.
Same setup for the API host
The API stack at Toronto can publish its own .onion using exactly the same recipe — just point HiddenServiceDir at /var/lib/tor/coincync_api/, append a parallel http://127.0.0.1:80 block to deploy/api/Caddyfile that proxies /rpc and /v1/* to the local cyncd, and publish the resulting onion hostname.
The two .onion addresses are independent. They’re both on the Tor network but they have separate keys, separate hostnames, and separate failure modes.
Hidden service security checklist
- Hostname recorded somewhere offline. If the
HiddenServiceDirfiles are lost, the onion address is gone forever (you can’t recover it from the network — the address is the public key, derived from the private key in that directory). -
HiddenServiceDircontents backed up.hostname,hs_ed25519_public_key,hs_ed25519_secret_key. The secret key is what proves you control the onion. If it leaks, anyone with the key can impersonate your onion, including running a man-in-the-middle that serves modified explorer HTML. - Permissions on
HiddenServiceDirare 0700, owned bydebian-tor. Tor refuses to start otherwise. - Caddy on
127.0.0.1:80is bound only to loopback. Verify withss -tlnp | grep ':80 '. If it’s0.0.0.0:80, the explorer is also publicly reachable on plaintext HTTP, which defeats both the TLS-on-clearnet and the Tor-only-via-onion contracts. - No external CDN dependencies in the embedded HTML. The vendoring pass in
deploy/explorer/fetch-vendor.sh+patch-vendor.shis required for the.onionto actually work — a Tor user can’t reachcdn.jsdelivr.netfrom inside a.onionsession unless their Tor Browser is configured to allow exit-node requests for those origins (most aren’t, by default, and that defeats the point anyway).
What an .onion does NOT protect
- Application-layer fingerprinting. A wallet that uses a unique ring-size or transaction shape leaks its identity through the on-chain data even if the network path is anonymous.
- Out-of-band correlation. If a user mentions their balance in chat, in email, in a leak, the cryptography didn’t fail — they did.
- Tor itself. Tor has known limitations against well-funded adversaries who can observe entry and exit traffic. The hidden-service circuit is more robust than clearnet-Tor (no exit node), but it’s not magic.
See also
- Federation & DDoS — the broader threat model
- Block explorer — the clearnet stack the
.onionmirrors - Public RPC API — the parallel API stack, also
.onion-able