Anonymity Integration Guide

Technology: anonymity · Category: tooling · Last reviewed: 2026-08-03

Source: https://tech-stack.codeamanilabs.org/guide/anonymity

Insight:

Anonymity is a property of a system under a stated adversary, never a product you install. The engineering job is subtractive: every identifier you never collect, every log line you never write, and every third-party script you never ship is anonymity you don't have to defend later. This guide is the audited, 2026-current replacement for a decade of stale "install Tor and you're invisible" folklore — including the specific tool-by-tool corrections.

 █████╗ ███╗   ██╗ ██████╗ ███╗   ██╗██╗   ██╗███╗   ███╗██╗████████╗██╗   ██╗
██╔══██╗████╗  ██║██╔═══██╗████╗  ██║╚██╗ ██╔╝████╗ ████║██║╚══██╔══╝╚██╗ ██╔╝
███████║██╔██╗ ██║██║   ██║██╔██╗ ██║ ╚████╔╝ ██╔████╔██║██║   ██║    ╚████╔╝ 
██╔══██║██║╚██╗██║██║   ██║██║╚██╗██║  ╚██╔╝  ██║╚██╔╝██║██║   ██║     ╚██╔╝  
██║  ██║██║ ╚████║╚██████╔╝██║ ╚████║   ██║   ██║ ╚═╝ ██║██║   ██║      ██║   
╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚═╝  ╚═══╝   ╚═╝   ╚═╝     ╚═╝╚═╝   ╚═╝      ╚═╝   

Anonymity Integration Guide

Focus: Building systems that don't deanonymize the people who use them — threat modelling, metadata minimization, Tor/onion-service integration, and anonymous intake — plus a tool-by-tool audit correcting a decade of stale advice.

Overview

Anonymity is not a tool. It is a property of a system, measured against a named adversary. "Am I anonymous?" is an unanswerable question. "Can a passive observer of my ISP link this request to my legal identity?" is answerable, testable, and engineerable.

Three distinct properties get conflated constantly, and conflating them is the single most common failure in this space:

Property Question it answers Broken by
Confidentiality Can they read the content? Weak/absent E2EE, backdoored endpoints
Privacy Can they link content to behaviour? Logging, tracking, data brokers
Anonymity Can they link behaviour to an identity? Metadata, correlation, one careless reuse

Encryption gives you the first. It gives you neither of the other two. A perfectly encrypted message that arrives from your home IP, at a predictable hour, to a recipient only you contact, is not anonymous — and this is precisely how most real deanonymizations happen.

For codeAmani the practical surface is defensive: we build products used by riders, clinic patients, SACCO members, donors, and whistleblowers. Our job is to avoid collecting the identifiers that would deanonymize them, not to help anyone evade lawful process.

flowchart TD
  A["Who is the adversary?"] --> B{"Capability"}
  B -->|"Curious insider / scraper"| C["Data minimization<br/>access control · retention limits"]
  B -->|"Commercial surveillance<br/>adtech · data brokers"| D["No 3rd-party scripts<br/>no SDK telemetry · IP truncation"]
  B -->|"Network observer<br/>ISP · café wifi"| E["TLS + ECH · Tor<br/>pluggable transports"]
  B -->|"Nation-state<br/>global passive adversary"| F["Compartmentalized OS<br/>Qubes-Whonix · Tails · burner hardware"]
  C --> G["Anonymity set:<br/>how many people<br/>could this have been?"]
  D --> G
  E --> G
  F --> G

The last box is the one that matters. Anonymity is a crowd property. You are only as anonymous as the number of people who look identical to you. Every "optimization" that makes you unusual — a rare font set, a custom user-agent, a niche browser extension — shrinks your crowd and hurts you. This inverts most people's intuition and is the reason the audit below rejects so much traditional "hardening" advice.

Official Documentation

Resource URL
Onion services (operator docs) https://community.torproject.org/onion-services/
Tor protocol specifications https://spec.torproject.org/
Tor Browser manual https://tb-manual.torproject.org/
Whonix documentation https://www.whonix.org/wiki/Documentation
SecureDrop (whistleblower intake) https://securedrop.org/
Arti (Rust Tor) API docs https://docs.rs/arti-client
Privacy Guides (tool selection) https://www.privacyguides.org/

