Rust SDK for MemWal.
This crate provides:
MemWalfor relayer-managed remember / recall / analyze / restore flows.MemWal::provisionfor account reuse/creation plus delegate-key registration.MemWalManualfor client-side embedding, Seal encryption, Walrus retrieval, and local decrypt.AccountClientfor on-chain MemWal account and delegate-key management.
Use MemWal::provision when you want the SDK to resolve relayer config, reuse or create the
MemWal account, register the delegate key, and return a ready client.
use std::time::Duration;
use memwal_core::MemWal;
use memwal_core::MemWalProvisionConfig;
use memwal_core::RecallParams;
#[tokio::main]
async fn main() -> Result<(), memwal_core::MemWalError> {
let provisioned = MemWal::provision(
MemWalProvisionConfig::new("suiprivkey...")
.registry_id("0x...")
.delegate_label("my-app"),
)
.await?;
let memwal = provisioned.memwal();
let remembered = memwal
.remember(
"User prefers dark mode and uses Rust.",
Duration::from_millis(1500),
Duration::from_secs(60),
)
.await?;
let recalled = memwal
.recall(RecallParams {
query: "What does the user prefer?".to_owned(),
limit: Some(5),
namespace: None,
top_k: None,
max_distance: None,
})
.await?;
println!("stored memory: {}", remembered.id);
println!("matches: {}", recalled.results.len());
let revoke_digest = provisioned.revoke_delegate_key().await?;
println!("revoked delegate key: {revoke_digest}");
Ok(())
}MemWalProvisionConfig accepts plain strings at the public API boundary:
new(delegate_suiprivkey)sets the delegate key used for relayer requests.wallet_suiprivkey(...)optionally sets a separate Sui wallet key for on-chain transactions.account_id(...)reuses an explicit MemWal account and registers the delegate key.registry_id(...)enables registry lookup and account creation/reuse when no account ID is set.server_url(...)andrelayer_config_url(...)override relayer discovery.namespace(...)sets the default memory namespace.delegate_label(...)sets the on-chain delegate-key label.
The basic SDK flow does not require callers to depend on sui-sdk-types or construct Sui clients.
The demo app maps environment variables into MemWalProvisionConfig, then runs the full lifecycle:
provision or reuse an account, register the delegate key, store memory, recall memory, and revoke
the delegate key.
Required:
export MEMWAL_PRIVATE_KEY=suiprivkey...Then choose one account path:
# Reuse or create through the registry.
export MEMWAL_REGISTRY_ID=0x...
# Or use an explicit existing account.
export MEMWAL_ACCOUNT_ID=0x...Run:
cargo run -p memwal-demo --bin memwal-demoOptional demo variables:
MEMWAL_WALLET_KEY: separate wallet key for on-chain provisioning.MEMWAL_SERVER_URLorMEMWAL_RELAYER_URL: relayer base URL.MEMWAL_RELAYER_CONFIG_URLorMEMWAL_RELAYER_CONFIG_PATH: explicit relayer config URL.MEMWAL_NAMESPACE: default namespace for memory operations.
Use AccountClient directly when you need fine-grained transaction control. provision_account
accepts a registry ID string and a ProvisionAccountMode:
use memwal_core::AccountClient;
use memwal_core::ProvisionAccountMode;
async fn example(
account_client: AccountClient,
delegate_public_key: [u8; 32],
) -> Result<(), memwal_core::MemWalError> {
let account_id = account_client
.provision_account(
"0x...",
ProvisionAccountMode::ReuseOrCreate,
delegate_public_key,
"my-app",
)
.await?;
Ok(())
}If you already have a delegate key and account ID, construct MemWal directly with MemWalConfig.
This bypasses provisioning but keeps the relayer API ergonomic.
use std::time::Duration;
use memwal_core::DelegateKey;
use memwal_core::MemWal;
use memwal_core::MemWalConfig;
use memwal_core::RecallParams;
async fn example(account_id: sui_sdk_types::Address) -> Result<(), memwal_core::MemWalError> {
let delegate_key = DelegateKey::from_suiprivkey("suiprivkey...")?;
let memwal = MemWal::new(
MemWalConfig::new(
delegate_key,
account_id,
Some("https://relayer.memory.walrus.xyz".to_owned()),
Some("default".to_owned()),
),
)
.await?;
let remembered = memwal
.remember(
"User prefers dark mode.",
Duration::from_millis(1500),
Duration::from_secs(60),
)
.await?;
let recalled = memwal
.recall(RecallParams {
query: "What does the user prefer?".to_owned(),
limit: Some(5),
namespace: None,
top_k: None,
max_distance: None,
})
.await?;
println!("stored memory: {}", remembered.id);
println!("matches: {}", recalled.results.len());
Ok(())
}Common MemWal methods:
remember_async(text)submits a memory write and returns a job ID.remember(text, poll_interval, timeout)submits and waits for completion.remember_bulk(items)andremember_bulk_and_wait(items, poll_interval, timeout)handle batch writes.recall(params)searches memory.analyze(options)andanalyze_and_wait(options, poll_interval, timeout)run relayer analysis jobs.restore(blob_id)restores a memory blob.embed(text)returns relayer embeddings.remember_manual(...)andrecall_manual(...)expose manual encryption/retrieval flows.health()andcompatibility()inspect relayer status.delegate_public_key_hex()returns the delegate public key used for request signing.
For strict security requirements, the MemWalManual client allows you to handle embeddings, encryption, and storage locally on your client machine before ever touching the relayer. The relayer will never see your plaintext data.
use memwal_core::manual::{
MemWalManual, MemWalManualConfig, OllamaEmbeddingProvider, SuiNetwork, WalrusHttpStore,
};
use memwal_core::sui::Ed25519Signer;
use memwal_core::types::ManualRecallOptions;
use std::sync::Arc;
async fn manual_example(
delegate_suiprivkey: &str,
account_id: sui_sdk_types::Address,
) -> Result<(), Box<dyn std::error::Error>> {
let signer = Arc::new(Ed25519Signer::from_suiprivkey(delegate_suiprivkey)?);
let embedder = Arc::new(OllamaEmbeddingProvider::new("nomic-embed-text-v2-moe"));
let store = Arc::new(WalrusHttpStore::new("https://publisher.walrus-testnet.walrus.space", "https://aggregator.walrus-testnet.walrus.space"));
let config = MemWalManualConfig::new(
account_id,
"0x...package_id...",
SuiNetwork::Testnet,
signer,
embedder,
store,
).with_relayer_url("https://relayer.memory.walrus.xyz");
let memwal = MemWalManual::new(config);
// 1. Embeds text locally, encrypts it with Seal, uploads to Walrus, then registers with Relayer
let remembered = memwal.remember_manual("Top secret local text.", Some("default")).await?;
println!("Stored secure memory: {}", remembered.blob_id);
// 2. Searches relayer, downloads encrypted blob from Walrus, decrypts it locally with Seal
let recalled = memwal.recall_manual(ManualRecallOptions {
query: "What is secret?".to_owned(),
namespace: Some("default".to_owned()),
..Default::default()
}).await?;
println!("Matches: {}", recalled.results.len());
Ok(())
}Note: The OllamaEmbeddingProvider is great for testing this zero-trust workflow locally. If pointing it at the public MemWal relayer, it handles automatically padding your local model's embeddings to the required 1536-dimensional size so the transaction succeeds.
Licensed under the Apache License, Version 2.0. See LICENSE.