Tor Hidden Service for Sovereign AI: When and How
A Tor hidden service in front of a sovereign-AI endpoint is the right answer for three specific reader populations and the wrong answer for everyone else. Get the population question right before you spend two days on the configuration.
Quick Take
- Right answer for: readers in censored networks (journalists in restrictive jurisdictions, researchers behind state firewalls), AI agents that should not reveal their origin to the LLM endpoint, and operators who want their MCP server reachable without a DNS dependency.
- Wrong answer for: everyone else. Tor adds 300-600 ms of round-trip overhead, complexity, and operational burden. The default web is sufficient for the default user.
- The setup pattern: Tor hidden service v3 (as of Tor 0.4.8.x, which is what Debian 12 ships) in front of Caddy in front of the actual service. The
.onionaddress coexists with the regular sovgrid.org address.- The configuration that works: systemd-managed
tor.servicewith aHiddenServiceDir, Caddy routing the.onionhost to the same backend as the regular host, audit-logging the hidden-service requests separately from the regular requests.- The honest cost: hundreds of milliseconds of added latency per request, occasional Tor-network instability events, no useful analytics on the hidden-service traffic.
Who actually needs this
Three populations. The rest is overkill.
Population 1: readers in censored networks. Journalists, researchers, and operators in jurisdictions where sovgrid.org might be blocked, throttled, or surveilled at the network layer. The Tor hidden service gives them a route that the censoring infrastructure cannot easily disrupt, because the connection never touches a publicly resolvable hostname. The population is small in absolute terms; the marginal cost of serving them is small in operational terms once the hidden service is configured.
Population 2: AI agents that should not reveal their origin to the LLM endpoint. Agents running in a privacy-sensitive context (research agents probing for bias in commercial models, customer-deployed agents whose origin would identify the customer’s deployment) benefit from connecting via Tor. This is because the Tor circuit masks the originating IP at the network layer, which means the MCP server log shows a Tor relay identifier rather than the agent’s residential or cloud IP. A niche use case, but a real one in 2026 as agent-based workflows become more sophisticated.
Population 3: operators who want DNS-independence for the public surface. A Tor hidden service is addressable by its .onion identifier without depending on the global DNS root. The .onion address is specifically a 56-character base32 hash derived from the service’s Ed25519 public key, which means the address is cryptographically bound to the key pair rather than to any registrar or DNS zone. If your threat model includes “the DNS authority above me changes its mind about my domain,” the hidden service is the always-reachable fallback. Sovgrid has a registered domain through normal channels; the hidden service is the redundancy.
Outside these three populations, the regular HTTPS-on-sovgrid.org surface is sufficient. Adding Tor to satisfy a generic “privacy posture” requirement is performative rather than functional. In practice, most sovereign-AI operators fall into the fourth population: people who read about Tor, decide it would be cool, spend a weekend on it, and then find that all their actual users connect via clearnet anyway.
The setup, end to end
The components: a systemd-managed tor.service, a Caddy reverse proxy in front of the actual workloads, and the existing sovgrid web/MCP services as the backend. This is the v3 hidden service path, which is why you need Tor 0.3.2 or later (Debian 12 Bookworm ships 0.4.8.10, so no version hunting needed).
Install Tor
sudo apt install tor
sudo systemctl enable tor.service
The default Tor installation is fine. The configuration that matters is the hidden-service section in /etc/tor/torrc. Open it and add:
HiddenServiceDir /var/lib/tor/sovgrid_hidden_service/
HiddenServicePort 80 127.0.0.1:8080
The HiddenServiceDir line tells Tor where to store the persistent identity keys for this service. The HiddenServicePort line maps port 80 on the .onion address to 127.0.0.1:8080 on the local host, which is where Caddy will listen for hidden-service traffic.
sudo systemctl restart tor generates the hidden-service keys on first start. The .onion address appears in /var/lib/tor/sovgrid_hidden_service/hostname. The file contains a single line: the 56-character .onion address. Back this file up immediately, because regenerating it means distributing a new address to all existing users.
Configure Caddy to handle both surfaces
Caddy listens on 127.0.0.1:8080 for the hidden-service traffic and on the regular :443 for normal HTTPS. The Caddyfile entry:
sovgrid.example.onion:80 {
bind 127.0.0.1
reverse_proxy 127.0.0.1:3000
log {
output file /var/log/caddy/tor-access.log
format json
}
}
sovgrid.org:443 {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
reverse_proxy 127.0.0.1:3000
}
Both entries route to the same backend (the Astro static site or the MCP server, both on :3000). The hidden-service entry binds to 127.0.0.1 only, so it is not reachable from outside the local host except via the Tor routing. This binding is the critical security property: without it, any process on a multi-tenant host could reach the Caddy port directly, bypassing the Tor layer entirely.
The separate log file for Tor traffic lets you analyze the hidden-service usage independently from the regular traffic.
Test the hidden service
From a Tor-enabled browser (Tor Browser, or curl with SOCKS proxy):
curl --socks5 127.0.0.1:9050 http://yourhiddenserviceaddress.onion/
The response should be the same as the regular site. Note that this first request will take several seconds rather than the 50-100 ms you expect from HTTPS, because Tor needs to build a 3-hop circuit from the client to a rendezvous point. Subsequent requests in the same session will be faster (typically 300-600 ms per round trip compared to under 100 ms on clearnet), but never as fast as a direct connection. If you see no response at all rather than a slow one, check that Caddy is listening on the correct port and that the hidden-service forward target is correct.
What the hidden service does and does not give you
Provides: network-layer anonymity for the visitor. The visitor’s IP is not visible to your server; only the Tor circuit identifier is visible. Censorship resistance for jurisdictions that block sovgrid.org but allow Tor. No exit node in the picture for hidden services, which is why this differs from VPN+clearnet: with a VPN the exit sees your traffic, while with a .onion address the 3-hop circuit terminates at the service’s own introduction point, not at a third-party relay.
Does not provide: content-layer anonymity. If your site has tracking pixels, analytics scripts, or any embedded resource that calls home to a non-Tor URL, the visitor’s traffic to those resources is not Tor-protected. This is a caveat that catches most Tor-naive implementations: the site appears to work, but a single third-party font load over clearnet deanonymizes the visitor. Make the static site stay static, or audit every external reference before calling the hidden service “private.”
Does not provide: identity verification for the operator. The .onion address is a hash of the operator’s public key, not a certificate issued by a trusted authority. Visitors must rely on the operator’s distribution of the address (Nostr post, mailing list, sovgrid.org footer) to know they are reaching the right hidden service. This is specifically why you should sign the .onion address with your Nostr key when distributing it.
Does not belong on a .onion: operations that require real-time performance. Interactive autocomplete, WebSocket-heavy interfaces, streaming LLM responses where 50 ms latency matters: these will feel broken over Tor. The 300-600 ms per-request overhead is fine for static content and infrequent API calls; it is not acceptable for anything a user interacts with in a tight loop. For the MCP server use case, batch tool calls are fine; streaming tokens are not.
Provides only modestly: server-side anonymity for the operator. The Tor network knows that some hidden service exists at the address; the operator’s IP is not directly exposed to visitors, but the operator’s residential ISP can detect that Tor is running. This is fine for most threat models and matters in some.
The MCP hidden service variant
A particularly useful application: the MCP server (org.sovgrid/self-hosted-ai) reachable via a hidden-service address. Agents connecting via Tor to the MCP get the same tools as agents connecting via HTTPS, because the backend is identical; the only difference is the network path to reach it.
The configuration is the same as above but binds a different port:
HiddenServicePort 80 127.0.0.1:8888
Where :8888 is the MCP server’s local port.
The dual-address pattern (HTTPS on mcp.sovgrid.org and Tor on the hidden-service address) gives agents a choice. Most agents will use HTTPS for the lower latency. Privacy-sensitive deployments use Tor. In practice, the Tor variant makes sense for batch tool use (fetch a document, run a query, return results) rather than streaming or interactive sessions, because the latency overhead compounds with every round trip. An agent making 10 sequential MCP calls over Tor is looking at 3-6 seconds of added wait time compared to clearnet, which means the Tor path should be reserved for cases where the anonymity justifies that cost.
The cost
Latency. A Tor circuit adds 300-600 ms of round-trip overhead per request. This is because the 3-hop path means each packet crosses at least 3 relay nodes before reaching the destination. For a static-site visit, this is acceptable. For an interactive agent making frequent calls, the latency adds up: 10 sequential calls at 400 ms overhead each is 4 extra seconds, which turns a snappy 2-second workflow into a 6-second one. The hidden-service surface should be considered high-latency by design, not by accident.
Operational. The Tor daemon needs to be running. The hidden-service keys in /var/lib/tor/sovgrid_hidden_service/ need to be backed up; if you lose them, the .onion address is gone permanently and every user who bookmarked it hits a dead end. The .onion address needs to be communicated to the audience. None of this is hard; it is just non-zero work that most operators underestimate before they start.
Analytics. The hidden-service traffic is intentionally anonymized. The operator cannot count unique visitors, cannot geolocate them, and cannot integrate with standard analytics pipelines. For sovgrid this is a feature (no visitor data to leak). For operators who need to prove audience size to sponsors or partners, the Tor surface is a black box and therefore not a substitute for the clearnet surface.
Bot traffic. The Tor network attracts a higher fraction of automated traffic than the regular web. The hidden-service log will show volume from scanners and crawlers that specifically probe .onion addresses. Filter it during analysis, and avoid using raw hidden-service request counts as a proxy for real human interest.
Why .onion instead of VPN + clearnet
The obvious alternative is to put the service behind a VPN and hand out VPN credentials to trusted users. This works, but it solves a different problem. A VPN protects the transit between the user and the VPN endpoint; it does not protect the transit between the VPN exit and your server, and it does not protect from the VPN provider itself. With a hidden service, the Tor protocol protects both legs: client to introduction point, and introduction point to service. There is no single entity in the middle who sees both the client’s IP and the destination service.
The concrete difference in practice: a VPN operator in a censored jurisdiction can be compelled to hand over connection logs. A Tor hidden service has no single operator who holds both ends of a connection log. That is why the hidden service is the right choice specifically for the “censored network” population rather than a VPN, and why VPN is the right choice for the “restricted corporate network” population rather than Tor.
For most sovereign-AI operators, the comparison goes like this: VPN means you manage credentials and revocation for every authorized user; Tor means the .onion address is public but the server IP stays private. Both have a role; they serve different threat models. Choosing one is not rejecting the other.
Where this fits
For the broader sovereignty-test framework, see What Sovereign Actually Means in 2026. For the reference architecture, see The Sovereign AI Stack in 2026. For the regular-web ingress pattern that coexists with the hidden service, see Caddy + Cloudflare Tunnel: The Reliability Pattern.
Follow for the operator-facing Tor playbook
A future article walks through the operator-facing Tor configuration (the Docker daemon’s daemon.json Tor proxy that is part of the sovgrid security baseline), which is separate from the public hidden-service surface this article covers. Follow via Nostr or the RSS feed (links in footer).