The 2015 → 2026 audit

This guide was commissioned as an audit of Tor and the Dark Art of Anonymity (Lance Henderson, 2015 edition). The book's principles aged well; its tooling did not, and a meaningful fraction of its advice is now actively harmful.

Full chapter-by-chapter mapping lives in reference/book-audit-2015-to-2026.md. Headlines:

Actively harmful if followed today

Book says Reality in 2026
Install CanvasBlocker, User-Agent Switcher, Ghostery, Adblock Never add extensions to Tor Browser. Each one raises fingerprint entropy and shrinks your anonymity set. Tor Browser ships letterboxing and a uniform fingerprint by design — customizing it is self-defeating.
Use Chrome with ScriptNo/ScriptSafe/FlashControl Chrome is not an anonymity browser and never was. Flash reached EOL Dec 2020. Use Tor Browser, or Mullvad Browser (Tor Browser's fingerprint without the Tor network) for VPN use.
Manually configure NoScript per-site Per-site whitelists are themselves a fingerprint. Use the built-in Security Level slider (Standard / Safer / Safest) and nothing else.
Pay for a VPN anonymously, chain it with Tor VPN+Tor generally does not improve anonymity and often harms it. Use Tor's bridges + pluggable transports for censorship, not a VPN hop.
Use Bitcoin mixers (BitFog et al.) for anonymity Bitcoin is pseudonymous, not anonymous; chain analysis is a mature industry. Mixing now carries severe legal exposure — Samourai Wallet's founders pleaded guilty and were sentenced to 5 and 4 years in late 2025, even as OFAC delisted Tornado Cash in March 2025.
Generate keys at Brainwallet.org Brainwallets are catastrophically insecure and were mass-drained. Never generate key material in a web page.

Dead, renamed, or superseded

Book (2015) Status Modern equivalent
Torbutton, HTTPS Everywhere Merged into Tor Browser core; HTTPS Everywhere retired Jan 2023 Built-in Security Level + HTTPS-Only Mode
v2 .onion addresses (16 char) Removed from Tor, Oct 2021 v3 onions — 56 char, ed25519/SHA3
Shallot / Scallion (v2 vanity) Dead with v2 mkp224o (v3 ed25519 vanity)
TextSecure + RedPhone Merged into Signal (2015) Signal — PQXDH (2023) + SPQR "Triple Ratchet" (Oct 2025)
CryptoCat, Torchat, ChatSecure Discontinued / unmaintained SimpleX, Briar, Cwtch (metadata-resistant)
Tor Instant Messaging Bundle Cancelled — never shipped as above
TrueCrypt Discontinued 2014 VeraCrypt, LUKS2, BitLocker, FileVault
TorBirdy, Enigmail Discontinued 2020 Thunderbird built-in OpenPGP
Freenet + Frost + Fuqid Renamed Hyphanet (2023); front-ends abandoned Hyphanet, I2P, Nym mixnet
MultiBit, MultiSigna, most listed exchanges Defunct —
Darkcoin Renamed Dash (2015); PrivateSend is opt-in CoinJoin, not anonymity Monero if privacy is the actual requirement
Panopticlick Renamed EFF Cover Your Tracks
Skype Retired May 2025 Signal
Macchanger as a manual step MAC randomization is now default in iOS 14+, Android 10+, Windows 10+, NetworkManager (nothing to do)

Missing entirely from the book — and now central

Qubes OS (compartmentalization by virtualization; Qubes-Whonix is the strongest widely-available desktop posture) · GrapheneOS (hardened Android) · Snowflake / WebTunnel pluggable transports · Arti (Rust Tor, 2.0.0, embeddable as a library) · Post-quantum crypto (NIST FIPS 203/204/205, Aug 2024) · Commercial spyware (Pegasus, Predator, Graphite) as the realistic high-end threat, answered by iOS Lockdown Mode / Android Advanced Protection · Data brokers and SDK-based location resale, which deanonymize at scale far more cheaply than any SIGINT program · SecureDrop / Hush Line for anonymous intake.

Also notable: Tails merged into the Tor Project in September 2024 — they are now one organization.

Deliberately not modernized

The book's chapters on darknet-market escrow, "finalize early" tactics, evading law enforcement, and running hidden marketplaces are out of scope and intentionally left un-updated. They are operational crime guidance, not privacy engineering, and much of the surrounding ecosystem they describe (Silk Road 2.0, Agora, Blackbank, Sheep) was seized or exit-scammed within a year of publication. This guide covers the defensive and civil-liberties surface only: protecting users, journalists, and at-risk people from surveillance.

What the book got right, and still is

Worth preserving explicitly, because it's the durable part:


Setup

Running a local Tor client

Everything below assumes a Tor SOCKS5 proxy on 127.0.0.1:9050 (daemon) or 9150 (Tor Browser).

# macOS
brew install tor && brew services start tor

# Debian / Ubuntu
sudo apt install tor && sudo systemctl enable --now tor

# Verify: should report Congratulations
curl -s --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip

--socks5-hostname (not --socks5) is load-bearing: it sends DNS resolution through the proxy. Plain --socks5 resolves DNS locally and leaks every hostname you visit to your resolver — a total anonymity failure that still looks like it's working.

Node — routing requests through Tor

npm install socks-proxy-agent

socks-proxy-agent implements Node's http.Agent, so it works with anything built on http/https — axios, node-fetch, got:

import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

// socks5h:// = resolve DNS at the proxy. socks5:// leaks DNS locally.
const agent = new SocksProxyAgent("socks5h://127.0.0.1:9050");

const { data } = await axios.get("https://check.torproject.org/api/ip", {
  httpAgent: agent,
  httpsAgent: agent,
});
console.log(data); // { IsTor: true, IP: "..." }

Node's native fetch silently ignores agent. Native fetch is undici, which only honours a dispatcher. Passing { agent } to it does not error — it just sends the request over your real IP. If you must use native fetch, build an undici Agent with a SOCKS connect function; otherwise stay on axios/node-fetch for proxied calls.

Fail closed, so a misconfiguration can never fall back to a direct connection:

export async function assertTor(agent: SocksProxyAgent): Promise<void> {
  const { data } = await axios.get("https://check.torproject.org/api/ip", {
    httpAgent: agent, httpsAgent: agent, timeout: 15_000,
  });
  if (!data.IsTor) throw new Error("Refusing to proceed: traffic is not over Tor");
}

Python — controller access with Stem

stem is the Tor Project's own controller library — use it to build circuits, rotate identity, and publish onion services programmatically.

pip install stem pysocks requests[socks]
import requests
from stem import Signal
from stem.control import Controller

# socks5h:// -> DNS resolved by Tor, not locally
PROXIES = {"http": "socks5h://127.0.0.1:9050",
           "https": "socks5h://127.0.0.1:9050"}

print(requests.get("https://check.torproject.org/api/ip", proxies=PROXIES).json())

# Request a fresh circuit (rate-limited by Tor to roughly one per 10s)
with Controller.from_port(port=9051) as c:
    c.authenticate()               # cookie auth by default
    c.signal(Signal.NEWNYM)

NEWNYM gives you a new circuit, not a new identity. Cookies, local storage, a logged-in session, and browser fingerprint all survive it. Treat it as changing your exit IP and nothing more.

Publishing a v3 onion service

Onion services give both parties anonymity and provide authenticated, end-to-end encrypted transport with no CA involved — the .onion address is the public key.

# /etc/tor/torrc
HiddenServiceDir /var/lib/tor/my_service/
HiddenServicePort 80 127.0.0.1:8080

# Enable the proof-of-work DoS defense (Tor 0.4.8+, 2023)
HiddenServicePoWDefensesEnabled 1
sudo systemctl reload tor
sudo cat /var/lib/tor/my_service/hostname   # -> <56-char>.onion

The directory now holds hs_ed25519_secret_key, hs_ed25519_public_key, and hostname. hs_ed25519_secret_key is the identity — anyone who copies it can impersonate the service permanently. Back it up encrypted; never commit it; mode 0600, owned by the tor user.

Restrict access to named clients (formerly "client authorization", now restricted discovery) by dropping public keys into authorized_clients/:

# /var/lib/tor/my_service/authorized_clients/alice.auth
descriptor:x25519:<BASE32_PUBKEY>

Bind the backend to loopback only (127.0.0.1:8080). An onion service whose origin is also reachable on a public IP is trivially correlated and defeats the entire construction — this is how several high-profile services were located.

Vanity addresses

Shallot and Scallion from the book only ever produced v2 addresses and are dead. The v3 tool is mkp224o:

git clone https://github.com/cathugger/mkp224o && cd mkp224o
./autogen.sh && ./configure && make
./mkp224o -d ./out amani     # addresses beginning "amani"

Difficulty is exponential in prefix length — 6 characters is quick, 8 is hours, and beyond that is a research budget. A vanity prefix is branding, not security: users must still verify the full 56-character address.


Key patterns

Don't blanket-block Tor exit nodes

The most common way a product harms at-risk users is invisible: a WAF rule or a fraud vendor silently blocking every Tor exit. That locks out journalists, abuse survivors, and people under censorship — the exact users who need you most.

// Tier by ACTION, not by network origin.
// Tor traffic is not fraud; it is traffic whose origin you cannot see.
export function riskTier(req: Request): "open" | "challenge" | "deny" {
  if (isReadOnly(req)) return "open";        // never block reading
  if (isAccountMutation(req)) return "challenge"; // proof-of-work / captcha
  return "deny";                              // only for known-abusive patterns
}

Rate-limit on a session or workload token, never on IP alone — IP-based limits punish everyone behind one exit node and are trivially evaded by anyone who matters.

Log hygiene — the leak that survives every other control

Most deanonymization risk in a normal SaaS lives in logs, not in the network.

/** Truncate IPs before they are ever written. IPv4 -> /24, IPv6 -> /48. */
export function coarseIp(ip: string): string {
  if (ip.includes(":")) return ip.split(":").slice(0, 3).join(":") + "::/48";
  return ip.split(".").slice(0, 3).join(".") + ".0/24";
}

const PII = /(\+?254\d{9})|([\w.+-]+@[\w-]+\.[\w.]+)|(\b[A-Z]{2}\d{6}\b)/g;
export const scrub = (s: string) => s.replace(PII, "[redacted]");

Apply it at the boundary — Sentry beforeSend, the logger transport, and analytics — so no code path can bypass it:

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  sendDefaultPii: false,
  beforeSend(event) {
    if (event.user) { delete event.user.ip_address; delete event.user.email; }
    return JSON.parse(scrub(JSON.stringify(event)));
  },
});

