Cassette
portable, read-only nostr relays.
Install / Use
/learn @sandwichfarm/CassetteREADME
ATTENTION: Alpha! WASM interface has not yet been standardized, cassettes you make today might be broken tomorrow. This notice will be removed when interface has stabilized.
Cassette 📼
Portable, read-only nostr relays that you can scrub, dub and distribute notes from. Mostly rust, compiled to WASM.
Initially written on a Saturday over brunch at SEC-04 with futurepaul
What's New in v0.9.x
- 📼 New
.cassettefile extension: Cassettes now use the.cassetteextension instead of.wasm. Backwards compatibility maintained -.wasmfiles continue to work - 🚀 Automatic REQ looping in all language bindings: All bindings now automatically detect REQ messages and loop until EOSE
- JavaScript/TypeScript: Returns
string | string[] - Python: Returns
Union[str, List[str]] - Rust: Returns
SendResultenum (Single/Multiple) - Go: Returns
*SendResultstruct - C++: Returns
std::variant<std::string, std::vector<std::string>> - Dart: Returns
dynamic(StringorList<String>)
- JavaScript/TypeScript: Returns
- 📦 Embedded cassette-tools in deck binary: Deck and record commands now work standalone without requiring cassette-tools in the working directory
- 🐛 Fixed Handlebars HTML escaping: Resolved issue where new cassettes couldn't be read due to JSON content being HTML-escaped
- 📊 Comprehensive benchmark suite: Added benchmarks for all language bindings and deck performance testing
- 🐳 Docker support: Official Docker image and docker-compose configuration for easy deployment
NIPs Look:
- [x] NIP-01 - Basic Relay Protocol (REQ/EVENT/EOSE/CLOSE)
- [x] NIP-11 - Relay Information Document (relay metadata and capabilities)
- [ ] NIP-42 - Authentication (framework implemented, functionality planned)
- [x] NIP-45 - Event Counts (COUNT queries for efficient event counting)
- [x] NIP-50 - Search Capability (text search with relevance ranking)
Quick Start
Prerequisites
Install
Download the latest cli binary from releases.
Record a cassette
Pipe the events or provide the path to a json file with events.
# cassette with NIP-50 "Search" and NIP-45 "Count"
nak req -k 1 -l 100 wss://nos.lol | cassette record --name my-notes --nip-50 --nip-45
cassette scrub my-notes.cassette -l 5
cassette scrub --count -k 0
cassette scrub --search "gm"
The cassette cli also demonstrates different potential ways to use a cassette
- Mix events from multiple cassettes into one with
dub - Cast events to relays with
play - Serve a read-only relay based on cassettes with
listen - Serve a relay that records incoming events into new cassettes, compiles them and hot-loads them into state with
deck
CLI Commands
record - Record events onto cassettes via stdin (ndjson, json arrays, NIP-01 EVENT messages) or with provided file.
cassette record [OPTIONS] [INPUT_FILE]
# Options:
# -n, --name Name for the cassette
# -d, --description Description of contents
# -a, --author Author/curator name
# -o, --output Output directory (default: ./cassettes)
# --no-bindings Skip JavaScript bindings, WASM only
# --nip-42 Enable NIP-42 (Authentication) - placeholder
# --nip-45 Enable NIP-45 (Event Counts)
# --nip-50 Enable NIP-50 (Search Capability)
# --relay-name Name for NIP-11 relay info
# --relay-description Description for NIP-11 relay info
# --relay-contact Contact for NIP-11 relay info
# --relay-pubkey Owner pubkey for NIP-11 relay info
# Examples:
# record accepts ndjson, json arrays, and NIP-01 `EVENT` messages.
nak req -k 30023 wss://relay.nostr.band | cassette record -n "long-form"
cassette record my-events.json --name "my-backup"
cassette record events.json --nip-45 --name "countable" # With COUNT support
cassette record events.json --nip-50 --name "searchable" # With search support
cassette record events.json --nip-45 --nip-50 --name "Archive"
scrub - Scrub through cassettes (send a req)
cassette scrub [OPTIONS] <CASSETTE>
# Options:
# -s, --subscription Subscription ID (default: sub1)
# -f, --filter Custom filter JSON
# -k, --kinds Event kinds to return
# -a, --authors Filter by authors
# -l, --limit Maximum events to return
# --since Events after timestamp
# --until Events before timestamp
# -o, --output Output format: json or ndjson
# --info Show NIP-11 relay information
# --count Perform COUNT query (NIP-45)
# --search Search query for NIP-50 text search
# --relay-name Set name for dynamic NIP-11 info
# --relay-description Set description for dynamic NIP-11 info
# --relay-contact Set contact for dynamic NIP-11 info
# --relay-pubkey Set owner pubkey for dynamic NIP-11 info
# Examples:
cassette scrub my-notes.cassette --kinds 1 --limit 50
cassette scrub archive.cassette --filter '{"#t": ["bitcoin", "lightning"]}'
cassette scrub events.cassette --output ndjson | grep "pattern"
dub - Combine cassettes into a Mixtape
cassette dub [OPTIONS] <CASSETTES...> <OUTPUT>
# Options:
# -n, --name Name for output cassette
# -d, --description Description
# -a, --author Author/curator
# -f, --filter Apply filters when combining
# -k, --kinds Include only these kinds
# --authors Include only these authors
# -l, --limit Limit total events
# --since Events after timestamp
# --until Events before timestamp
# Examples:
cassette dub cassette1.cassette cassette2.cassette combined.cassette
cassette dub *.cassette all-events.cassette --name "Complete Archive"
cassette dub raw/*.cassette clean.cassette --kinds 1 --kinds 30023
play - Broadcast events to Nostr relays
cassette play [OPTIONS] <CASSETTES...> --relays <RELAYS...>
# Options:
# -r, --relays Target relay URLs (required)
# -c, --concurrency Max concurrent connections (default: 5)
# -t, --throttle Delay between events in ms (default: 100)
# --timeout Connection timeout in seconds (default: 30)
# --dry-run Preview without sending
# Examples:
cassette play events.cassette --relays wss://relay.damus.io
cassette play *.cassette --relays wss://nos.lol wss://relay.nostr.band
cassette play archive.cassette --relays ws://localhost:7000 --dry-run
# Note: The 'cast' command is deprecated and will show a warning
listen - Serve a read-only relay from one or more cassettes
cassette listen [OPTIONS] <CASSETTES...>
# Options:
# -p, --port Port to listen on (auto-selects if not specified)
# --bind Bind address (default: 127.0.0.1)
# --tls Enable TLS/WSS
# --tls-cert Path to TLS certificate
# --tls-key Path to TLS key
# -v, --verbose Show connection details
# Examples:
cassette listen my-notes.cassette # Auto-select port
cassette listen *.cassette --port 8080 # Serve all cassettes
cassette listen dir/*.cassette --bind 0.0.0.0 --port 1337 # Listen on all interfaces
cassette listen archive.cassette --verbose # Debug mode
# Features:
# - Serves cassettes as a NIP-01 compliant WebSocket relay
# - Supports NIP-11 relay information via HTTP with Accept: application/nostr+json
# - Handles multiple cassettes - aggregates responses from all loaded cassettes
# - Auto-selects available port if not specified
# - Compatible with all Nostr clients (nak, nostcat, web clients, etc.)
# - Each connection gets a fresh state to prevent cross-connection contamination
deck - Run a cassette deck relay
cassette deck [OPTIONS]
# Options:
# -m, --mode Operation mode: 'relay' (writable) or 'record' (from relays)
# -r, --relays Relay URLs to record from (record mode only)
# -n, --name Base name for cassettes (default: deck)
# -o, --output Output directory (default: ./deck)
# -p, --port Port to serve on (default: 7777)
# --bind Bind address (default: 127.0.0.1)
# -e, --event-limit Max events per cassette (default: 10000)
# -s, --size-limit Max cassette size in MB (default: 100)
# -d, --duration Recording duration per cassette in seconds (default: 3600)
# -f, --filter Filter JSON for recording
# -k, --kinds Event kinds to record
# --authors Authors to filter
# -v, --verbose Show verbose output
# --nip-11 Enable NIP-11 support
# --nip-45 Enable NIP-45 (COUNT) support
# --nip-50 Enable NIP-50 (search) support
# Examples:
# Relay mode - accept events and compile cassettes
cassette deck # Default relay mode
cassette deck -p 1337 -e 100 # Custom port and rotation
cassette deck -v --name archive # Verbose with custom name
# Record mode - record from other relays
cassette deck --mode record --relays wss://relay.damus.io
cassette deck -m record -r wss://nos.lol --kinds 1 --kinds 30023
cassette deck -m record -r wss://relay.nostr.band --filter '{"#t":["bitcoin"]}'
# Features:
# - Relay mode: Acts as a writable relay, stores events in rotating cassettes
# - Record mode: Continuously records from other relays
# - Auto-rotation based on event count, size, or time
# - Hot-loads compiled cassettes for immediate querying
# - Proper NIP-01 compliance with event deduplication
# - Replaceable event handling (kinds 0, 3, 10000-19999, 30000-39999)
Advanced Configuration
Modular NIP Support
Cassettes support modular NIP (Nostr Implement
Related Skills
node-connect
353.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
353.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
353.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
