Embed the WormDB engine directly in a native app as a single-node, in-process
key/value store — KV + WAL + snapshot persistence, std.crypto (no libsodium),
no socket server, no clustering. This is "Path A" from
meshguard#102: once libsodium
became optional, the engine cross-compiles to mobile/ARM as a plain libc library.
| file | role |
|---|---|
src/ffi.zig |
the C-ABI surface (export fn wormdb_*) over Store |
ffi/wormdb.h |
C header for any caller (Swift bridging header / JNI) |
ffi/swift/ |
idiomatic Swift wrapper + runnable PoC (iOS-native language) |
ffi/android/ |
JNI bridge (wormdb_jni.c) + Kotlin WormDB.kt |
zig build produces zig-out/lib/libwormdb_ffi.{dylib,so} (and a static .a on
iOS, since apps cannot dlopen user dylibs).
wormdb_Db *wormdb_open(const char *dir, int persistence); // WORMDB_PERSIST_{FULL,SNAPSHOT,NONE}
void wormdb_close(wormdb_Db *db);
int wormdb_set(wormdb_Db*, const u8 *key, size_t, const u8 *val, size_t);
int wormdb_set_worm(...); // write-once entry
int wormdb_get(wormdb_Db*, const u8 *key, size_t, u8 **out, size_t *out_len); // free with wormdb_free
int wormdb_get_meta(wormdb_Db*, const u8 *key, size_t, wormdb_EntryMeta *out);
int wormdb_scan_prefix(wormdb_Db*, const u8 *prefix, size_t, size_t limit, void *ctx, wormdb_scan_callback);
int wormdb_append_log(wormdb_Db*, const u8 *log_id, size_t, const u8 *payload, size_t,
const u8 *attachment_hashes, size_t attachment_hash_count,
uint64_t ingest_time_ms, wormdb_AppendReceipt *out);
int wormdb_append_log_verify(wormdb_Db*, const u8 *log_id, size_t, wormdb_AppendLogReport *out);
int wormdb_proof_build_mmr_bundle(wormdb_Db*, const u8 *log_id, size_t,
uint64_t from_seq, uint64_t to_seq,
const u8 public_key[32], const u8 secret_key[64],
uint64_t created_at_ms, uint64_t ingested_at_ms,
const u8 *checkpoint_ext, size_t checkpoint_ext_len,
const u8 *bundle_ext, size_t bundle_ext_len,
u8 **out_bundle, size_t *out_len,
wormdb_ProofBundleInfo *out_info);
int wormdb_proof_verify_bundle(const u8 *bundle, size_t, wormdb_ProofBundleInfo *out_info);
int wormdb_mmr_proof_verify(const u8 *proof, size_t, uint64_t seq,
const u8 leaf_hash[32], const u8 expected_root[32]);
int wormdb_delete(wormdb_Db*, const u8 *key, size_t);
void wormdb_free(u8 *ptr, size_t len);Values from wormdb_get are library-owned — release with wormdb_free. The store
is internally sharded with per-shard locks, so a handle is safe to share across
threads.
wormdb_get_meta returns a caller-owned metadata receipt for a single key:
WormDB's local timestamp_ms, is_worm, value_len, and SHA-256(value).
wormdb_scan_prefix scans keys by prefix in lexicographic key order. The callback
receives borrowed key/value pointers plus the same metadata receipt shape; copy
anything that must survive after the callback returns. limit == 0 means no
limit, while a positive limit returns the lexicographic tail to match the engine's
existing scanPrefix semantics.
The proof calls expose the WORM append-log and MMR proof spine to native apps:
append opaque payload bytes to a named immutable log, verify the local chain,
build one encoded proof bundle for a sequence range, and verify that bundle on
another device without opening a database. Encoded bundles and wormdb_get
buffers are released with wormdb_free.
bash ffi/swift/run.shBuilds the dylib, compiles ffi/swift/WormDB.swift +
main.swift against the bridging header, and runs a demo that
sets/gets string + binary values, exercises WORM rejection, deletes, and reopens
to prove persistence (WAL replay). This is the same WormDB class an iOS app
would ship.
The Zig source is portable to aarch64-ios; only libc/SDK wiring is needed (Zig
stops at "unable to find libSystem" without the SDK sysroot). Produce a static lib
per slice and bundle as an .xcframework:
# device (arm64) + simulator (arm64); add x86_64 sim if you need Intel Macs
zig build -Dtarget=aarch64-ios -Dno-sodium=true \
--sysroot "$(xcrun --sdk iphoneos --show-sdk-path)"
zig build -Dtarget=aarch64-ios-simulator -Dno-sodium=true \
--sysroot "$(xcrun --sdk iphonesimulator --show-sdk-path)"
# -> libwormdb_ffi.a per slice; xcodebuild -create-xcframework ...In Xcode: add the .xcframework, set ffi/wormdb.h as the bridging header, drop
in WormDB.swift. Open with context paths under the app sandbox, e.g.
FileManager.default.urls(for: .applicationSupportDirectory, ...).
Cross-compile the engine .so per ABI, then build the JNI bridge .so with the
NDK, and place both in jniLibs/<abi>/:
# 1. engine: needs an NDK libc file (see deps/meshguard/android-aarch64-libc.conf
# as a template; point it at your $ANDROID_NDK_HOME sysroot)
zig build -Dtarget=aarch64-linux-android -Dno-sodium=true --libc android-aarch64-libc.conf
# -> libwormdb_ffi.so
# 2. JNI bridge (CMake/ndk-build), linking libwormdb_ffi + ffi/wormdb.h:
# $NDK/.../clang --target=aarch64-linux-android21 -shared \
# ffi/android/wormdb_jni.c -I ffi -Iffi -L<engine.so dir> -lwormdb_ffi \
# -o libwormdb_jni.soAdd WormDB.kt (package dev.wormdb) to your sources; it
System.loadLibrarys both .sos. Open under context.filesDir.
- ✅ Swift PoC builds + runs on macOS: set/get (string + 256-byte binary), WORM rejection, delete, and persistence across reopen via WAL replay.
- ✅
wormdb_jni.ccompiles against the JDK'sjni.h(JNI ABI correct). - ✅
zig build testruns FFI tests for append-log writes, encoded MMR bundle construction/verification, and direct MMR proof-byte verification. - ✅ FFI library cross-compiles to
aarch64-linux-musl(ARM, static-friendly, no libsodium) — same class of build as Android/iOS, which additionally need their NDK/SDK libc.
Not included: clustering / multi-device replication ("Path B") — that layer is Linux-only today.