Timing and volume are identifiers

An "anonymous" report submitted at 14:03 from a clinic that has four staff is not anonymous. Where the anonymity set is small, add jitter and batching rather than delivering immediately:

// Release anonymous submissions on a fixed cadence so submission time
// carries no information about event time.
const BATCH_WINDOW_MS = 60 * 60 * 1000;
export const releaseAt = (t: number) =>
  Math.ceil(t / BATCH_WINDOW_MS) * BATCH_WINDOW_MS;

Strip file metadata on upload

Photos carry GPS coordinates, device serials, and timestamps. For any user-supplied image, re-encode server-side and drop all EXIF — never trust the client to have done it.

import sharp from "sharp";
// Re-encoding drops EXIF/GPS by default; `rotate()` first so orientation
// survives the metadata loss.
export const sanitize = (buf: Buffer) =>
  sharp(buf).rotate().toFormat("webp", { quality: 82 }).toBuffer();

Anonymous intake

For genuine whistleblower or abuse-report intake, do not roll your own. Use SecureDrop (onion-based, hardened, designed for newsrooms) or Hush Line for a lighter-weight tip line. A bespoke "anonymous form" on your main domain shares TLS fingerprints, CDN logs, and analytics with the rest of your product, and almost always leaks.


codeAmani notes

Security

African-market and Kenya-targeted projects

This is where the guide earns its keep, because our Kenya-targeted builds collect precisely the identifiers that deanonymize:

AI routing

Where this guide fits


Scope statement. This guide covers defensive privacy engineering, censorship circumvention, and protection of at-risk users — journalists, abuse survivors, whistleblowers, and people under repressive governments. It deliberately excludes operational guidance for evading lawful investigation, and does not modernize the source book's darknet-marketplace chapters.

Official docs: