SkillAgentSearch skills...

Cassette

portable, read-only nostr relays.

Install / Use

/learn @sandwichfarm/Cassette
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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 .cassette file extension: Cassettes now use the .cassette extension instead of .wasm. Backwards compatibility maintained - .wasm files 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 SendResult enum (Single/Multiple)
    • Go: Returns *SendResult struct
    • C++: Returns std::variant<std::string, std::vector<std::string>>
    • Dart: Returns dynamic (String or List<String>)
  • 📦 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

View on GitHub
GitHub Stars4
CategoryDevelopment
Updated3mo ago
Forks0

Languages

Rust

Security Score

87/100

Audited on Jan 3, 2026

No findings