Documentation

Configuration

MOOR uses a config file (moorrc) for all options. Load with moor -f moorrc. Lines starting with # are comments. See moorrc.sample in the tarball for every option.

Client

SocksPort 9050
$ moor -f moorrc
$ curl -x socks5h://127.0.0.1:9050 https://check.torproject.org/api/ip

Guard Relay

Mode relay
BindAddress 0.0.0.0
ORPort 9001
AdvertiseAddress YOUR_PUBLIC_IP
Guard 1
Nickname MyGuard
DataDirectory /var/lib/moor
ContactInfo you@example.com
Verbose 1

Middle Relay

Mode relay
BindAddress 0.0.0.0
ORPort 9001
AdvertiseAddress YOUR_PUBLIC_IP
Nickname MyMiddle
DataDirectory /var/lib/moor
ContactInfo you@example.com
Verbose 1

Exit Relay

Mode relay
BindAddress 0.0.0.0
ORPort 9001
AdvertiseAddress YOUR_PUBLIC_IP
ExitRelay 1
Nickname MyExit
DataDirectory /var/lib/moor
ContactInfo you@example.com
Verbose 1

Hidden Service

Mode hs
HiddenServiceDir /var/lib/moor/hs
HiddenServicePort 8080
Verbose 1
$ python3 -m http.server 8080 &
$ moor -f moorrc
# Prints: Hidden service address: abc123...xyz.moor

Bridge Relay

Mode relay
BindAddress 0.0.0.0
ORPort 443
IsBridge 1
AdvertiseAddress YOUR_PUBLIC_IP
DataDirectory /var/lib/moor-bridge
ContactInfo you@example.com
Verbose 1

Client via Bridge

SocksPort 9050
UseBridges 1
Bridge shade 1.2.3.4:443 <64-char-hex-fingerprint>

Directory Authority

Mode da
BindAddress 0.0.0.0
DirPort 9030
DataDirectory /var/lib/moor-da
DAPeers 107.174.70.122:9030
ContactInfo you@example.com
Verbose 1

NAT / Firewall

BindAddress = local interface (always 0.0.0.0 behind NAT).
AdvertiseAddress = your public IP (what other relays connect to).
Port 9001/tcp must be open inbound. Do NOT set BindAddress to your public IP behind NAT.

Run

$ moor -f moorrc

All Config Options

See moorrc.sample in the tarball for full documentation.

# Core
Mode                client|relay|da|hs|ob
BindAddress         0.0.0.0
ORPort              9001
SocksPort           9050
AdvertiseAddress    YOUR_PUBLIC_IP
Nickname            MyRelay
DataDirectory       /var/lib/moor
ContactInfo         you@example.com

# Relay flags
Guard               1
ExitRelay           1
MiddleOnly          1

# Client
EntryNode           DROPOUT
TransPort           9040
DNSPort             5353
PIR                 1

# Hidden service
HiddenServiceDir    /var/lib/moor/hs
HiddenServicePort   8080
HSPoW               1

# Traffic analysis
PaddingMachine      generic|web|stream|none
MixDelay            0
Conflux             0

# Bridges
IsBridge            1
UseBridges          1
Bridge              shade 1.2.3.4:443 <fingerprint>

# Accounting
AccountingMax       0
AccountingPeriod    2592000  # 30 days (seconds)

# Control
ControlPort         9051
Verbose             1
Daemon              1

Flag Assignment

All relays start as middle relays. The DA assigns flags based on performance:

Running   DA can probe your OR port
Fast      Bandwidth above 12.5th percentile
Stable    Uptime above median
Guard     Fast + Stable + bandwidth + uptime
HSDir     Fast + Stable + 96h uptime
Exit      Self-declared, DA preserves

On small networks (<20 relays), Guard and Exit are granted immediately.

Build-ID fleet gate: every binary is stamped with its git commit. DAs reject descriptors whose build ID doesn't match — the fleet upgrades in lockstep, so there's never silent version drift. Delete DataDirectory to regenerate keys.

Protocol

Cell format (514 bytes fixed)

circuit_id   4 bytes (big-endian)
command      1 byte
payload    509 bytes

PQ hybrid circuit handshake

Client -> Relay:  CREATE_PQ  [identity(32) | eph_pk(32)]
Relay -> Client:  CREATED_PQ [relay_eph(32) | auth_tag(32)]
Client -> Relay:  CELL_KEM_CT  [ml_kem_768_ct(1088)]

Both sides derive:
  dh_shared  = X25519(ephemeral pairs)
  kem_shared = ML-KEM-768_Decap(ct, sk)
  hybrid     = BLAKE2b(dh_shared || kem_shared)
  fwd_key    = KDF(hybrid, 1, "moorFWD!")
  bwd_key    = KDF(hybrid, 2, "moorBWD!")

Sequential-AND: both halves MUST succeed or the
circuit is torn down. No classical-only fallback.

Crypto stack

Layer                Algorithm                    PQ
-----                ---------                    --
Link                 Noise_IK + XChaCha20-Poly    + ML-KEM-768
Circuit (all 3 hops) X25519 CKE                   + ML-KEM-768 KEM
Cell encryption      ChaCha20 stream              —
Cell digest          BLAKE2b-256                  —
Consensus            Ed25519                      + ML-DSA-65
HS identity          Ed25519                      + Falcon-512
HS INTRODUCE1        —                            + ML-KEM-768 sealed

.moor address anatomy

addr = base32(
  Ed25519_pk                          # 32 bytes (classical id)
  || BLAKE2b-16(
       ML-KEM-768_pk || Falcon-512_pk # commits PQ material
     )
  || checksum || version
) + ".moor"

Security

Threat model

Protects against: network observers, malicious minority relays, harvest-now-decrypt-later quantum attacks.

Does NOT protect against: global passive adversary (timing correlation), compromised endpoints, all 3 hops controlled by same adversary.

PQ hybrid guarantee

Every key exchange combines X25519 and ML-KEM-768. Every HS identity combines Ed25519 and Falcon-512. Consensus is co-signed with Ed25519 and ML-DSA-65. An attacker must break both halves to compromise any secret — sequential-AND, no downgrade paths.

What's audited

libsodium (Ed25519, X25519, ChaCha20, BLAKE2b): independently audited, constant-time.
ML-KEM-768 / ML-DSA-65 / Falcon-512: vendored from PQClean reference implementations.
Protocol code: security-audited March 2026. Fixes for nonce reuse, UAF, crypto wipe, DoS hardening.

OPSEC

Use socks5h:// — the h means DNS resolves through the network. Without it, your ISP sees every domain you visit.

HTTPS everywhere — MOOR encrypts between you and the exit relay. The exit-to-destination link is the open internet.

Don't log into real accounts — MOOR anonymizes your IP, not your identity.

Use pluggable transports in censored environments — ShitStorm mimics Chrome, Mirage looks like TLS 1.3, Nether like Minecraft, Speakeasy like SSH, Scramble like HTTP, Shade is indistinguishable from uniform random bytes.

Anonymity requires a crowd — the more people use MOOR, the stronger everyone's anonymity becomes.