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.1.tar.gz (87.0 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.1-py3-none-any.whl (54.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tornion-1.4.1.tar.gz
  • Upload date:
  • Size: 87.0 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.1.tar.gz
Algorithm Hash digest
SHA256 17b02000faf308845bc6c933fe3299c2adbe35bdf4c4b2220849edb7a7bf9d06
MD5 6617d7e30a32a680bf8e0fb838ef3af8
BLAKE2b-256 def79d82b1b6f30f99594d3b4f56f0972e280256019f524dabf3c2de71e310a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for tornion-1.4.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: tornion-1.4.1-py3-none-any.whl
  • Upload date:
  • Size: 54.5 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f011a269ce964ffa71762714a8a4453006bc5f6817683034e98189e52c81af17
MD5 9fd4a420f8495257dbf7efd57f5d4dff
BLAKE2b-256 ae0da0ba5866a4f4f3044a99382f2df5a13cbb34546fbb5c2afe63eec8fd4122

See more details on using hashes here.

Provenance

The following attestation bundles were made for tornion-1.4.1-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