Standard Grant: Sia NFS Gateway - indexd & NFSv4 (rebranded to: JanusFS)

Project Name:

Sia NFS Gateway - indexd & NFSv4

Name of the organization or individual submitting the proposal:

Roland Rauch

Describe your project.

This project continues the development of sia-nfs, the Sia NFS Gateway. The main goals are:

  • indexd support: Replace the current renterd dependency with indexd, since indexd is where the ecosystem is heading.
  • Partial NFSv4 support: Implement enough of the NFSv4 protocol to support common Linux and macOS clients reliably. NFSv3’s stateless design required workarounds that made write operations brittle; NFSv4’s stateful model (open, read, write, close) is a much better fit for the underlying storage and will significantly improve write stability.
  • Improved modularity: As part of this work, the codebase will be restructured into separate crates. The lower-level components (I/O scheduling, caching, virtual file system) become reusable building blocks that other indexd-backed Sia applications can build on.

The project is entirely written in Rust and will use the official Sia Rust SDK, specifically the indexd client library.

Who benefits from your project?

End users who want to access their Sia-stored data through a standard, OS-native protocol without running any Sia-specific software locally. Developers who may find the modular crates (I/O scheduling, caching, virtual file system) useful as building blocks for their own indexd-backed Sia applications.

How does the project serve the Foundation’s mission of user-owned data?

It makes user-owned data on Sia accessible through NFS, a protocol supported natively by every major operating system. The modular crate structure means other projects can reuse the same VFS, I/O and caching layers without starting from scratch.

Are you a resident of any jurisdiction on the sanctioned/monitored list?

No.

Will your payment bank account be located in any jurisdiction on that list?

No.

Grant Specifics

Amount of money requested and justification with a reasonable breakdown of expenses:

$5,600/month, covering four months of development work. Total: USD 22,400. 100% developer fees; no additional equipment or infrastructure costs.

What is the high-level architecture overview for the grant?

The project is structured into multiple Rust crates:

sia-io

An I/O library that sits between the indexd client library and higher-level consumers like sia-vfs. It provides two core capabilities:

  • Transparent access & caching: Objects are accessed through a type that is AsyncRead + AsyncSeek. Chunking, downloading, and caching are handled transparently in the background.
  • I/O scheduling: During the previous development cycle, significant time was spent dealing with the high and variable latency inherent to distributed storage over the internet. This led to a custom I/O scheduler capable of handling a wide variety of workloads, especially when facing out-of-order I/O (described in detail here). This scheduler will be extracted from the current sia-nfs codebase and moved into sia-io, making it available to any application built on top of the library.

Both the cache and the I/O scheduler are defined as traits, allowing alternative implementations.

This crate will also subscribe to object-related events from indexd and automatically evict stale cache entries.

sia-vfs

A virtual file system library (not a service or mountable file system) built on top of sia-io. It provides file-and-directory semantics over indexd-managed objects, serving as a building block for applications that need a file system hierarchy on Sia. Key characteristics:

  • File systems can be read-only or read-write.
  • Local state is transient and fully reconstructible from the network (as long as pending changes have been committed). It is stored in a local SQLite database as an operational optimization, not as critical data that needs to be backed up or preserved.
  • In read-write mode, modifications are local-first with eventual consistency. Changes are recorded in a write-ahead log and applied in an overlay fashion; modifications are stored separately and merged during reads. The WAL is eventually flushed and changes are uploaded. Packed uploads are used where beneficial.
  • Files are AsyncRead + AsyncWrite + AsyncSeek, providing familiar open/seek/read/write/close semantics.
  • The VFS targets practical client compatibility: the operations and patterns that real-world applications actually rely on. Full POSIX compliance is explicitly out of scope.

nfsserve

The existing Rust-based NFSv3 server library (nfsserve) that sia-nfs already uses. This crate will be forked and extended with partial NFSv4 support - enough to cover common Linux and macOS client workflows, not full protocol compliance. NFSv3 compatibility will be maintained for Windows clients. Changes will be contributed upstream where possible.

sia-nfs

The standalone application, built on top of the crates above. Because the heavy lifting is handled by the underlying libraries, the application itself is relatively thin. The final artifact is a single native binary running as a single process. The only external dependency is access to an indexd instance. Local storage requirements depend on cache size & WAL configuration; VFS state itself is negligible. A Docker image will be provided for convenience.

What security best practices are you following?

In addition to the practices outlined in the grants development guide:

  1. Memory-safe language: The project is written entirely in Rust, eliminating entire classes of vulnerabilities (buffer overflows, use-after-free, data races).
  2. Type-driven correctness: Rust’s strong type system is used to provide guardrails at compile time; invalid states are made unrepresentable where possible.
  3. Secure handling of sensitive data: Confidential data (mnemonics, key material, credentials) is wrapped in a dedicated secure type that prevents accidental leakage through logging or debug output, makes every access explicit and easily traceable in the codebase, and ensures sensitive data is zeroized (overwritten with 0x00) in memory as soon as it is no longer needed. Sensitive data is never persisted.

Timeline with measurable objectives and goals.

Milestone 1 - sia-io crate
Due: 25 April 2026

  • AsyncRead + AsyncSeek access to indexd-managed objects with transparent chunking and caching
  • I/O scheduler extracted from current sia-nfs codebase
  • Automatic cache eviction via indexd event subscription
  • Trait-based cache and scheduler interfaces for alternative implementations

Milestone 2 - sia-vfs crate
Due: 25 May 2026

  • Read-only and read-write file system modes
  • SQLite-backed transient local state, fully reconstructible from the network
  • Write-ahead log with overlay-based modifications and eventual upload
  • AsyncRead + AsyncWrite + AsyncSeek file interface

Milestone 3 - Refactored sia-nfs, built on sia-vfs
Due: 25 June 2026

  • sia-nfs rebuilt on top of sia-io and sia-vfs, replacing the renterd dependency with indexd
  • Feature parity with the current NFSv3-based implementation
  • Docker image provided

Milestone 4 - Partial NFSv4 support
Due: 25 July 2026

  • NFSv4 support sufficient for common Linux and macOS client workflows
  • Stateful open/read/write/close model replacing NFSv3 workarounds for write operations
  • NFSv3 compatibility maintained for Windows clients

Potential risks that will affect the outcome of the project:

Milestones 1-3: No significant risks identified. The indexd client library crate is already available and has given a clear picture of the expected API surface. Development can proceed even with early preview builds of indexd; the only notable risk would be an extensive delay in the indexd release, which is unlikely given the Foundation’s current priorities.

Milestone 4: NFSv4 is a major protocol change over NFSv3. Full compliance is explicitly out of scope; the goal is to support enough of the protocol for common Linux and macOS clients to work reliably, and to fix the brittle write behavior caused by NFSv3’s stateless design. Even so, the amount of work required is harder to estimate than for the other milestones. NFSv3 compatibility must also be maintained for Windows clients. The main risk is that the work required to support a reasonable NFSv4 subset could exceed the budgeted time. This is also why NFSv4 is scheduled as the final milestone - if the work exceeds the budgeted time, Milestones 1-3 will already be complete and fully usable.

Will all of your project’s code be open-source?

Yes, 100%. All code will be fully open source.

Leave a link where code will be accessible for review.

Do you agree to submit monthly progress reports?

Yes.

Previously completed grants

Contact info

Email: [email protected]

Hi @rrauch - thank you for your proposal. This will be presented to the Committee for review at the next meeting on March 17, 2026.

1 Like

Thanks for your proposal to The Sia Foundation Grants Program.

After review, the Committee has decided to approve your proposal. Congratulations! They’re excited to see what you can accomplish with this grant.

There was one recommendation given, namely to keep the compatibility with renterd alongside indexd, as that would give the users more flexibility and should be relatively easy to do.

We’ll reach out to your provided email address for onboarding. This shouldn’t take long unless your info has changed from last time, but you may still need to adjust your timelines.

April 2026 Progress Report

