Numbers, API names, feature defaults and PR references checked against CONFORMANCE.md, BENCHMARKS.md, CHANGELOG.md, the code and git history. - int8 index figures (1.74x memory, 1.63x QPS) carry the dates git gives them (2026-09-19/20, machine not recorded, not re-run) instead of none; the Pi 5 1.18x carries 2026-09-21. - BENCHMARKS headline: the libhdf5 chunked-write figure is the newest measurement (35x, 2026-09-23), not 45.3x (2026-08-03). - Conformance counts follow the 2026-09-28 run (1 our-error, 2 ref-bug) in conformance/README.md, ROADMAP.md and CLAUDE.md, with a pointer to the bad_nbit_parms_walk.h5 flip. - README: LZ4 is opt-in; the browser refuses reference/opaque/bitfield/ time datasets too; zlib-rs byte-identity scoped to what was measured; macOS default links the system libz for inflate. - Crate READMEs: system-zlib-decompress does something (macOS), SweepDetector lives in prefetch, checkpoint after more than 500 WAL entries, NetCDF-4 unlimited-dimension size warning. - agent-memory.md: string-dataset compression threshold, agents-md prints Markdown, float16 file sizes linked to their study. - known-issues.md: contiguous selection reads, 1.21x vs h5py threads. - docs/README.md, USE_CASES.md, ROADMAP.md, CLAUDE.md: range-read milestones M0-M5 and PRs #17-#19, missing README rows, CLI keygen/verify, dated figures, fast-math is not BLAS. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
16 KiB
clawhdf5
Purpose
Pure-Rust HDF5 implementation (read, write, in-place edit, remote and browser
reads) plus agent memory on top of it: HNSW vector search, a WAL-backed store,
and GPU vector distances. A standalone library. Its one verified consumer is
ClawBrainHub (.brain files); no agent framework integrates it (see
Standing rules).
Architecture
Cargo workspace, 19 crates under crates/ (plus libaec-sys, the FFI crate
behind the optional szip feature). MSRV 1.92 (rust-version, checked in CI).
| Crate | Role |
|---|---|
clawhdf5-format |
The HDF5 format: parsers and writer (superblock, headers, B-trees, heaps, chunk indexes), the Storage trait, the filter pipeline and registry (filter_registry), every codec except deflate (LZ4, Zstd, SZIP, N-Bit, scale-offset, pcodec; pure-Rust LZF, bitshuffle, bzip2, Blosc 1; Blosc2 and ZFP read-only), float16, checksum |
clawhdf5-filters |
Deflate backends (zlib-rs default, zlib-ng, Apple Compression) |
clawhdf5-io |
I/O adapters (buffers, mmap, prefetch) |
clawhdf5-derive |
#[derive(H5Type)] for compound types |
clawhdf5 |
Facade: File, FileBuilder, Dataset, FileEditor (src/edit/), SWMR reading (src/swmr.rs) |
clawhdf5-netcdf4 |
NetCDF-4 read support |
clawhdf5-remote |
open_url: HTTP(S) range requests and object stores (S3, GCS, Azure) through BlockCache |
clawhdf5-tools |
h5rs: ls, dump (DDL / hdf5-json), stat, diff, check |
clawhdf5-py |
PyO3 bindings (h5py-like API, remote files, 'r+' editing) |
clawhdf5-wasm |
wasm-bindgen browser reader (open(bytes), openUrl(url)); demo in examples/wasm-viewer/ |
clawhdf5-ann |
HNSW index |
clawhdf5-agent |
Agent memory store (HDF5Memory), sessions, knowledge graph, BM25 |
clawhdf5-accel |
CPU SIMD kernels (AVX2, NEON) |
clawhdf5-gpu |
wgpu vector distances (WGSL) — not dataset I/O; HDF5 I/O is CPU-only |
clawhdf5-migrate |
SQLite → agent store migration |
clawhdf5-cli |
Agent-memory CLI |
clawhdf5-napi |
Node.js addon (the packages/clawhdf5-node wrapper is broken; docs/known-issues.md) |
clawhdf5-android |
Android JNI bindings |
clawhdf5-bench |
Benchmarks and harnesses (search_harness, read_harness, concurrent_read, longmemeval_bench, …) |
Reference docs: docs/known-issues.md (open issues table first — check it
before calling something a bug or a feature), BENCHMARKS.md (headline
numbers first), CONFORMANCE.md (generated), docs/design/range-reads.md
and docs/design/swmr.md, CHANGELOG.md (full detail of every fix).
Standing rules
- No C in the default build. No libhdf5; deflate defaults to pure-Rust
zlib-rs (
fast-deflateopts into zlib-ng, which needs cmake).ci-test.shfails if a C-building crate enters the core crates' default tree. Zstd, SZIP,https(ring) ands3/gcs/azure(aws-lc-rs) are opt-in. flate2 must keepruntime_detectionwith zlib-rs — without it zlib-rs loses SIMD and inflates 3.5x slower. - Every file we write must open in h5py/libhdf5. Interop tests compare
against h5py and h5dump;
f32and empty datasets did not open until 2026-09-23. - float16 has one implementation:
clawhdf5_format::float16. - Claims need evidence. Performance and integration claims in docs must be measured, dated (with machine and command), or withdrawn. Benchmark numbers are dated records: never edit a measured value, add a new dated section and mark the old one superseded.
- OpenClaw is not supported (decided 2026-09-25): clawhdf5 is not and
never was an OpenClaw memory plugin; the old
memory.backend = "clawhdf5"config was never valid.docs/openclaw.mdrecords what a real plugin would need. Theopenclawmodule'sClawhdfBackendis justsearchwith re-rank + confidence on. - ZeroClaw does not use clawhdf5 (checked 2026-09-25 against upstream
v0.8.5 and the
osobh/zeroclawfork and their history): its memory backends are its own;clawhdf5-migrate's default SQLite layout is not ZeroClaw's schema. Don't reintroduce integration claims without an integration and a test against the real consumer. - known-issues.md: one entry per bug; when fixed, record it in
CHANGELOG.mdand move the entry to Fixed (history) with date, PR, affected releases and what users must do — never delete it.
HDF5 library: invariants and gotchas
- Remote/range reads (
docs/design/range-reads.md, M0-M5 merged in PRs #17-#19, M4 listing costs cut in #21): every format-crate read path goes throughStorage(read_at/read_ranges/hint).File::open_storagetakes anyStorage;clawhdf5_remote::open_urlwraps HTTP (HttpStorage, ureq) orObjectStoreStorageinBlockCache(1 MiB blocks, LRU budget, in-flight dedup, coalesced runs). Remote files are pinned by ETag/Last-Modified and length (RemoteError::FileChanged). Zero-copy APIs andFile::as_bytesneed an in-memory file. Parse throughFile::storage()and the*_infunctions, notas_bytes, in new code (the Python bindings do).ObjectStoreStorageruns reads on its own small tokio runtime, so it works from any thread. - SWMR (
docs/design/swmr.md):File::open_swmrreads a file a libhdf5 SWMR writer is appending to — positioned reads, no chunk cache, bounded retries (100),Dataset::refresh(). clawhdf5 has no SWMR writer; remote SWMR is out of scope. - Browser (
clawhdf5-wasm, read-only, no Zstd/SZIP):openUrlreads through the restartable "NeedBytes" cache (src/lazy.rs: a call is re-run after each wave of misses; no block is evicted while a call runs); the HTTP is JavaScript (js/remote.js). - In-place editing (
clawhdf5::FileEditor): overwrites values, grows and shrinks chunked datasets (every chunk index) and sets attributes (compact and dense) without rewriting the file, changing indexes and heaps as libhdf5 does; freed space is reused within one editor. Anything it cannot do safely isError::Unsupportedbefore any write (limits indocs/known-issues.md). The algorithms follow libhdf5hdf5_1_14_6(github.com/HDFGroup/hdf5). Test changes withcargo test -p clawhdf5-tools --test edit_interop --test edit_coverage_interop. - Provenance:
Dataset::verify_provenance()(facadeprovenancefeature, default) re-hashes a dataset against its_provenance_sha256attribute (DatasetBuilder::with_provenance). Opt-in per call; unkeyed hash — tamper-evident, not tamper-proof.
Agent memory: invariants and gotchas
- Search.
HDF5Memory::search(query_emb, text, &SearchOptions)is the full path: optional source-channel filter (before ranking; exact scan of the allowed records when cheaper thanpool × Mindex distance evaluations, and as the fallback when the pool comes back short), fusion, activation scaling, optional re-ranking and confidence rejection.hybrid_search/hybrid_search_withare thin wrappers. It keeps one incremental BM25 index for the life of the store and never writes the store: Hebbian activation boosts are persisted by the next checkpoint (or on drop). - HNSW (
hnswfeature, default): the approximateclawhdf5-annindex mirrors the cache and self-heals on drift; build the agent with--no-default-features --features float16for the exact linear scan.parallel(default) builds it on a thread pool with an identical graph. Neighbour selection uses the HNSW paper's diversity heuristic (closest-M capped recall at 0.31 recall@10 at 100K on clustered data). The graph is saved to<store>.h5.annat each checkpoint, tied to it by a generation id; a stale or damaged sidecar is ignored and the index rebuilt. MemoryConfig::quantized_index(default on for new stores, persisted; older stores load asfalse— guarded bytests/fixtures/store_v2_5_0.h5; CLIcreate --f32-index): the index's copy of the embeddings isi8, and the query path re-scores candidates against the exact embeddings. The aarch64 kernels (clawhdf5_accel::dot_i8, NEONSDOTvia inline asm) arecfg'd out on x86, so x86 CI never compiles them — test on real ARM (rpivision02, 10.0.2.3, a Pi 5) or rely on thetest-arm64job.MemoryConfig::float16(default on for new stores, persisted; older stores keepfalse— guarded intests/float16_store.rs; CLIcreate --f32):/memory/embeddingsis IEEE half.MemoryCache::half_precisionrounds each embedding as it enters the cache (push, update, WAL replay, and load of a store stillf32on disk) so memory and file agree bit for bit. Values beyond ±65504 areMemoryError::InvalidEntry. The agent'sh5py_interoptest guards that a whole store opens in h5py.- WAL. Chained CRC32 per entry (a corrupted, reordered, duplicated or
spliced entry stops replay cleanly). Header version 4 (
Updaterecord forsave_or_update); v3 is upgraded in place, v2 read, v1 only through the one-time migration inHDF5Memory::open. Each checkpoint records aWalMarkin/metasoopen()never applies an entry twice; checkpoints and snapshots are durable as a unit (temp file synced, renamed, directory synced). Individual WAL appends are not fsynced (deliberate): saves since the last checkpoint can be lost on power failure or kernel panic. - Single writer.
create/openhold an exclusive lock on<store>.h5.lock(MemoryError::Lockedfor a second opener);open_read_onlyis a lock-free point-in-time view (CLIrecall/stats/agents-md/export). An unreadable WAL is quarantined to<store>.h5.wal.corrupt-<ts>; a WAL of an unknown newer version fails and is left untouched. - Signed checkpoints (
signingmodule): withset_signing_keyeach checkpoint stores an Ed25519-signed manifest (per-record SHA-256 in a Merkle tree plus settings/sessions/graph hashes;/integrity/record_hashes);HDF5Memory::verify(path, &pk)locates edits. The hashes must cover exactly what the file persists in the form the loader returns it (strings lose trailing NULs; an empty WAL mark is not written) —tests/signed_store.rsround-trips awkward strings. The key is never persisted; a signed store refuses to checkpoint without it (MemoryError::SigningKeyRequired;MemoryErroris#[non_exhaustive]). WAL entries after the checkpoint are not covered. - Write bookkeeping.
save/save_batch/save_or_updatefeed an in-memory, session-scoped provenance ledger and anomaly detector (provenance.rs,anomaly.rs); alerts never block a save (take_anomaly_alerts).MemorySourceis inferred from the caller'ssource_channelstring — a heuristic, not a trust boundary. MemoryConfig::compressionis off by default (deflate, or Zstd with the agent'szstdfeature, which links libzstd).
Workflows
Put $HOME/.cargo/bin on PATH. The h5py/netCDF4 interop tests find their
Python through CLAWHDF5_PYTHON (or .venv/bin/python); create it with
python3 -m venv .venv && .venv/bin/pip install h5py numpy netCDF4 hdf5plugin.
Set CLAWHDF5_REQUIRE_INTEROP=1 to make a missing interpreter a failure.
cargo build --release
cargo test --workspace
bash scripts/ci-test.sh # everything CI runs (see below)
CI (.gitea/workflows/)
ci.ymltest(ubuntu-latest,rust:latestcontainer; runnerstank,architect): installs h5py/netCDF4/xarray/hdf5plugin/maturin/pytest,hdf5-toolsandcmake, then runsscripts/ci-test.shwithCLAWHDF5_REQUIRE_INTEROP=1. The script runs: fmt; clippy (workspace, the format feature matrix, each plugin filter alone, parallel, fast-deflate, remote with all backends, h5rs remote); "no C in the default build"; wasm32 build and clippy;check-32bit-casts.sh; the wasm package under Node whennodeandwasm-bindgenexist (not in CI); the MSRV check;cargo test(workspace plus feature variants: format matrix, parallel, remote/object_store, h5rs URLs, ann parallel, fast-deflate); the h5py interop suites (writer_h5py_tests --include-ignored, plugin filters, ZFP); the Python package (clippy,maturin build, pytest vs h5py);cargo bench --no-run;check-nostd.sh; an optional fuzz smoke run (CLAWHDF5_FUZZ_SECONDS).ci.ymltest-arm64(linux_arm64;vision-01host mode,vision-02Docker — steps must work in both): clippy ofclawhdf5-accel, tests of-accel,-ann,-format; the only place the NEON kernels build.conformance.yml(nightly 03:17 UTC and manual): probe unit tests,conformance/test_ref.py, thenconformance/run.sh(gate:conformance/check.pyagainstbaseline.json).
Keep workflows free of JavaScript actions (actions/checkout,
actions/cache, …): rust:latest has no node and not every runner reaches
GitHub. Check out with plain git. Runners are gitea-runner 3.5.0 from
docker.gitea.com/act_runner (gitea/act_runner:latest on Docker Hub is
frozen at 0.6.1).
Conformance
CLAWHDF5_PYTHON=.venv/bin/python bash conformance/run.sh --no-fetch # writes CONFORMANCE.md
Reads 697 files of eight pinned corpora with clawhdf5 and h5py and compares
them object by object (602 ok in the run of 2026-09-28). CONFORMANCE.md is
generated — never hand-edit it (its wording lives in conformance/report.py).
Use --update-baseline only after an intended change in results.
CONFORMANCE_CACHE points at an existing corpus cache (conformance/.cache,
about 450 MB). See conformance/README.md.
HDF5 tools (h5rs)
cargo run -p clawhdf5-tools -- ls -r file.h5 # also dump [--json], stat, diff, check
bash scripts/h5rs-fuzz.sh # every subcommand over the CVE corpus: no panic/crash/hang
bash scripts/h5rs-check-ok-files.sh --data # check passes every fully-read conformance file
Interop tests compare against h5ls/h5stat/h5dump/h5diff (Debian hdf5-tools);
dump must stay byte-identical to h5dump on the test files.
Remote and browser tests
clawhdf5-remotetests run a std-only HTTP server (tests/common/server.rs, also therange_serverexample);CLAWHDF5_REMOTE_CORPUS=conformance/.cache/corpuscompares every corpus file over HTTP withFile::open.- wasm:
bash examples/wasm-viewer/test/run.shbuilds the package (needs thewasm-bindgenCLI at the crate's exact version) and tests it under Node and headless Chromium (Playwright's download in~/.cache/ms-playwrighton tank) againsttest/serve.py(range server with request counts). CI has neither, so it runs the nativeh5py_interopandlazytests (CLAWHDF5_WASM_CORPUS=conformance/.cache/corpusfor the corpus).
Python bindings
cd crates/clawhdf5-py && maturin develop
python -m pytest crates/clawhdf5-py/tests # compares with h5py; editing tests want CLAWHDF5_H5RS=<path to h5rs>
Benchmarks
- Search path:
cargo run --release -p clawhdf5-bench --bin search_harness(--full,--options-study,--footprint, …); reads:read_harness,concurrent_read; criterion benches withcargo bench -p <crate>. - Run on an idle machine (1-minute load average below 2; wait otherwise),
alternate base and candidate binaries for A/B comparisons, and record date,
machine, commit and command with every number in
BENCHMARKS.md. BENCHMARKS.mdis written by hand from dated runs; no script regenerates it (the oldscripts/run-benchmarks.sh, which benchmarked the pre-renamerustyhdf5-formatand overwrote the file, was removed on 2026-09-28).
CLI
cargo run -p clawhdf5-cli -- --help
# create, save, search, recall, stats, flush-wal, agents-md, export, snapshot, keygen, verify
Integration
- ClawBrainHub (
clawverse/clawbrainhubon git.redclaw.dev) is the one verified consumer:cbh-corereads and writes.brainfiles through the facade (File,FileBuilder,AttrValue,Selection),cbh-scanneruses the facade, andcbh-cliusesclawhdf5_agent::bm25::BM25Index. It depends on this repo by path (../clawhdf5), so changes to those APIs reach it directly. Verified 2026-09-25 against main: builds, and its 204 tests pass. - OpenClaw and ZeroClaw integrate nothing (see Standing rules).