A Rust Nostr relay with RocksDB storage, synchronous writes, and WebSocket and Unix-domain socket transports. Wok follows the Nostr specifications and provides verified, offline migration from existing Wok and strfry databases.
- Verified migration.
wok migrate strfryimports a stopped strfry v3 database directly into RocksDB, preserves packed events and encoded payloads, translates supported config settings, and emits a checksummed manifest.wok migrate rocksdbpreserves the full state of a Wok LMDB v4/v5 database. - RocksDB storage. Every build uses the same Wok-owned runtime layout, snapshot reads, atomic index updates and synchronous WAL commits. LMDB is supported only as a read-only migration source; no writers share a database.
- Nostr protocol conformance. EVENT/REQ/CLOSE/COUNT/EOSE/OK/NOTICE/ CLOSED/AUTH and NEG-* are tested against pinned NIPs.
- NIP-42 AUTH, NIP-45 COUNT with mergeable HyperLogLog sketches, NIP-50 ranked content search, NIP-62 restart-safe Request to Vanish, NIP-67 EOSE completeness hints, NIP-70 protected events, NIP-59 gift-wrap deletion semantics, NIP-77 negentropy set reconciliation (persistent B-tree, tree-backed multi-round sync sessions), and draft NIP-91 AND tag filters.
- Standards-first ephemeral delivery: ephemeral kinds are live-only by default, with an explicit persisted TTL compatibility mode.
- permessage-deflate via an in-house RFC 6455/7692 codec.
- Unix
SOCK_STREAMtransport: 4-byte big-endian length-prefixed JSON, same dispatcher as WebSocket. - Mesh tooling:
router(multi-connection replication with hot reconfig),sync(NIP-77 two-phase transfer),upload,download. - Operational continuity: worker pools (
numThreads.*), single application writer, bounded queues with backpressure, slow-client termination (max_pending_outbound_bytes), config hot-reload, graceful shutdown (SIGUSR1/SIGINT), write-policy plugins, Prometheus metrics, structured JSON tracing for Grafana/Loki pipelines, hard-bounded local chart history, and migration of the supportedstrfry.confsubset. - Native abuse resistance: per-connection, per-address and per-author token buckets, separate connection/EVENT/REQ/NEG-MSG/COUNT budgets, pre-scan query costing, historical-query concurrency limits, bounded NIP-50 index growth, default global and author storage quotas, a free-disk reserve, rejection metrics, and optional NIP-13 proof-of-work enforcement advertised through NIP-11.
- Hardened untrusted-input boundary: safe crates forbid unsafe Rust, native storage and OS FFI are isolated and documented, and property tests plus scheduled AddressSanitizer-backed fuzzing exercise JSON, events, WebSockets, compression, Negentropy, and database transaction sequences.
cargo build --release --locked -p wok-cliThe binary is target/release/wok. Requires Rust 1.88 or newer, a C/C++
toolchain and libclang for RocksDB bindings. On Debian/Ubuntu install
g++ clang libclang-dev; on macOS use Xcode's command-line tools and LLVM
(brew install llvm, with LIBCLANG_PATH pointing to its lib directory if
needed). RocksDB and zstd build from vendored native sources. LMDB 0.9.37 is
vendored for read-only imports; no system liblmdb is linked. Outbound TLS uses
Rustls and the operating system's native root certificates.
Tagged releases publish one checksummed native archive per target for Linux x86-64/ARM64 and macOS Intel/Apple Silicon. The 0.8.0 release candidate uses RocksDB throughout. See CHANGELOG.md for notable changes and docs/re 8000 leases.md for the tag and release process.
# Stop strfry and all other source writers; keep them stopped through copying.
# Read-only preflight creates no snapshot or output directory.
./target/release/wok migrate strfry \
--db /var/lib/strfry \
--config /etc/strfry.conf \
--output /var/lib/wok \
--check
# Review the report, then migrate while the source remains stopped.
./target/release/wok migrate strfry \
--db /var/lib/strfry \
--config /etc/strfry.conf \
--output /var/lib/wok --confirm-relay-stopped
# Review the generated config and manifest, then start Wok.
./target/release/wok --config /var/lib/wok/wok.toml relayThe output contains db/, wok.toml, and migration-manifest.json. The source
authoritative data and config are unchanged; registered reader coordination can
update LMDB's lock.mdb. The output directory must not already
exist. See Migration from strfry for cutover,
verification, and rollback.
For a new relay, start with docs/wok.toml and an empty database path instead.
Unix socket (disabled by default):
[relay.unix]
enabled = true
path = "./wok-db/wok.sock"
mode = 0o600Available operator commands:
| Command | Notes |
|---|---|
migrate strfry --db <dir> --config <file> --output <dir> [--check] |
Direct read-only strfry v3 → RocksDB import; copying requires --confirm-relay-stopped |
migrate rocksdb --source <dir> --output <dir> [--check] [--json] |
Offline full-state Wok LMDB v4/v5 → RocksDB copy; copying requires --confirm-relay-stopped |
backup --output <new-directory>, storage-backend, rocksdb-check |
Native checkpoint (relay stopped), runtime identity, offline physical audit |
relay |
WS (+ optional Unix) relay |
import / export |
JSONL, --fried, --since/--until/--reverse |
scan, event <levId>, info, delete, monitor, integrity |
DB utilities |
config show [--overrides | --defaults] |
Effective configuration with each value's origin: default, preset, or file line (--overrides: only values the file changes from its preset; --defaults: every setting with the preset's value) |
config minimize [--dry-run] |
Remove settings that repeat the preset's values, keeping comments and a .bak backup (--dry-run: print a diff) |
doctor [--json] |
Config, storage, index, payload, negentropy, capacity, and runtime-path diagnostics |
purge-superseded [--dry-run] [--json] |
Delete replaceable versions superseded at their address, keeping the version reads serve |
reindex --confirm-relay-stopped [--backup <dir>] |
Stage and verify rebuilt indexes, repack records that dropped long tag values, atomically promote them, and retain the original DB |
dict stats/train/compress/decompress |
zstd dictionary management (ZDICT training included) |
negentropy list/add/build |
persistent trees; build uses bounded, restart-safe batches |
router <file> |
mesh replication with hot reconfig |
sync, upload, download |
mesh transfers; sync reports transfer outcomes and set differences |
Before cutover or after an unclean shutdown, run wok --config wok.toml doctor. It validates every event-derived index semantically, decompresses
payloads and matches their IDs to packed records, opens every negentropy tree,
checks the Wok database marker, reports RocksDB health and free disk
capacity, and verifies configured plugin and Unix-socket paths. --json emits
the complete machine-readable report; failures exit nonzero.
If doctor finds damage confined to derived event indexes or negentropy,
stop every process using the database and run wok --config wok.toml reindex --confirm-relay-stopped. Wok rebuilds a fresh sibling database from exact
PackedEvent and EventPayload primary bytes, recreates all event and negentropy
indexes, verifies integrity and the complete event fingerprint, then promotes
the staged directory. The original database remains in the reported sibling
backup directory and a reindex-manifest.json records the operation.
crates/
wok-event Event JSON, NIP-01 hashing (tao::json-exact), Schnorr, PackedEvent
wok-db RocksDB storage, read-only LMDB imports, transactions, integrity
wok-query Filters, DBScan, QueryScheduler, ActiveMonitors
wok-negentropy NIP-77 protocol, Vector storage, persistent B-tree
wok-relay Transport-neutral dispatcher, writer, AUTH, plugins, cron
wok-ws HTTP + WebSocket transport (in-house codec, permessage-deflate)
wok-unix Length-prefixed Unix SOCK_STREAM transport
wok-cli relay, dbutils, mesh commands
wok-bench Comparative benchmark harness
wok-compat C++ differential harnesses and fixtures
Threading model: Tokio only at transport boundaries; dedicated synchronous OS
threads own storage. Transactions, cursors, and borrowed records never cross an
.await. One application-level writer with bounded queues; worker pools
per stage (numThreads.*) with connections hashed onto workers.
We tested Wok RocksDB and strfry over WebSocket during sustained writes on separate relay and load VMs, starting with 15 million generated, signed events of kinds 1 and 7. Queries covered IDs, author history, 500-author feeds and tags at 1,000 queries/second. Each write rate ran twice with reversed relay order: ten minutes at 1,000 writes/second and five at 4,000. Later fastr runs used the same workload. All three completed 1.8 million reads and 3.6 million writes, with event bodies, ordering and accepted-write readback checked.
p99 latency, in milliseconds (lower is better). Read ranges cover all query shapes and write rates; each range spans trial percentiles without pooling.
| Relay | Reads | Writes: 1k/s | Writes: 4k/s |
|---|---|---|---|
| Wok RocksDB | 3.2–7.9 | 9.1 | 43.2–46.9 |
| strfry 1.1.3 | 7.2–11.9 | 150.4–159.5 | 430.5–3,724.0 |
| fastr | 52.2–20,300 | 266,900–269,200 | 227,000–227,100 |
Latency includes queueing, client and network delay, from scheduled arrival to EOSE or write acknowledgement. fastr accumulated queues and acknowledged buffered writes without an fsync barrier; its durability guarantee differs. Wok's read p99 was 34–61% lower than strfry across all 16 matched cases, but its rare read tail (p99.9) was worse in one run at 4,000 writes/second.
These finite, prewarmed runs used different authors for writes and queries. They establish neither maximum capacity nor equal power-loss durability. A separate one-hour correctness soak checked latest feeds between write batches, live delivery, restart and acknowledged-write process-crash recovery.
Results describe the measured RocksDB candidate at
11fdf70
and fastr at
bdbc0bc,
before the single-runtime release preparation; they do not measure every later
release change.
Earlier comparison evidence and limits
· Matched campaign instructions.
For a smaller local comparison with the current RocksDB runtime:
cargo build --release --locked -p wok-cli -p wok-bench
./target/release/wok-bench --profile full --out bench-results \
--strfry /path/to/strfry --wok ./target/release/wok --seed 1Build requirements and full-state upgrades: RocksDB migration guide. Harness and methodology: docs/benchmarks.md. NIP-50 design, semantics, and search workload: docs/nip50-search.md.
- Architecture
- Migration from strfry
- strfry LMDB v3 import contract
- Unix socket protocol
- Supported NIPs
- Configuration
- Production deployment security
- Observability
- Operator dashboard
- Benchmark methodology
- Mesh and maintenance
- Cutover / rollback
- Security
- Reliability validation and CI gates
- Local and two-VM load lab
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
cargo test -p wok-compat --test nip_conformance --test e2e_transports
# Optional import/interop regression tests (requires a strfry binary):
cargo test -p wok-db --test cpp_roundtrip
cargo test -p wok-compat --test cpp_export --test cpp_negentropySee CONTRIBUTING.md for setup, conventions, and what a pull request needs. Report vulnerabilities as described in SECURITY.md.
Fast property tests live beside the relevant crates. The composed libFuzzer target and scheduled sanitizer workflow are documented in Security.
GNU Affero General Public License v3.0 or later
(AGPL-3.0-or-later).
Wok began as an independent Rust reimplementation of strfry by Doug Hoyte, which is GPL-3.0. The Nostr protocol is documented by the nostr-protocol/nips repository.