What progress was made on your grant this month?

  • sia-io, a library that sits between indexd and/or renterd and higher-level consumers.
  • All planned features (transparent access, caching, chunking, i/o scheduling) where achieved
  • Furthermore - following the feedback from the Committee - renterd support was added as well

Detail tasks worked on this month per milestone with the appropriate Pull Request(s) links as outlined in the guide:

Milestone Task Pull Request(s) Additional Notes
Milestone 1 1. Add support for renterd renterd backend for sia-io by rrauch · Pull Request #1 · rrauch/sia_nfs · GitHub
2. Add support for indexd indexd backend for sia-io by rrauch · Pull Request #2 · rrauch/sia_nfs · GitHub uses sia_storage crate
3. Introduce abstraction layer Abstraction layer for sia-io by rrauch · Pull Request #3 · rrauch/sia_nfs · GitHub a type-erased abstraction layer over indexd & renterd backends
4. Cache support added Multi-layer cache for sia-io by rrauch · Pull Request #4 · rrauch/sia_nfs · GitHub a multi-layer (L1 = in-memory, L2 = persistent) caching system
5. I/O Scheduler & Resource manager support IO-Scheduler & ResourceManager ported to sia-io by rrauch · Pull Request #5 · rrauch/sia_nfs · GitHub ported from previous codebase

Summarize any problems that you ran into this month and how you’ll be solving them.

Supporting both - indexd & renterd required some additional work, but went well overall. No problems where experienced.

What will you be working on next month?

Milestone 2

2 Likes

Hi @rrauch - your technical review is still in progress but I wanted to address your project branding.

It’s been pointed out that we haven’t been consistent with our brand guidelines and have been permitting the use of ‘Sia’ in our grantee’s projects names. We are therefore asking all current active projects with ‘Sia’ in their name to rebrand to be compliant with our brand guidelines.

If there are any instances where it’s not possible to alter your project name, please let me know.

Thanks, and be back with the results of your review soon.

Hi @mecsbecs, I will think of a different project name that does not conflict with the current branding guidelines.

To clarify: is it sufficient to only change the project name (Sia NFS Gateway) or should I also rename the underlying libraries I am building (sia-io, sia-vfs, sia-nfs)?

Thanks @rrauch - and yes, I think it would make sense to rename the underlying libraries you’re building to match.

Hi @rrauch - this month’s review was successful. Your reviewer made the following recommendations for future work:

- add a CI pipeline before codebase grows further

  • cover queue.rs, seems to be the highest risk untested module
  • Object::cast() in renterd/object.rs:419-428 should replace its ptr::read/mem::forget with a safe field-by-field reconstruction since the optimizer eliminates it anyway, removing an unnecessary audit burden as the struct evolves.

The team is looking forward to seeing what you do over the next month!

May 2026 Progress Report

What progress was made on your grant this month?

  • sia-vfs, a virtual file system layer that presents a POSIX-like interface (local-first operation with eventual consistency).
  • The planned core functionality of the VFS layer has been achieved. Some “plumbing” around file creation and modification will be added shortly.
  • The current approach allows for adding advanced features such as snapshots and branching in the future - surpassing the original proposal.

Detail tasks worked on this month per milestone with the appropriate Pull Request(s) links as outlined in the guide:

Milestone Task Pull Request(s) Additional Notes
Milestone 2 1. Chunk & Blob support Core chunk & blob types for sia-vfs · Pull Request #7 · rrauch/sia_nfs · GitHub BlobReader & BlobWriter allow random access
2. VFS API Main VFS API & types · Pull Request #8 · rrauch/sia_nfs · GitHub
3. Database backend Database-backed persistence layer · Pull Request #9 · rrauch/sia_nfs · GitHub embedded SQLite with comprehensive schema
4. Data serialization Zero-copy data serialization · Pull Request #10 · rrauch/sia_nfs · GitHub FlatBuffers backed; zero-copy; for local and remote data
5. Caching In-memory caches for path and inode lookups · Pull Request #11 · rrauch/sia_nfs · GitHub for Inode retrieval and Path lookups

Summarize any problems that you ran into this month and how you’ll be solving them.

