Self-host your .onion

The whole point of a Tor v3 hidden service is that you are anonymous as its operator, and visitors are anonymous reaching it, and the connection is end-to-end encrypted without a CA in the middle. Letting someone else host the .onion for you breaks all three. Here's how to not do that.

The 4 commands

Assumes you have your BIP-39 mnemonic (any Solana wallet's seed words work). Tested on macOS + Debian; Windows works in WSL.

step 1
Install Tor
# macOS
brew install tor

# Debian / Ubuntu
sudo apt install tor

# Arch
sudo pacman -S tor
step 2
Derive the hidden-service key files from your mnemonic

Use any ouija-aware tool — the math is in ouija-bridge/wallet/onion.go(Go), ouija-mcp/src/index.ts (TS), and the local ouija-relay daemon. Easiest path: run the relay locally and let it export the Tor key files.

# Run the local relay with your mnemonic on stdin
echo "your twelve or twenty four word mnemonic here" | ouija-relay

# In another terminal — bearer is printed to the relay's stderr on launch
BEARER=<paste from relay stderr>
mkdir -p ~/tor-hs/ouija
curl -s -H "Authorization: Bearer $BEARER" \
     http://127.0.0.1:18964/v1/tor-keys > ~/tor-hs/ouija/files.json

# Split into Tor's expected layout
jq -r .hs_ed25519_secret_key_b64 ~/tor-hs/ouija/files.json | base64 -D \
     > ~/tor-hs/ouija/hs_ed25519_secret_key
jq -r .hs_ed25519_public_key_b64 ~/tor-hs/ouija/files.json | base64 -D \
     > ~/tor-hs/ouija/hs_ed25519_public_key
jq -r .hostname ~/tor-hs/ouija/files.json > ~/tor-hs/ouija/hostname
chmod 700 ~/tor-hs/ouija
chmod 600 ~/tor-hs/ouija/*

⚠ Note: the /v1/tor-keys endpoint is in the v1 relay roadmap. Until it ships, derive the same files with any standalone ouija identity tool that supports the secret-key export, or build them yourself from the 32-byte seed (see TorSecretKeyFile in ouija-bridge/wallet/onion.go).

step 3
Point a Tor service at the directory
# Add to /etc/tor/torrc (or /opt/homebrew/etc/tor/torrc on macOS):
HiddenServiceDir /Users/you/tor-hs/ouija/
HiddenServicePort 80 127.0.0.1:8080

# Replace the path with where you put the keys.
# 8080 is wherever your local site / server is listening.
step 4
Restart Tor and serve
# macOS
brew services restart tor

# systemd
sudo systemctl restart tor

# Verify Tor picked up your key
cat ~/tor-hs/ouija/hostname
# → should match the .onion derived from your Solana pubkey

# Now serve content at 127.0.0.1:8080 — anything works
python3 -m http.server 8080 --bind 127.0.0.1

Visit your .onion in Tor Browser. You now have a real hidden service — no convenience operator in the middle, no clearnet mirror, no third party able to take it down or read the logs.

What "real Tor" gives you that convenience hosting doesn't

Once it's running, give it a memorable name

A 56-character .onion is a pain to share. Register yourname.stacc on AllDomains · .stacc with the owner field set to the wallet whose key you just used. Visitors with the ouija-onion-resolver extension type yourname.stacc in their URL bar and the extension intercepts pre-DNS, derives your .onion from the on-chain owner pubkey (same math you just used), and routes them to your hidden service. Full chain walkthrough.

Tradeoffs of self-hosting

Convenience hosting trades these for the opposite set of problems — mostly trust in us. Pick the one whose failure mode you can live with.