Ruzor
Ruzor is the Rust port of the Pyzor 1.1.2 UDP client and server.
Project Links
- Website: https://ruzor.org
- Documentation: https://docs.ruzor.org and documentation/index.html
- Public Ruzor service:
public.ruzor.org:24441 - GitHub: https://github.com/bonjourservices/ruzor
- crates.io: https://crates.io/crates/ruzor
Pyzor is a collaborative, networked spam detection system that identifies messages by digest and lets clients check, report, or whitelist those digests against a Pyzor server. This crate provides the ruzor client and ruzord daemon as a Rust package with command-line behavior and storage formats compatible with the upstream Pyzor 1.1 documentation.
Compatibility
This project targets 1:1 observable compatibility with Pyzor 1.1.2 for the client and server CLIs:
- Same UDP protocol shape: RFC-822-style datagrams,
PV: 2.1, 8192-byte packet limit, thread ids, SHA-1 digests, and SHA-1 request signatures. - Same client commands:
check,info,report,whitelist,ping,pong,digest,predigest,genkey,local_whitelist, andlocal_unwhitelist. - Same input styles:
msg,mbox, anddigests. - Same server operations, anonymous access defaults, passwd/access-file semantics, logging behavior, forwarding behavior, and Unix signal handling for graceful shutdown and reload.
- Same backend record formats for GNU gdbm, Redis v1, Redis v0, and MySQL.
Upstream Pyzor documentation is available at https://www.pyzor.org/en/latest/. This crate intentionally covers the client/server package surface; it does not ship the Python-only pyzor-migrate helper.
Install
Prebuilt Binaries
Download release archives from https://github.com/bonjourservices/ruzor/releases. Release archives contain ruzor, ruzord, README.md, and LICENSE for the target platform. The default release binaries use the GNU gdbm backend and require GNU gdbm at runtime.
Cargo
Install the full package with the default backends:
The default build includes the GNU gdbm backend, so system GNU gdbm headers/libraries must be available:
# Debian/Ubuntu
# macOS/Homebrew
For a build without the gdbm backend:
Quick Start
Create a small test message:
Print the Pyzor digest without contacting a server:
Start a local server in one terminal:
Use the client from another terminal:
Check by digest rather than by message content:
Client Usage
The client reads from stdin for message-oriented commands:
Common commands:
Useful options:
||
If no server file is configured, Pyzor-compatible clients default to the upstream public Pyzor server public.pyzor.org:24441, matching upstream Pyzor. To use the public Ruzor endpoint instead, write public.ruzor.org:24441 to your servers file:
Use a local servers file for private testing so report and whitelist do not affect a public server.
Server Usage
Run a server with the default GNU gdbm backend:
Use explicit paths for database, passwd, and ACL files:
Backend examples:
# Redis v1 hash backend
# Redis with upstream check-miss proxying and local positive-cache writes
# Legacy Redis v0 string backend
# MySQL backend: host,user,password,database,table
Redis DSNs also accept an optional fifth username field (host,port,password,db,username) for managed Redis ACL users.
--proxy-source accepts a comma-separated list of host[:port] Pyzor-compatible servers. On a local check miss, ruzord checks those sources in order, returns the first positive upstream Count or WL-Count, and stores that positive response in the configured backend before replying. Empty upstream matches and upstream errors are not cached.
The MySQL table must use the upstream Pyzor schema:
(
digest char(40) NOT NULL,
r_count int(11) DEFAULT NULL,
wl_count int(11) DEFAULT NULL,
r_entered datetime DEFAULT NULL,
wl_entered datetime DEFAULT NULL,
r_updated datetime DEFAULT NULL,
wl_updated datetime DEFAULT NULL,
PRIMARY KEY (digest)
);
Operational options:
On Unix, send SIGTERM for graceful shutdown and SIGUSR1 to reload passwd/access files:
Configuration Files
By default, both commands use ~/.ruzor when HOME is set, otherwise /etc/ruzor. Paths in config files are resolved relative to --homedir unless absolute.
Common files:
servers: onehost:portserver per line for client operations.accounts: client credentials in upstream Pyzor format.whitelist: local client whitelist digests.ruzord.passwd: server account database.ruzord.access: server ACL file.ruzord.db: default GNU gdbm digest database.
If no access file exists, anonymous users may check, report, ping, pong, and info; whitelist is denied by default.
Build From Source
Requirements:
- Rust stable, MSRV
1.95. - GNU gdbm development files for the default backend.
- Redis or MySQL only when using those live backends.
Build:
Run directly from the checkout:
Test
The normal package test suite is self-contained:
Optional live backend tests:
PYZOR_MYSQL_DSN=host,user,password,db,table
Benchmarks
Run local comparison benchmarks against upstream Pyzor 1.1.2:
Use a Python interpreter with dbm.gnu support for the Python pyzord comparison. On Homebrew macOS, pass --python-bin /opt/homebrew/bin/python3 if needed.
Benchmark snapshot from May 28, 2026 on Apple M3 Pro, macOS 26.5 arm64, Python 3.14.5, hyperfine 1.20.0, and release-mode Ruzor binaries:
| What | Python (Pyzor) | Rust (Ruzor) | Difference |
|---|---|---|---|
| Client program footprint | 96.4 MiB Python runtime + 14.7 KiB script | 710.4 KiB native binary | -99.3% |
| Server program footprint | 96.4 MiB Python runtime + 16.4 KiB script | 2.36 MiB native binary | -97.5% |
| Release package footprint | 96.6 MiB Pyzor install + runtime | 1.43 MiB release archive | -98.5% |
| Client startup latency | 65.23 ms | 4.44 ms | -93.2% |
| Small message digest latency | 59.34 ms | 3.40 ms | -94.3% |
| 46 KiB message digest latency | 65.61 ms | 2.88 ms | -95.6% |
| 100-message mbox digest latency | 62.31 ms | 5.40 ms | -91.3% |
| Idle server RSS | 26.61 MiB | 1.77 MiB | -93.3% |
UDP ping p50 latency |
99.1 us | 26.9 us | -72.9% |
UDP ping throughput |
9,698 req/s | 30,571 req/s | +215% |
UDP check p50 latency |
133.8 us | 35.4 us | -73.5% |
UDP check throughput |
7,131 req/s | 26,508 req/s | +272% |
These numbers are local-loopback measurements, so use the included benchmark harness when comparing on another machine or before publishing updated claims.
Feature Flags
| Feature | Default | Description |
|---|---|---|
backend-gdbm |
yes | GNU gdbm server backend, compatible with Python dbm.gnu databases. |
backend-gdbm-native |
no | Alias for backend-gdbm kept for compatibility with earlier builds. |
backend-redis |
yes | Redis v1/v0 server backends. |
backend-mysql |
yes | MySQL server backend through the Rust mysql crate. |
Releases
GitHub releases are tag-driven. To cut a release:
VERSION=0.1.2
The release workflow builds with stable Rust, verifies the crate package, and uploads native binary archives for Linux x64, macOS arm64, and macOS Intel. CI runs on pushes and pull requests with fmt, clippy, cargo test, and cargo package.
License
GPL-3.0-only. See LICENSE.