plasmite
Persistent JSON message queues backed by plain files. No daemon, no broker, no config.
Plasmite gives you fast, crash-safe, disk-backed ring buffers ("pools") that multiple processes can read and write concurrently. Use it for IPC, event sourcing, job queues, or anywhere you'd reach for Redis or a database-backed queue but don't want to run a server.
- ~600k msg/sec append throughput (single writer, M3 MacBook)
- Lock-free, zero-copy reads via mmap
- Crash-safe writes with configurable durability
- Bounded disk usage (ring buffer — old messages overwritten when full)
- Structured JSON messages with sequence numbers, timestamps, and tags
Quick start
use ;
use json;
// Create a client (pools stored in ~/.plasmite/pools/ by default)
let client = new;
// Create a 1 MB pool
let pool_ref = name;
client.create_pool?;
let mut pool = client.open_pool?;
// Append messages with tags
let msg = pool.append_json_now?;
println!;
// Read back by sequence number
let fetched = pool.get_message?;
assert_eq!;
// Tail — stream messages as they arrive
let mut tail = pool.tail;
while let Some = tail.next_message?
Core concepts
A pool is a single .plasmite file containing a ring buffer. Messages
are appended to the head and the oldest messages are silently overwritten when
the pool is full.
Every message carries:
- seq — monotonically increasing sequence number
- time — nanosecond-precision UTC timestamp
- tags — optional string labels for filtering
- data — your JSON payload
Multiple processes can write to the same pool concurrently (serialized via OS file locks). Multiple processes can read concurrently (lock-free).
API overview
Client and pool lifecycle
use ;
let client = new;
// Or with a custom directory:
let client = new.with_pool_dir;
// Create
client.create_pool?;
// Open (returns a mutable Pool handle)
let mut pool = client.open_pool?;
// Inspect
let info = client.pool_info?;
println!;
// List all pools
let pools = client.list_pools?;
// Delete
client.delete_pool?;
Pool references resolve names to ~/.plasmite/pools/{name}.plasmite, or
you can use PoolRef::path(...) for an absolute path.
Writing messages
The PoolApiExt trait extends Pool with the message API:
use ;
use json;
// Simple append (generates timestamp for you)
let msg = pool.append_json_now?;
// With explicit options (custom timestamp)
let msg = pool.append_json?;
Durability:
Durability::Fast— buffered writes, higher throughputDurability::Flush— fsync after write, crash-safe
Reading messages
use PoolApiExt;
// By sequence number
let msg = pool.get_message?;
println!;
Tailing (streaming)
use ;
use Duration;
let mut tail = pool.tail;
while let Some = tail.next_message?
Replay
Play back messages with timing preserved:
use ;
let mut replay = pool.replay?; // 10x speed
while let Some = replay.next_message
Remote pools
Connect to a plasmite server over HTTP:
use ;
use json;
let client = new?
.with_token;
let pool = client.open_pool?;
let msg = pool.append_json_now?;
// Tail remote messages
let mut tail = pool.tail?;
while let Some = tail.next_message?
tail.cancel;
Error handling
Errors carry structured context:
use ErrorKind;
match pool.get_message
Error kinds: Internal, Usage, NotFound, AlreadyExists, Busy,
Permission, Corrupt, Io.
Pool validation
let report = client.validate_pool?;
match report.status
Lite3 (binary framing)
For high-throughput paths that skip JSON encoding:
use ;
let seq = pool.append_lite3_now?;
let frame = pool.get_lite3?;
// frame.seq, frame.timestamp_ns, frame.payload
CLI
The crate also installs the plasmite and pls CLI binaries:
Language bindings
Plasmite also has native bindings for Node.js, Python, and Go — all through the same C ABI, so pools are interoperable across languages.
License
MIT