Skip to main content

Drop-in client + zero-config server for Tor hidden services

Project description

🧅 tornion

Tor hidden service toolkit for Python — drop-in client + zero-config server.

PyPI version Python versions License: MIT

Consume any .onion API, or publish your own — in 3 lines of Python.


from tornion import client, server

# Consume a .onion API
r = client.get("http://xxxxxxxxxxxxxx.onion/ping")

# Publish your own
from fastapi import FastAPI
app = FastAPI()
server.serve(app)

That's it. No Docker, no torrc, no manual tor install — tornion handles everything: discovers or downloads the tor binary, spawns it, manages the SOCKS proxy or the hidden service, and tears it down on exit.

✨ Features

  • 🔌 Drop-in requestsclient.Session is a real requests.Session subclass
  • Async tooclient.AsyncSession is a real httpx.AsyncClient subclass (opt-in)
  • 🚀 Zero-config serverserver.serve(app) takes any ASGI or WSGI app
  • 🧠 Smart tor reuse — auto-detects an already-running tor on :9050/:9150
  • 📦 Auto-install tor — downloads the official Tor Expert Bundle on first use
  • 🔄 Secure auto-update — opt-in: always run the latest tor, PGP-verified against the Tor Project's signing key
  • 🎯 Framework-agnostic — FastAPI, Flask, Starlette, Django, Quart, Litestar…
  • 🔑 Persistent .onion — the address stays stable across restarts
  • 🪶 Lightweight client — server features are opt-in via pip install tornion[server]

📦 Installation

# Client only
pip install tornion

# With the async client (httpx-based)
pip install tornion[async]

# With server features
pip install tornion[server]

# With secure tor auto-update (PGP-verified latest)
pip install tornion[autoupdate]

🚀 Quick start

Client — call a .onion API

from tornion import client

r = client.get("http://xxx.onion/ping")
print(r.json())

# Reusable session for multiple calls
with client.Session() as s:
    s.post("http://xxx.onion/items", json={"name": "foo"})
    s.get("http://xxx.onion/items/1")

📖 Full client guide →

Server — publish your app on a .onion

from fastapi import FastAPI
from tornion import server

app = FastAPI()

@app.get("/")
def root():
    return {"hello": "from .onion"}

server.serve(app)

When you run this, tornion prints the .onion URL and blocks until Ctrl+C. Same code works with Flask, Starlette, Django, etc. — auto-detected.

📖 Full server guide →

Hybrid — server that also makes outbound calls

from fastapi import FastAPI
from tornion import client, server

app = FastAPI()

@app.get("/relay")
def relay(target: str):
    r = client.get(target, timeout=30)
    return {"upstream": r.status_code, "body": r.json()}

server.serve(app)

🛠️ CLI

tornion install-tor              # pre-download the tor binary
tornion update                   # update tor to the latest PGP-verified version
tornion serve myapp:app          # uvicorn-style: run an app on a .onion
tornion get http://xxx.onion/    # one-shot HTTP request
tornion info                     # diagnostic: where's tor, what's installed

📖 Configuration & CLI reference →

🔍 How does it actually work?

tornion orchestrates a real tor binary as a subprocess. Python doesn't implement Tor — it uses the C reference implementation (tor) under the hood, talks to it via SOCKS5 (client side) or the hidden-service API (server side), and shuts it down on exit.

📖 Architecture & internals →

📚 Documentation

Topic Doc
Calling .onion APIs from Python docs/client.md
Publishing your app on a hidden service docs/server.md
Env vars, CLI, paths, diagnostic docs/configuration.md
Architecture, design choices, pitfalls docs/internals.md
Examples (client / server / hybrid) examples/
Release history & versioning policy CHANGELOG.md

🧪 Try it

git clone https://github.com/LouisCourrian/tornion
cd tornion
pip install -e ".[dev]"
pytest
python examples/server_fastapi.py

✅ Status

tornion is stable as of 1.0.0. The public API of tornion, tornion.client, and tornion.server follows Semantic Versioning; see CHANGELOG.md for the full versioning policy and release history. Pin to a major version (tornion>=1.0,<2.0) and you're good.

📄 License

MIT — see LICENSE.

tornion is an independent project, not affiliated with the Tor Project. The bundled tor binary comes from torproject.org under 3-clause BSD. The package embeds the public Tor Browser build signing key (tornion/assets/tor-signing-key.asc, fingerprint EF6E286D…93298290) solely to verify auto-update downloads.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tornion-1.4.0.tar.gz (86.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tornion-1.4.0-py3-none-any.whl (54.0 kB view details)

Uploaded Python 3

File details

Details for the file tornion-1.4.0.tar.gz.

File metadata

  • Download URL: tornion-1.4.0.tar.gz
  • Upload date:
  • Size: 86.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tornion-1.4.0.tar.gz
Algorithm Hash digest
SHA256 319bd3faaa0ccf2d1bfd18cbfb7949d57d8f109a5e5624ec0a739932325b8009
MD5 cd6e31c2dc285202f7ca7c7305173bab
BLAKE2b-256 d786653f23e8c5b6a9cddd1b189b834910259b162029dc0fc3556c1094562066

See more details on using hashes here.

Provenance

The following attestation bundles were made for tornion-1.4.0.tar.gz:

Publisher: release.yml on LouisCourrian/tornion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tornion-1.4.0-py3-none-any.whl.

File metadata

  • Download URL: tornion-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 54.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tornion-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5f153614a5d991d44c2654396157734f1a58626571fda84df62ef8a42163fcb0
MD5 e520a3c9bc0781f2c841d61df9f4e506
BLAKE2b-256 8f01cc44d553b6e341f087a40676d460798df96aa6387f833311671788ed8d46

See more details on using hashes here.

Provenance

The following attestation bundles were made for tornion-1.4.0-py3-none-any.whl:

Publisher: release.yml on LouisCourrian/tornion

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page