VSS Rust Client FFI
February 11, 2026 ยท View on GitHub
Cross-platform FFI bindings for the VSS (Versioned Storage Service) Rust Client, providing a simple interface for mobile applications to interact with VSS servers.
Installation
Prerequisites
- Rust 1.75+
uniffi-bindgenfor generating bindings
Building
# Basic build
cargo build --release
# Generate ALL bindings
./build.sh all
# Generate Swift bindings for iOS
./build_ios.sh
# Generate Kotlin bindings for Android
./build_android.sh
# Generate Python bindings
./build_python.sh
Usage Examples
Swift (iOS)
import VssRustClientFfi
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
let passphrase: String? = nil
let storeId = try vssDeriveStoreId(
prefix: "bitkit_v1_regtest",
mnemonic: mnemonic,
passphrase: passphrase
)
try await vssNewClientWithLnurlAuth(
baseUrl: "https://vss.example.com",
storeId: storeId,
mnemonic: mnemonic,
passphrase: passphrase,
lnurlAuthServerUrl: "https://auth.example.com/lnurl"
)
// Store data
let item = try await vssStore(
key: "user-settings",
value: "{'theme': 'dark'}".data(using: .utf8)!
)
print("Stored at version: \(item.version)")
// Retrieve data
if let item = try await vssGet(key: "user-settings") {
let data = String(data: item.value, encoding: .utf8)
print("Retrieved: \(data)")
}
// List all items with prefix
let items = try await vssList(prefix: "user/")
for item in items {
print("Key: \(item.key), Version: \(item.version)")
}
// Delete data
let wasDeleted = try await vssDelete(key: "user-settings")
print("Deleted: \(wasDeleted)")
// Clean shutdown (optional)
vssShutdownClient()
Dedicated LDK Client (Swift)
For fully isolated LDK operations with independent key derivation (full 64-byte BIP39 seed):
// Initialize the dedicated LDK client (separate from the app client)
try await vssNewLdkClientWithLnurlAuth(
baseUrl: "https://vss.example.com",
storeId: storeId,
mnemonic: mnemonic,
passphrase: passphrase,
lnurlAuthServerUrl: "https://auth.example.com/lnurl"
)
// Read an ldk-node key
if let item = try await vssLdkGet(key: "network_graph", namespace: .default) {
print("Graph size: \(item.value.count) bytes")
}
// Store a key
let item = try await vssLdkStore(
key: "network_graph",
value: graphData,
namespace: .default
)
// Delete a key
let wasDeleted = try await vssLdkDelete(key: "network_graph", namespace: .default)
// List keys in a namespace
let keys = try await vssLdkListKeys(namespace: .monitors)
// List all keys across all namespaces
let allKeys = try await vssLdkListAllKeys()
// Clean shutdown
vssShutdownLdkClient()
Python
from vss_rust_client_ffi import *
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
passphrase = None
store_id = vss_derive_store_id(
prefix="bitkit_v1_regtest",
mnemonic=mnemonic,
passphrase=passphrase
)
await vss_new_client_with_lnurl_auth(
"https://vss.example.com",
store_id,
mnemonic,
passphrase,
"https://auth.example.com/lnurl"
)
# Store data
item = await vss_store("user-settings", b"{'theme': 'dark'}")
print(f"Stored at version: {item.version}")
# Retrieve data
item = await vss_get("user-settings")
if item:
print(f"Retrieved: {item.value.decode()}")
# List keys only (more efficient)
keys = await vss_list_keys("user/")
for kv in keys:
print(f"Key: {kv.key}, Version: {kv.version}")
# Batch store multiple items
items_to_store = [
KeyValue(key="config/theme", value=b"dark"),
KeyValue(key="config/lang", value=b"en")
]
stored_items = await vss_put_with_key_prefix(items_to_store)
print(f"Stored {len(stored_items)} items")
# Delete data
was_deleted = await vss_delete("user-settings")
print(f"Deleted: {was_deleted}")
# Clean shutdown (optional)
vss_shutdown_client()
Dedicated LDK Client (Python)
# Initialize the dedicated LDK client (separate from the app client)
await vss_new_ldk_client_with_lnurl_auth(
"https://vss.example.com",
store_id,
mnemonic,
passphrase,
"https://auth.example.com/lnurl"
)
# Read an ldk-node key
item = await vss_ldk_get("network_graph", LdkNamespace.DEFAULT)
# Store a key
item = await vss_ldk_store("network_graph", graph_data, LdkNamespace.DEFAULT)
# Delete a key
was_deleted = await vss_ldk_delete("network_graph", LdkNamespace.DEFAULT)
# List keys in a namespace
keys = await vss_ldk_list_keys(LdkNamespace.MONITORS)
# List all keys across all namespaces
all_keys = await vss_ldk_list_all_keys()
# Clean shutdown
vss_shutdown_ldk_client()
Kotlin (Android)
import uniffi.vss_rust_client_ffi.*
val mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
val passphrase: String? = null
val storeId = vssDeriveStoreId(
prefix = "bitkit_v1_regtest",
mnemonic = mnemonic,
passphrase = passphrase
)
vssNewClientWithLnurlAuth(
baseUrl = "https://vss.example.com",
storeId = storeId,
mnemonic = mnemonic,
passphrase = passphrase,
lnurlAuthServerUrl = "https://auth.example.com/lnurl"
)
// Store data
val item = vssStore(
key = "user-settings",
value = "{'theme': 'dark'}".toByteArray()
)
println("Stored at version: ${item.version}")
// Retrieve data
val retrievedItem = vssGet("user-settings")
retrievedItem?.let {
println("Retrieved: ${String(it.value)}")
}
// List all items
val items = vssList(prefix = null)
items.forEach { item ->
println("Key: ${item.key}, Version: ${item.version}")
}
// Clean shutdown (optional)
vssShutdownClient()
Dedicated LDK Client (Kotlin)
// Initialize the dedicated LDK client (separate from the app client)
vssNewLdkClientWithLnurlAuth(
baseUrl = "https://vss.example.com",
storeId = storeId,
mnemonic = mnemonic,
passphrase = passphrase,
lnurlAuthServerUrl = "https://auth.example.com/lnurl"
)
// Read an ldk-node key
val item = vssLdkGet(key = "network_graph", namespace = LdkNamespace.Default)
item?.let { println("Graph size: ${it.value.size} bytes") }
// Store a key
val stored = vssLdkStore(
key = "network_graph",
value = graphData,
namespace = LdkNamespace.Default
)
// Delete a key
val wasDeleted = vssLdkDelete(key = "network_graph", namespace = LdkNamespace.Default)
// List keys in a namespace
val keys = vssLdkListKeys(namespace = LdkNamespace.Monitors)
// List all keys across all namespaces
val allKeys = vssLdkListAllKeys()
// Clean shutdown
vssShutdownLdkClient()
API Reference
Client Management
vssNewClient(baseUrl: String, storeId: String) -> Void
Initialize the global VSS client connection without authentication.
baseUrl: VSS server URL (e.g., "https://vss.example.com")storeId: Unique identifier for your storage namespace
vssNewClientWithLnurlAuth(baseUrl: String, storeId: String, mnemonic: String, passphrase: String?, lnurlAuthServerUrl: String) -> Void
Initialize the global VSS client connection with LNURL-auth authentication. Provides automatic JWT token management and data encryption.
baseUrl: VSS server URL (e.g., "https://vss.example.com")storeId: Unique identifier for your storage namespacemnemonic: BIP39 mnemonic phrase (12 or 24 words)passphrase: Optional BIP39 passphrase (passnullif none)lnurlAuthServerUrl: LNURL-auth server URL for authentication
vssShutdownClient() -> Void
Shutdown the VSS client and clean up resources. Optional but recommended for clean application shutdown.
Utility Functions
vssDeriveStoreId(prefix: String, mnemonic: String, passphrase: String?) -> String
Derives a deterministic VSS store ID from a mnemonic and optional passphrase using BIP32 key derivation.
prefix: A prefix to include in the store ID (e.g., "bitkit_v1_regtest")mnemonic: BIP39 mnemonic phrase (12 or 24 words)passphrase: Optional BIP39 passphrase
Data Operations
vssStore(key: String, value: Data) -> VssItem
Store a key-value pair. The server automatically manages versioning, incrementing the version number with each update.
vssGet(key: String) -> VssItem?
Retrieve an item by key. Returns null if not found.
vssList(prefix: String?) -> [VssItem]
List all items, optionally filtered by key prefix. Includes full data.
vssListKeys(prefix: String?) -> [KeyVersion]
List keys and versions only (more efficient than vssList).
vssPutWithKeyPrefix(items: [KeyValue]) -> [VssItem]
Store multiple items in a single atomic transaction. The server manages versioning for all items.
vssDelete(key: String) -> Bool
Delete an item. Returns true if item existed and was deleted.
Dedicated LDK Client
A fully separate client (LdkVssClient) with its own key derivation using the full 64-byte BIP39 seed, independent from the app backup client. Use this when LDK operations must be completely isolated from app backup operations.
vssNewLdkClientWithLnurlAuth(baseUrl: String, storeId: String, mnemonic: String, passphrase: String?, lnurlAuthServerUrl: String) -> Void
Initialize the dedicated LDK client with LNURL-auth authentication. This client is fully separate from the app client initialized with vssNewClientWithLnurlAuth.
vssShutdownLdkClient() -> Void
Shutdown the dedicated LDK client and clean up resources.
vssLdkStore(key: String, value: Data, namespace: LdkNamespace) -> VssItem
Store a key-value pair using the dedicated LDK client.
vssLdkGet(key: String, namespace: LdkNamespace) -> VssItem?
Retrieve an item by key using the dedicated LDK client. Returns null if not found.
vssLdkDelete(key: String, namespace: LdkNamespace) -> Bool
Delete an item using the dedicated LDK client. Returns true if item existed and was deleted.
vssLdkListKeys(namespace: LdkNamespace) -> [KeyVersion]
List keys in a namespace using the dedicated LDK client.
vssLdkListAllKeys() -> [KeyVersion]
List all keys across all singleton LDK namespaces using the dedicated LDK client.
Data Types
VssItem
key: String- The item keyvalue: Data- The stored dataversion: Int64- Version number
KeyValue
key: String- The item keyvalue: Data- The data to store
KeyVersion
key: String- The item keyversion: Int64- Version number
LdkNamespace
Default- Default namespace (channel_manager, scorer, etc.)Monitors- Channel monitorsMonitorUpdates(monitorId)- Updates for a specific monitorArchivedMonitors- Archived channel monitors
VssError
Error enum with detailed error information for different failure scenarios.
Building from Source
iOS Framework
./build_ios.sh
Generates:
bindings/ios/VssRustClientFfi.xcframework- iOS frameworkbindings/ios/vss_rust_client_ffi.swift- Swift bindings
Android Library
./build_android.sh
Generates:
bindings/android/vss_rust_client_ffi.kt- Kotlin bindingsbindings/android/jniLibs/- Native libraries for all Android architectures
Python Package
./build_python.sh
Generates:
bindings/python/- Python package with bindings and native library
Development
Prerequisites
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install uniffi-bindgen
cargo install uniffi_bindgen
Running Tests
# Run unit tests (recommended)
cargo test tests::tests --lib
# Check compilation
cargo check
# Build library
cargo build --release
# Test bindings generation
cargo run --bin uniffi-bindgen generate \
--library ./target/release/libvss_rust_client_ffi.dylib \
--language swift \
--out-dir ./test_bindings
Architecture
This library provides a thin FFI wrapper around the vss-client-ng Rust library, exposing a simplified async API suitable for mobile and cross-platform applications.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Run
cargo testandcargo clippy - Submit a pull request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Projects
- VSS Server - The VSS server implementation
- vss-client-ng - The underlying Rust client library
- UniFFI - The FFI binding generator