Creative Low-Latency Application Streaming Protocol
CLASP is a real-time infrastructure layer for connected devices. One binary protocol handles state synchronization, pub/sub messaging, E2E encryption, device identity, programmable transforms, reactive automation, and multi-site federation. Bridges for MQTT, BLE, Serial, HTTP, OSC, MIDI, DMX, and Art-Net make existing hardware work together immediately.
Building connected applications means solving the same problems every time: state sync, auth, encryption, discovery, automation, multi-site coordination. Every project reinvents this from scratch. CLASP provides all of it in one system.
Five things set it apart from generic pub/sub:
Semantic signal types. The router distinguishes Params (stateful, conflict-resolved, persisted, delivered to late-joiners), Events (confirmed, ephemeral), Streams (best-effort, droppable under congestion), Gestures (phased lifecycle with begin/update/end), and Timelines (keyframed automation with easing). The infrastructure layer makes routing decisions that other systems push to application code.
Protocol bridging as architecture. MQTT, OSC, MIDI, DMX, Art-Net, sACN, HTTP, WebSocket, Socket.IO, BLE, Serial. Each protocol gets one bridge. Any device on any protocol can read and write state from any other device on any other protocol, through one router. N bridges, not N-squared adapters.
Late-joiner state sync. New clients receive a snapshot of all current Param state the instant they subscribe. Five conflict strategies (LWW, Max, Min, Lock, Merge) with optimistic concurrency via revision numbers. This eliminates an entire class of "initial state" bugs that plague every real-time system.
Server-side rules engine. Reactive automation evaluated on every state change: thresholds, pattern-matched triggers, conditions, transforms, cooldowns. Zero application code. The router becomes a programmable edge compute node.
Federation with namespace ownership. Hub-leaf topology where each site owns its namespace prefix. No distributed consensus needed. DefraDB integration adds Merkle CRDT-based persistent state that survives restarts and syncs across sites automatically.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ TouchOSC │ │ Ableton │ │ LED Strip │
│ (OSC) │ │ (MIDI) │ │ (Art-Net) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└───────────────────┼───────────────────┘
│
┌──────▼──────┐
│ CLASP │
│ Router │
└──────┬──────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ Web UI │ │ IoT Hub │ │ Resolume │
│ (WebSocket) │ │ (MQTT) │ │ (OSC) │
└─────────────┘ └─────────────┘ └─────────────┘
cargo install clasp-cliClient Libraries
| Platform | Package | Install |
|---|---|---|
| JavaScript | @clasp-to/sdk | npm install @clasp-to/sdk |
| JavaScript | @clasp-to/core | npm install @clasp-to/core |
| JavaScript | @clasp-to/crypto | npm install @clasp-to/crypto |
| Python | clasp-to | pip install clasp-to |
| Arduino | Clasp | Arduino Library Manager |
Rust Crates: Core
| Crate | Description | Install |
|---|---|---|
| clasp-core | Types, codec, state management | cargo add clasp-core |
| clasp-client | High-level async client | cargo add clasp-client |
| clasp-router | Message routing and pattern matching | cargo add clasp-router |
| clasp-transport | WebSocket, QUIC, TCP, BLE, Serial | cargo add clasp-transport |
| clasp-bridge | Protocol bridges (OSC, MIDI, MQTT, etc.) | cargo add clasp-bridge |
| clasp-crypto | E2E encryption (AES-256-GCM, ECDH) | cargo add clasp-crypto |
| clasp-identity | Unified Ed25519 identity (EntityId + DID + PeerID) | cargo add clasp-identity |
Rust Crates: Infrastructure
| Crate | Description | Install |
|---|---|---|
| clasp-caps | Delegatable Ed25519 capability tokens | cargo add clasp-caps |
| clasp-registry | Persistent entity identity registry | cargo add clasp-registry |
| clasp-journal | Append-only event journal | cargo add clasp-journal |
| clasp-rules | Server-side reactive rules engine | cargo add clasp-rules |
| clasp-federation | Router-to-router federation | cargo add clasp-federation |
| clasp-discovery | mDNS/DNS-SD device discovery | cargo add clasp-discovery |
| clasp-lens | LensVM WASM transform host | cargo add clasp-lens |
Rust Crates: DefraDB Integration (docs | DEFRA.md)
| Crate | Description | Install |
|---|---|---|
| clasp-journal-defra | DefraDB journal backend with P2P sync | cargo add clasp-journal-defra |
| clasp-state-defra | Write-through cache with DefraDB persistence | cargo add clasp-state-defra |
| clasp-defra-bridge | Bidirectional DefraDB/CLASP signal bridge | cargo add clasp-defra-bridge |
| clasp-config-defra | P2P config sync with version history | cargo add clasp-config-defra |
| clasp-registry-defra | DefraDB entity store with P2P identity sync | cargo add clasp-registry-defra |
| clasp-defra-transport | DefraDB sync over CLASP transports | cargo add clasp-defra-transport |
Create an Ubuntu 22.04 droplet (1 GB+ RAM), SSH in, and run:
curl -fsSL https://raw.githubusercontent.com/lumencanvas/clasp/main/deploy/marketplace/digitalocean/bootstrap.sh | bashThis installs Docker, pulls the relay image, and sets up the clasp-setup interactive configurator. Then run clasp-setup to choose your deployment profile, configure TLS, auth, persistence, protocol bridges, and start the relay.
To remove CLASP from a droplet:
docker compose -f /opt/clasp/docker-compose.yml down -v
rm -rf /opt/clasp /var/lib/clasp /usr/local/bin/clasp-setupSee deployment docs for full details.
Download the latest release for your platform:
- macOS: CLASP Bridge.dmg
- Windows: CLASP Bridge Setup.exe
- Linux: clasp-bridge.AppImage
The easiest way to use CLASP from JavaScript/TypeScript. @clasp-to/sdk wraps the core protocol with a human-friendly API:
import clasp from '@clasp-to/sdk'
const c = await clasp('ws://localhost:7330', { name: 'My App' })
await c.set('/lights/brightness', 0.8) // persistent state
c.on('/lights/**', (val, addr) => { ... }) // wildcard subscribe
await c.emit('/cues/go') // fire-and-forget event
c.stream('/sensors/accel', { x: 0.1, y: 9.8 }) // high-rate data
// Devices, encrypted rooms, rules, bridges. All built in
const device = await c.register({ name: 'Sensor', scopes: ['write:/sensors/**'] })
const room = await c.room('/chat/private') // E2E encrypted
c.rule('alert', { when: '/temp', above: 30, then: { emit: ['/alert', 'hot!'] } })For lower-level control, use @clasp-to/core directly (docs).
CLASP state is ephemeral by default. DefraDB is a peer-to-peer document database built on Merkle CRDTs. Six crates connect them so CLASP gets persistent, distributed state without giving up the sub-100us hot path.
Signals route through the in-memory cache as before. Writes flush to DefraDB in the background. DefraDB handles P2P replication between nodes. On restart, state loads back from DefraDB automatically.
This is primarily a win for CLASP: durable state, multi-router sync, and crash recovery. It may also be useful as a data ingestion path for DefraDB, since CLASP bridges MQTT, BLE, Serial, and HTTP protocols that DefraDB does not speak natively.
clasp-router --journal --journal-backend defra --journal-defra-url http://localhost:9181CLASP Router A ──> DefraDB Node 1 ◄──P2P──► DefraDB Node 2 <── CLASP Router B
See DEFRA.md for the full guide. Detailed docs at docs/defra/.
Important: CLASP uses a router-based architecture. Start a router first, then add protocol connections.
# 1. Start CLASP router (required - central message hub)
clasp server --port 7330
# 2. Start protocol connections (these connect TO the router)
# OSC: listens for OSC messages, translates and routes to CLASP
clasp osc --port 9000
# MQTT: connects to MQTT broker, translates and routes to CLASP
clasp mqtt --host broker.local --port 1883
# HTTP: provides REST API that translates to CLASP
clasp http --bind 0.0.0.0:3000
# Show all options
clasp --helpHow it works: Protocol commands (clasp osc, clasp mqtt, etc.) create bidirectional protocol connections that connect to the CLASP router. They translate between external protocols and CLASP, routing all messages through the central router. This enables any protocol to communicate with any other protocol through CLASP.
See Bridge Setup Guide for detailed setup instructions.
CLASP clients can communicate directly with each other through a CLASP router. Here are examples in each supported language:
Server (Node.js):
import { ClaspBuilder } from '@clasp-to/core';
// Connect to router
const server = await new ClaspBuilder('ws://localhost:7330')
.withName('LED Controller')
.connect();
// Listen for brightness changes
server.on('/lights/*/brightness', (value, address) => {
console.log(`Setting ${address} to ${value}`);
// Control actual LED hardware here
});
// Publish current state
await server.set('/lights/strip1/brightness', 0.8);Client (Browser or Node.js):
import { ClaspBuilder } from '@clasp-to/core';
const client = await new ClaspBuilder('ws://localhost:7330')
.withName('Control Panel')
.connect();
// Control the lights
await client.set('/lights/strip1/brightness', 0.5);
// Read current value
const brightness = await client.get('/lights/strip1/brightness');
console.log(`Current brightness: ${brightness}`);
// Subscribe to changes from other clients
client.on('/lights/**', (value, address) => {
console.log(`${address} changed to ${value}`);
});Publisher:
import asyncio
from clasp import ClaspBuilder
async def main():
client = await (
ClaspBuilder('ws://localhost:7330')
.with_name('Sensor Node')
.connect()
)
# Publish sensor data
while True:
temperature = read_sensor() # Your sensor code
await client.set('/sensors/room1/temperature', temperature)
await asyncio.sleep(1)
asyncio.run(main())Subscriber:
import asyncio
from clasp import ClaspBuilder
async def main():
client = await (
ClaspBuilder('ws://localhost:7330')
.with_name('Dashboard')
.connect()
)
# React to sensor updates
@client.on('/sensors/*/temperature')
def on_temperature(value, address):
print(f'{address}: {value}°C')
# Keep running
await client.run()
asyncio.run(main())Publisher:
use clasp_client::{Clasp, ClaspBuilder};
use clasp_core::Value;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = ClaspBuilder::new("ws://localhost:7330")
.name("Rust Publisher")
.connect()
.await?;
// Set values that other clients can subscribe to
client.set("/app/status", Value::String("running".into())).await?;
client.set("/app/counter", Value::Int(42)).await?;
// Stream high-frequency data
for i in 0..100 {
client.set("/app/position", Value::Float(i as f64 * 0.1)).await?;
tokio::time::sleep(std::time::Duration::from_millis(16)).await;
}
client.close().await?;
Ok(())
}Subscriber:
use clasp_client::{Clasp, ClaspBuilder};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = ClaspBuilder::new("ws://localhost:7330")
.name("Rust Subscriber")
.connect()
.await?;
// Subscribe to all app signals
let _unsub = client.subscribe("/app/**", |value, address| {
println!("{} = {:?}", address, value);
}).await?;
// Keep running
tokio::signal::ctrl_c().await?;
client.close().await?;
Ok(())
}CLASP clients in different languages can seamlessly communicate:
┌────────────────────┐ ┌─────────────────┐ ┌────────────────────┐
│ Python Sensor │ │ CLASP Router │ │ JS Web Dashboard │
│ │────▶│ (port 7330) │◀────│ │
│ set('/temp', 23.5) │ │ │ │ on('/temp', ...) │
└────────────────────┘ └─────────────────┘ └────────────────────┘
▲
│
┌───────┴───────┐
│ Rust Actuator │
│ │
│ on('/temp', │
│ adjust_hvac)│
└───────────────┘
- Protocol Connections: OSC, MIDI, Art-Net, DMX, MQTT, WebSocket, Socket.IO, HTTP/REST
- Signal Routing: Wildcard patterns (
*,**), 18 built-in transforms, custom WASM transforms via LensVM - Low Latency: WebSocket transport with sub-millisecond overhead
- State Sync: Automatic state synchronization between clients
- E2E Encryption: Client-side AES-256-GCM encryption with ECDH key exchange, TOFU, auto-rotation
- Delegatable Auth: Ed25519 capability tokens with UCAN-style delegation chains
- Entity Registry: Persistent identity for devices, users, services, and routers
- Journal Persistence: Append-only event log for crash recovery and state replay
- Rules Engine: Server-side reactive automation (triggers, conditions, transforms)
- Federation: Router-to-router state sharing for multi-site deployments
- Desktop App: Visual protocol configuration and signal monitoring
- DefraDB Integration: P2P persistent storage via Merkle CRDTs, zero-config multi-node sync, Zanzibar-style access control
- CLI Tool: Start routers and protocol connections from the command line
- Embeddable: Rust crates, WASM module, Python, JavaScript
We believe in transparent benchmarking with honest methodology.
These measure raw encode/decode speed, the theoretical ceiling, not system throughput:
| Protocol | Encode | Decode | Size | Notes |
|---|---|---|---|---|
| MQTT | 11.4M/s | 11.4M/s | 19 B | Minimal protocol |
| CLASP | 8M/s | 11M/s | 31 B | Rich semantics |
| OSC | 4.5M/s | 5.7M/s | 24 B | UDP only |
| JSON-WS | ~2M/s | ~2M/s | ~80 B | Typical JSON overhead |
Actual measured performance on localhost (macOS, M-series):
| Metric | P50 | P95 | P99 | Notes |
|---|---|---|---|---|
| SET (fire-and-forget) | <1µs | 1µs | 39µs | Client → Router |
| Single-hop | 34µs | 52µs | 82µs | Pub → Router → Sub |
| Fanout (10 subs) | 1.3ms | 1.4ms | 1.4ms | Time until ALL receive |
| Fanout (100 subs) | 2.0ms | 2.2ms | 2.6ms | Time until ALL receive |
| Throughput | 74k msg/s | - | - | Single client, sustained |
Run benchmarks yourself:
cargo run --release -p clasp-e2e --bin latency-benchmarks
cargo run --release -p clasp-e2e --bin chaos-testsCLASP uses efficient binary encoding that is 55% smaller than JSON:
JSON: {"type":"SET","address":"/test","value":0.5,...} → ~80 bytes
CLASP: [SET][flags][len][addr][value][rev] → 31 bytes
| Feature | CLASP | OSC | MQTT |
|---|---|---|---|
| State synchronization | ✅ | ❌ | ❌ |
| Late-joiner support | ✅ | ❌ | ✅ |
| Typed signals (Param/Event/Stream) | ✅ | ❌ | ❌ |
| Wildcard subscriptions | ✅ | ❌ | ✅ |
| Clock sync | ✅ | ✅ | ❌ |
| E2E encryption | ✅ | ❌ | ❌ |
| Multi-protocol bridging | ✅ | ❌ | ❌ |
| Delegatable auth (Ed25519) | ✅ | ❌ | ❌ |
| Router-to-router federation | ✅ | ❌ | ❌ |
| Server-side rules engine | ✅ | ❌ | ❌ |
| Programmable transforms (WASM) | ✅ | ❌ | ❌ |
| P2P state persistence (CRDTs) | ✅ | ❌ | ❌ |
- LAN (wired): Target ±1ms clock sync accuracy
- WiFi: Target ±5-10ms clock sync accuracy
- Not suitable for: Hard realtime, safety-critical, industrial control systems
CLASP is designed for soft realtime applications: live performance, IoT control, interactive installations, sensor networks, collaborative tools.
| Protocol | Direction | Features |
|---|---|---|
| CLASP | Bidirectional | Native protocol, WebSocket transport, sub-ms latency |
| OSC | Bidirectional | UDP, bundles, all argument types |
| MIDI | Bidirectional | Notes, CC, program change, sysex |
| Art-Net | Bidirectional | DMX over Ethernet, multiple universes |
| DMX | Output | USB interfaces (FTDI, ENTTEC) |
| MQTT | Bidirectional | v3.1.1/v5, TLS, wildcards |
| WebSocket | Bidirectional | Client/server, JSON/binary |
| Socket.IO | Bidirectional | v4, rooms, namespaces |
| HTTP | Bidirectional | REST API, CORS, client/server |
CLASP supports multiple network transports for different use cases:
| Transport | Use Case | Features |
|---|---|---|
| WebSocket | Web apps, cross-platform | Default transport, works everywhere, JSON or binary |
| QUIC | Native apps, mobile | TLS 1.3, 0-RTT, connection migration, multiplexed streams |
| UDP | Low-latency, local network | Minimal overhead, best for high-frequency data |
| TCP | Reliable delivery | For environments where UDP is blocked |
| Serial | Hardware integration | UART/RS-232 for embedded devices |
| BLE | Wireless sensors | Bluetooth Low Energy for IoT devices |
| WebRTC | P2P, browser-to-browser | NAT traversal, direct peer connections |
Enable transports with feature flags:
# Default (WebSocket + UDP + QUIC)
cargo add clasp-transport
# All transports
cargo add clasp-transport --features full
# Specific transports
cargo add clasp-transport --features "websocket,quic,serial"CLASP Chat is a production chat application built on a generic CLASP relay. Rooms, DMs, friend lists, video calling, E2E encryption, and namespace-based organization, all expressed as pub/sub addresses on a router that knows nothing about chat. The relay never needs to be updated for new features. See CHAT.md for the full architecture breakdown.
CLASP includes opt-in distributed infrastructure crates for production deployments that need authentication, persistence, automation, and multi-site operation. All features are behind Cargo feature flags and add zero overhead when disabled.
Delegatable Ed25519 capability tokens (UCAN-style) for fine-grained access control:
# Generate a root keypair
clasp key generate --out root.key
# Create a root token with full admin access
clasp token cap create --key root.key --scopes "admin:/**" --expires 30d
# Delegate with narrower scopes
clasp token cap delegate <parent-token> --key child.key --scopes "write:/lights/**"Persistent identity for devices, users, services, and routers with Ed25519 signatures:
# Generate an entity keypair
clasp token entity keygen --out sensor.key --name "Sensor A" --type device
# Mint a token
clasp token entity mint --key sensor.keyAppend-only event log for crash recovery. The router records all SET/PUBLISH operations and can replay state on restart:
let journal = Arc::new(SqliteJournal::new("state.db")?);
let router = Router::new(config).with_journal(journal);Server-side reactive automation with triggers, conditions, and transforms:
{
"id": "motion-lights",
"trigger": { "OnChange": { "pattern": "/sensors/*/motion" } },
"actions": [{ "Set": { "address": "/lights/hallway", "value": 1.0 } }],
"cooldown": { "secs": 5, "nanos": 0 }
}Programmable WASM signal transforms that run on the router. Write a filter in Rust, compile to wasm32-unknown-unknown, load it at runtime. Lenses are bidirectional (forward + inverse) and configurable via parameters.
Three bundled lenses ship with CLASP: lowpass (IIR filter), hysteresis (Schmitt trigger for debouncing), and moving-average. Custom lenses are typically 30-100KB compiled.
# Build a lens
cd lenses/lowpass
cargo build --target wasm32-unknown-unknown --release
# Validate and test it
clasp lens validate ./target/wasm32-unknown-unknown/release/lowpass.wasm
clasp lens test ./target/wasm32-unknown-unknown/release/lowpass.wasm \
--input '0.5' --params '{"alpha": 0.3}'See docs/transforms/authoring-lenses.md for the authoring guide.
Router-to-router state sharing for multi-site deployments using a hub/leaf topology:
# Hub (accepts inbound peers)
clasp server --port 7330 --features federation
# Leaf (connects to hub, owns /site-a/**)
clasp server --port 7331 \
--federation-mode leaf \
--federation-hub ws://hub:7330 \
--federation-namespaces "/site-a/**"Visit clasp.to for full documentation.
- Getting Started
- Protocol Specification
- API Reference
- DefraDB Integration | DEFRA.md
- E2E Encryption
- Examples
- Rust 1.75+
- Node.js 20+ (for desktop app)
- Platform-specific dependencies:
- Linux:
libasound2-dev,libudev-dev - macOS: Xcode Command Line Tools
- Linux:
# Clone the repository
git clone https://github.com/lumencanvas/clasp.git
cd clasp
# Build all Rust crates
cargo build --release
# Build with all distributed infrastructure features
cargo build --release --features full
# Build desktop app
cd apps/bridge
npm install
npm run buildcargo test --workspaceWe welcome contributions! Please see CONTRIBUTING.md for guidelines.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
CLASP builds on the shoulders of giants:
- Quinn - QUIC implementation
- rosc - OSC codec
- midir - MIDI I/O
- rumqttc - MQTT client
- DefraDB - P2P document database (Merkle CRDTs)
Maintained by LumenCanvas | 2026