I lost several days of work attempting to use a graph database instead of a relational one. The graph-db approach did not work out, so I discarded that work and went with SQLite plus a strict schema instead. This works very well, and I have almost fully made up the lost time. A few finishing touches are therefore still outstanding.

Please note: I have not yet rebranded the project (as discussed above). This will happen next month.

What will you be working on next month?

  • Finishing touches on the VFS layer
  • Milestone 3
  • Rebranding the project
1 Like

Hi @rrauch - your reviewer raised the following items that require immediate attention before this milestone can be approved and payment issued:

  • > Milestone 1 follow-ups were not addressed. In particular there is still no CI, which is the easiest way to keep this kind of bug out of future PRs.

  • Half of one deliverable is missing. The proposal promised local state “fully reconstructible from the network.” The database schema is ready, there are columns for remote locations and a sync queue. But no Rust code touches any of it. Reading a chunk marked as remote just errors out. Is this being pushed to the next milestone?

  • Data-corruption bug found during review. Write some bytes. Flush. Seek to the end. Write more. Reopen the file. The original bytes come back as zeros. This is exactly the pattern NFS uses to grow a file.


For additional consideration, before your next milestone, the reviewer recommends:

- Add one end-to-end test: write, flush, seek, extend, commit, reopen, read. That single test catches the corruption bug.

  • Add basic CI: formatting, linting with warnings as errors, tests. It would have caught the hygiene drift from both milestones.

Hi @mecsbecs,

thank you for the feedback. Here are my responses:

I have read the recommendation in the previous review regarding introducing CI. Being a team of one, however, I have to pick my battles carefully.
The project does have a fair number of tests covering a meaningful number of cases - even some fairly extensive ones where it matters most, e.g. blob reading/writing.
In my current workflow I run all tests locally prior to committing - not ideal, but given the limitations I am under, I find it to be a reasonable tradeoff.

I have re-read the progress report I submitted and I can see how I did not emphasize enough that the some functionality did not make it into the milestone on time and spilled over into this months workload. Apologies for not being clear about this.
The hard parts have been completed. Syncing, however, has not yet been merged. Progress can be followed here and I expect it to be ready for merging before the end of the week.

Thank you very much for the bug report. It was very clearly stated and easy to reproduce. I have fixed the bug already and added two regression tests to make sure it doesn’t resurface in the future.
Here is the commit for the bugfix

Two extra tests have been added. See bugfix above.

The codebase should become more stable & polished during this milestone. If time permits I will add basic CI.

I hope these comments help shine some light on last months progress. Please let me know if you need anything from me before the next report.

1 Like

Thanks @rrauch - the above is acceptable to us and there is no further feedback.

The team is looking forward to seeing what you do over the next month!

1 Like

June 2026 Progress Report

What progress was made on your grant this month?

  • VFS improvements and additional testing.
  • The outstanding bi-directional syncing has been added. The project now supports both push (sending local changes to the network) and pull (retrieving network changes and applying them locally). push supports packed uploads when used with the indexd backend. This completes Milestone 2.
  • Milestone 3: the previous (now legacy) VFS implementation was replaced with the VFS from the new codebase, and any obsolete parts were removed. Feature parity has been reached as outlined in the original project plan.
  • Snapshot and branching functionality was added, though it is not yet exposed via the CLI.

Detail the tasks worked on this month per milestone, with the appropriate Pull Request link(s), as outlined in the guide:

Milestone Task Pull Request(s) Additional Notes
Milestone 2 1. VFS improvements File operations & improved caching · Pull Request #12 · rrauch/sia_nfs · GitHub
2. Bi-directional syncing Bi-directional syncing · Pull Request #13 · rrauch/sia_nfs · GitHub Completes Milestone 2
Milestone 3 3. NFS integration NFS integration · Pull Request #15 · rrauch/sia_nfs · GitHub Replaced VFS implementation

Summarize any problems that you ran into this month and how you’ll be solving them.

While the project is developing as planned in terms of functionality, it currently lacks polish and testing. I haven’t had enough time to focus on these areas yet. For the same reason, I’ve had to push the project rebranding to next month’s workload again.

