Grant Milestone Progress Report (May)
What progress was made on your grant this month?
-
Completed Milestone 4 end-to-end. The upload → read-back → share-URL cycle runs against the live Foundation indexer at https://sia.storage. With the AppKey and a stored SealedObject cached client-side, S5 apps using Sia storage no longer need
renterdrunning and can fetch blobs direct from Sia hosts viaSdk::download. -
Built
s5_store_packingto bundle many small writes into larger packs before they reach a slow or per-operation-expensive backend. Generic over three sub-stores (blobs,manifests,staging) and reusable with anyStoreimplementation, not justIndexdStore. The first-fit + slab-waste heuristic is ported from Sia Foundation’ss3d. -
Bumped iroh 0.97 → 1.0.0-rc.0 to clear two RUSTSEC advisories that surfaced in hickory-proto 0.25 mid-month.
Architecture and design choices
IndexdStore<P, M> adapts sia_storage::Sdk to S5’s existing Store trait. P is any Store holding a 72-byte fixed-format pointer per caller path (object_id + metadata_hash). M is any BlobsReadWrite caching SealedObject bytes by their BLAKE3 hash; misses fall through to Sdk::object and write back. PackingStore<B, M, S> is an independent decorator that bundles small writes into ≤ 256 MiB packs before they hit a slow backend, with three pluggable sub-stores. Both crates speak only existing S5 traits and compose with TieredStore, CachedBlobsRead, LocalStore, MemoryStore, and the rest of the existing ecosystem.
Design choices:
-
No
BlobLocation::IndexdObjectvariant.Store::provide()returns the SDK’s signed share URL wrapped asBlobLocation::Url. Backend-specific location surfaces don’t belong on the key-valueStoretrait, which couples a storage concern (path → bytes) to a network-distribution concern (where on the network to fetch). The dev branch carries a deprecation notice onStore::provide()to formalise this. -
SealedObject persisted client-side. It is the trustless capability: with the AppKey it lets a client fetch from Sia hosts even if the indexd provider disappears. The current implementation caches SealedObjects in a content-addressed
BlobsReadWritekeyed by their BLAKE3 hash. -
No fork or patch of
sia_storage. Upstream shipsSiaDecodableforSealedObjectbut notSiaEncodable. The new crate carries a local encoder mirroring the decoder field-for-field, with a TODO to upstream. -
Two encryption layers, intentional. L1 is FS5’s existing per-vault deterministic encryption (
HKDF(plaintext_hash, vault_key, …)), which gives cross-device dedup. L3 is the SDK’s per-slab encryption applied at upload. No L2 insidePackingStore; if you need another layer, drop in anEncryptedStore<Inner>decorator. -
Three pluggable backends in
PackingStore.blobsholds pack bodies (content-addressed).manifestsholds per-packPackManifestrecords (list()required at startup).stagingis a durable write-ahead buffer for pending writes (LocalStorefor crash survival,MemoryStorewhen re-ingest is cheap). -
Self-identifying manifests. 48-byte header with
{magic, pack_hash, blob_count}followed by sorted 16-byte members{prefix[12], length: u32}. Reading a manifest does not depend on its filename, so the manifest store can use S5’s existingpaths::path_for_hashconvention (base64url / base32_fs / sharded per the backing store’sfeatures()). -
In-memory index layout.
HashMap<prefix, pack_index>for dispatch, binary search in the pack’s sorted members for the entry, cumulative sum of prior lengths for the offset. Avoids carryinglengthandpack_hashper entry; trades a small read-side CPU cost for the memory savings.
Status against the original M4 task table
| Grant deliverable | Status | Notes |
|---|---|---|
s5_store_indexd crate implementing all Store trait methods |
✓ | Every Store method implemented. 8 offline tests cover wire formats and AppKey vault. The interactive examples/end_to_end.rs exercises the live cycle. |
| Map S5 blob paths to indexd Object keys (consistent key derivation from content hash) | ✓ | Pointers are keyed by the caller’s path; the SDK assigns object IDs internally (Blake2b256 of slab metadata). IndexdStore records {object_id, BLAKE3-of-SealedObject} per caller path, so any path scheme works including FS5’s blob3/<base32(hash)> form. The pointer Store handles the indirection; no separate path-to-object-key translation step. |
| App registration flow: browser-based OAuth, store credentials | ✓ (with deviation) | Browser-OAuth via Builder::request_connection / wait_for_approval. The AppKey persists at ~/.config/s5/keys/indexd_appkey.bin, age-encrypted when a recipient is configured, 0600 plaintext otherwise. I went with age-encrypted files instead of the proposal’s keyring crate suggestion, matching the existing s5_node::identity_vault pattern for the master signing key. This keeps the security model unified across S5 and sidesteps platform-specific keychain failure modes (locked keychain, no daemon, etc.). |
BlobLocation::IndexdObject variant for direct RHP4 downloads |
✗ (covered differently) | Direct RHP4 downloads still happen; Sdk::download issues them through internal SDK paths. I skipped the new variant per design choice 1. The dev branch’s deprecation notice on Store::provide() documents the plan: location sharing moves out of Store into a dedicated higher-level API. |
Merged / open this month
| Milestone | Task | PR | Notes |
|---|---|---|---|
| M4 | s5_store_indexd crate |
#8 | IndexdStore<P, M> over sia_storage::Sdk. Browser-OAuth + age-encrypted AppKey caching. 72-byte fixed pointer wire format. Local SiaEncodable for SealedObject. put_bytes overwrites and reclaims the old Sia object best-effort. 8 offline tests + interactive E2E example. |
| M4 | s5_store_packing crate |
#7 | First-fit bin-packing with a slab-waste heuristic, self-identifying manifests, three pluggable backends, and crash-safe append-only writes that recover on restart. 15 unit + integration tests. |
| M4 | iroh 0.97 → 1.0.0-rc.0, irpc 0.13 → 0.15 | bundled in #7 | Brings hickory-proto to 0.26.1, clears RUSTSEC-2026-0118 and -0119. |
Validation
After git checkout stores-indexd:
# offline test suite for both new crates
cargo test -p s5_store_packing -p s5_store_indexd
# interactive E2E against the live Foundation indexer
# one-shot OAuth on first run, AppKey cached at ~/.config/s5/keys/indexd_appkey.bin
cargo run --example end_to_end -p s5_store_indexd
What will you be working on next month?
-
Land
BlobsDeleteand theStore::provide()deprecation on main. -
Performance: parallel blob uploads in
IndexdStoreand intense testing -
Vup Web refresh: mobile-responsive layout, modal-based dialogs replacing browser prompts, first-run onboarding wizard for indexd.
-
Documentation pass: user guide for
vup, architecture doc for the pointer-and-metadata-cache layering insideIndexdStore. -
Publish to crates.io:
s5_store_indexd,s5_store_packing, updateds5_fs.