WeaveDocs
Forum

Usage with the SDK

Driving Forum through WeaveNode — the recommended entry point for most applications.

Why use the SDK

The weave-sdk crate exposes Forum through a namespaced API on WeaveNode. The SDK handles the Strand lifecycle, replication wiring, and identity binding. You write, read, take, and the SDK does the rest.

Open, write, take

use weave_sdk::prelude::*;

#[tokio::main]
async fn main() -> WeaveResult<()> {
    let node = WeaveNode::builder()
        .namespace("l1fe")
        .identifier("queue-runner")
        .storage_dir("/tmp/weave-queue")
        .build()
        .await?;

    node.open_forum("jobs").await?;

    // Producer.
    node.forum_write("jobs", b"render frame 42".to_vec()).await?;
    node.forum_write("jobs", b"render frame 43".to_vec()).await?;

    // Peek (does not consume).
    let peek = node.forum_read("jobs", b"render frame 42").await?;
    assert_eq!(peek.as_deref(), Some(b"render frame 42".as_slice()));

    // Consume.
    let taken = node.forum_take("jobs", b"render frame 42").await?;
    assert_eq!(taken.as_deref(), Some(b"render frame 42".as_slice()));

    // The take is durable — restart the process and the tuple is gone.
    let gone = node.forum_take("jobs", b"render frame 42").await?;
    assert!(gone.is_none());

    Ok(())
}

SDK surface for Forum

CallReturnsNotes
node.open_forum(name)WeaveResult<()>Idempotent; first call creates the strand
node.forum_write(name, tuple)WeaveResult<u64>Returns the EntryId of the written tuple
node.forum_read(name, pattern)WeaveResult<Option<Vec<u8>>>Non-consuming match
node.forum_take(name, pattern)WeaveResult<Option<Vec<u8>>>Atomic consume
node.forums()&Arc<RwLock<ForumStore>>Direct access for advanced flows

A worker pool

use weave_sdk::prelude::*;
use std::time::Duration;

async fn worker(node: WeaveNode, pattern: &'static [u8]) -> WeaveResult<()> {
    loop {
        match node.forum_take("jobs", pattern).await? {
            Some(payload) => {
                tracing::info!("got {} bytes", payload.len());
                process(&payload).await;
            }
            None => tokio::time::sleep(Duration::from_millis(50)).await,
        }
    }
}

async fn process(_p: &[u8]) {
    // your job logic
}

Errors and recovery

SurfaceErrorWhat to do
open_forum on existing nameReturns existing handleNo action
forum_write with disk fullWeaveError::Storage from StrandFree disk, retry; previous state is consistent
forum_take returning NoneNo matching live tupleBackoff and poll
Process crash mid-takelive_tuples may have removed the entry without the tombstone landing in the strandOn restart, the Forum rebuilds live_tuples from the strand, including the dropped entry — your worker may see the tuple again. Make handlers idempotent.

Idempotent consumption pattern

Pair each tuple with a Lens-stored claim before doing irreversible work:

use weave_sdk::prelude::*;

async fn consume_idempotent(node: &WeaveNode, tuple: Vec<u8>) -> WeaveResult<()> {
    let claim_key = blake3::hash(&tuple).as_bytes().to_vec();

    if node.lens_get("processed", &claim_key).await?.is_some() {
        return Ok(()); // already handled
    }

    // Take the tuple from the forum.
    if let Some(payload) = node.forum_take("jobs", &tuple).await? {
        do_work(&payload).await;
        node.lens_put("processed", &claim_key, b"1").await?;
    }

    Ok(())
}

async fn do_work(_p: &[u8]) { /* ... */ }

This is the recommended shape for cross-peer or crash-tolerant consumers; see Distributed Coordination for the broader pattern.