Next month, I’ll prioritize this work over Milestone 4. Overall, I estimate the project is around 1.5-2 weeks behind the original timeline.

What will you be working on next month?

  • Implementing the missing CLI functions, such as:
    • create new filesystem
    • list filesystems
    • show filesystem details
    • create branch
  • Additional testing across Windows, Linux, and macOS clients with different workloads
  • Cleaning up the codebase and updating dependencies
  • Rebranding the project
  • Updating user-facing documentation
  • Milestone 4

Hi @rrauch - thank you for this progress report.

Based on your reviewer’s feedback, we are requiring the below items be addressed before we can consider this milestone approved:

  • Wire indexd into the binary with its own flags, or formally re-scope this deliverable to “indexd-capable libraries, renterd default” and get committee sign-off. Remove the now-orphaned renterd_client dependency.

  • Fix and verify the Dockerfile against the current crates, then confirm docker build succeeds from a clean checkout.

  • Add a config integrity check before trusting network-driven head updates. Bound allocations before verification. Replace the panics on remote data with recoverable errors. Stop writing encryption keys to disk in plain text. Wire up the sync cleanup function and restore structured error logging.

  • Add basic CI: formatting, linting with warnings as errors, and the full workspace test run. It would have caught the broken Docker build, the workspace compile break, and the hygiene drift immediately.

  • Add a full-tree test that writes, flushes, seeks, extends, commits, pushes, pulls, and reads back non-empty content, plus smoke tests for the NFS handlers and the upload scheduler.

  • Update the README and CLAUDE.md to the new CLI and architecture. The documented usage and Docker commands no longer run.

Please tag me when you’re ready to have this re-reviewed.

Hi @rrauch - checking in on the above. Note: any delay in addressing the above risks non-payment for the milestone on the 15th of this month.

Hi @mecsbecs,

thanks again for the detailed review. Here’s my feedback:

indexd support was already implemented but not yet exposed via the CLI. The plan was to do it after the new CLI functions mentioned in my report. Based on your feedback I’ve moved this forward: the user can now select indexd or renterd, with indexd as the default. This is merged.

This was an oversight on my part. I’ve updated the Dockerfile to the new project structure and confirmed the image builds from a clean checkout.

All remote data is treated as untrusted and validated before use, so I want to make sure I address the right thing here. Could you provide more details? Happy to add further checks if there’s a gap I’m not seeing.

I believe this is already the case, remote-data problems are treated as recoverable errors, and there are no panic! calls in the codebase. There are two expect calls in the sync logic, but they run on data that’s already been validated, so they shouldn’t be reachable.

This really shouldn’t be the case. At no point is any key material, passwords, or other sensitive data written to disk - neither in plain text nor otherwise. Even in memory it is treated as sensitive - wrapped in a secure type to avoid accidental exposure and wiped from memory as soon as possible. Can you point me to where this is happening? I will deal with it ASAP!

Sync cleans up after itself. Non-recoverable errors trigger an automatic transaction rollback. The VFS tree is never left in an inconsistent state. Intermediate data is reference counted and automatically deleted as soon as the counter hits 0.

I do run the full workspace test before committing. Here is what it looks like:

cargo test --lib

    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.23s
     Running unittests src/lib.rs (target/debug/deps/foyer_cache-cc40137ac6fd735e)

running 1 test
test tests::test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/lib.rs (target/debug/deps/sia_io-56926186d9db32c9)

running 7 tests
test indexd::tests::acquire_authorization ... ignored
test indexd::tests::integration_test1 ... ignored
test renterd::tests::integration_test1 ... ignored
test tests::indexd_test1 ... ignored
test tests::renterd_test1 ... ignored
test renterd::object::tests::object_serde ... ok
test tests::mock_test1 ... ok

test result: ok. 2 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out; finished in 0.11s

     Running unittests src/lib.rs (target/debug/deps/sia_vfs-17fd448016e48d54)

running 48 tests
test blob::io::tests::read_from_dirty_write_buffer ... ok
test blob::tests::flatbuffer_roundtrip ... ok
test blob::io::tests::simple_roundtrip_write_extend_read ... ok
test blob::io::tests::operations_after_close_fail ... ok
test chunk::chunk_map::tests::adjacent_entries_no_merge_different_chunk ... ok
test chunk::chunk_map::tests::basic_insert_and_get ... ok
test chunk::chunk_map::tests::adjacent_entries_merge ... ok
test chunk::chunk_map::tests::insert_beyond_len_is_noop ... ok
test chunk::chunk_map::tests::insert_clamps_to_len ... ok
test blob::io::tests::read_after_flush_invalidates_fetch_cache ... ok
test blob::io::tests::sparse_write_with_holes ... ok
test chunk::chunk_map::tests::iter_entries ... ok
test chunk::chunk_map::tests::empty_map ... ok
test chunk::chunk_map::tests::remove_splits_entry ... ok
test chunk::chunk_map::tests::set_len_extends ... ok
test chunk::chunk_map::tests::set_len_truncates ... ok
test chunk::compression::tests::lz4_roundtrip ... ok
test chunk::compression::tests::zstd_roundtrip ... ok
test object::metadata::tests::roundtrip ... ok
test chunk::chunk_map::tests::overlapping_insert_splits ... ok
test blob::io::tests::overwrite_spanning_two_chunks ... ok
test blob::io::tests::len_truncate_extend ... ok
test blob::io::tests::read_with_seek_and_small_buffer ... ok
test blob::io::tests::roundtrip_varying_chunk_sizes ... ok
test blob::io::tests::many_small_writes_equal_one_big_write ... ok
test sync::pull::tests::pull_blob_ok ... ok
test vfs::commit::tests::flatbuffer_roundtrip ... ok
test vfs::config::tests::flatbuffer_roundtrip ... ok
test sync::pull::tests::pull_empty ... ok
test sync::pull::tests::pull_empty_blob ... ok
test sync::pull::tests::pull_blob_err ... ok
test sync::pull::tests::pull_config ... ok
test sync::pull::tests::pull_dir ... ok
test sync::pull::tests::pull_commit ... ok
test sync::pull::tests::pull_idempotent ... ok
test sync::pull::tests::pull_file ... ok
test sync::pull::tests::pull_single_chunk ... ok
test vfs::tests::bootstrap ... ok
test vfs::tests::create_directories ... ok
test vfs::tests::create_file ... ok
test vfs::tests::rename_mv ... ok
test vfs::tests::root ... ok
test vfs::tests::drop_fh ... ok
test vfs::tests::delete ... ok
test vfs::tests::root_undeletable ... ok
test vfs::tests::write_extend ... ok
test tests::full_tree_test ... ok
test sync::push::tests::basic_push ... ok

test result: ok. 48 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 4.34s

     Running unittests src/lib.rs (target/debug/deps/sia_nfs-b7c0c7d9fd6307e0)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

I have addressed the hygiene part: all warnings are fixed and unused code removed, so the project builds cleanly.

I have added the requested additional test here. These cases were largely covered by the existing tests, but a single end-to-end test is a good addition.

The README will be updated after the new commands have been added to the CLI and the project has been rebranded (as noted in my report).

On CLAUDE.md: there isn’t one in this repo, and the project has never used a GenAI harness.


I have already merged the changes I mentioned above into main. The corresponding PR is here if you are interested in the details.

Next steps:

  1. Add the new commands to the CLI
  2. Manual testing across different OSes and workloads
  3. Rebranding the project
  4. Updating user-facing documentation

I expect these done by the end of next week. I will update this thread again and tag you to keep you in the loop.

After that I’ll begin the final Milestone.

Hi @rrauch - thank you for this reply. Please note a response from your reviewer will be forthcoming at the beginning of next week given the time difference and the fact we have a company holiday tomorrow.

1 Like

Hello @rrauch - your technical review is now complete and this milestone has been approved.

To address this question:

Your reviewer wanted me to pass on the following:

Yeah, for chunks, blobs, entities and commits you’re spot on. Each one rehashes the bytes on pull and checks them against the id the parent handed down, so if the backend messes with any of them the pull just fails. I went through that path and it holds up.

The one place it doesn’t is the config object. It’s the only thing coming off the backend that gets taken on faith. In Config::load_from_backend, config.rs:98, the only thing checked is the “CONFIG” type tag. No id, no hash, no signature over the bytes, and it’s even located by a key the writer gets to name, /configs/{last_modified}.config. The one other guard, in maybe_set_config at config.rs:617, is the last_modified comparison, but that field lives inside the config and the writer sets it, so getting past it just means claiming a bigger number.

The reason hashing can’t cover this one is that config is the anchor everything else hangs off. A commit is trusted because some parent named its id, a chunk because its blob named it. Nothing names the config. The gateway just lists what’s on the backend and takes whichever config claims to be newest, so there’s no id to check it against, and the thing that’s your root of trust can’t be verified by the things sitting below it. It needs its own root.

What it buys an attacker: a backend that’s been compromised, or someone sitting on the link to it, can hand back a config with the timestamp bumped and point a branch head wherever they like among the commits they still have. set_config only checks that the head name exists in the config, not that its commit stayed put, config.rs:632, and once the head’s commit changes it wipes the tree and rebuilds it, config.rs:751. The Syncer pulls on a timer, so this isn’t some obscure path, it runs on every sync.

To be fair it is bounded. The commits and everything under them still get hash checked, so nobody’s slipping in fake files. What they can do is quietly walk you back to an older state or shuffle you between real ones: undo a delete, hide something newer, drop a branch. That’s why I put it at High and not Critical, and also why I didn’t drop it lower. It’s the single decision about which verified snapshot you land on, it fails silently instead of erroring, and it’s the one spot that breaks the untrusted backend assumption the rest of the code is genuinely careful about.

Simplest fix I can see is to sign the config with the identity the gateway already has and check the signature on pull, so it gets the same guarantee as everything else, just anchored to your key instead of a parent’s id. In steady state you could also just remember the last config you wrote yourself, since you should be the only one writing them, but a fresh gateway rebuilding from scratch has nothing local to lean on, so that case really wants the signature.

Hi @mecsbecs,

To summarize the above:

It criticizes that the Config - which is (sort of) the logical “root” of the data hierarchy - is not cryptographically signed, and therefore an attacker with write access to the backend could theoretically modify it.

The whole text is internally consistent, but it rests on a premise that doesn’t hold here: that the backend is untrusted. It isn’t. Backend in this context means either indexd or renterd, and everything stored on the (Sia) network is encrypted and/or cryptographically authenticated. As Config is stored on the network, it cannot (practically) be modified by an attacker. The root of trust is the Sia network + its established cryptography. The whole issue is moot / out-of-scope by design.

On a wider note (and meant constructively): the review process appears to rely heavily on LLMs. By their very nature their primary job is to produce output (text) - they’re not really good at just saying “looks good, nothing to add”. So the more they’re prompted, the more output they produce. In the code-review use-case this can lead to a virtually never-ending stream of “issues” with a noticeably poor signal-to-noise ratio, and this finding is a good example: a well-argued issue for a scenario that is entirely out-of-scope.

Please let me know if there is anything I can help with to make this process more productive for everyone.

Hi @rrauch - here is the response from the reviewer to the above:

You’re right about the config. On indexd it’s already fully authenticated end to end by the SealedObject layer, so a signature at the VFS level would be redundant. The same doesn’t hold for renterd though. As far as I can tell that path fully trusts the daemon and the transport, with no client side layer in between, so a small note on the trust model in the README might be worth it.

For what it’s worth, I think this surfaced an issue in our SDK: Sdk::object() never checks the returned object’s id against the requested key. I’ll flag that upstream so we can fix it on our end.

Otherwise, please bear in mind that everyone’s intentions here are focused on helping grantees to succeed and to produce the best possible work. The feedback from your reviewer - regardless of any tools utilized to assist in its construction - was shared with this in mind and was in response to a clarifying question, not to raise an additional “issue.” These review periods are generally meant to have a bit of back-and-forth for everyone’s benefit, so your constructive feedback is noted.

We look forward to seeing how the next month goes in the completion of this grant!