paritytech / paritytech/web3-storage

[Provider] Layer-0 read endpoints are unauthenticated, so bucket `visibility` is unenforced

Open
#383 0 comments 0 reactions 1 assignee View on GitHub

@ilchu is already working on this.

Since Sep 4, 2026.

Dominant language
Rust
Stars
13
Forks
3
Avg merge
2d 2h
Merged PRs (30d)
33

Description

Follow-up of https://github.com/paritytech/web3-storage/pull/330

Needs double-checking and investigation before acting on it.

Motivation

#330 introduces bucket visibility (Private ⇒ primaries serve reads only to members) and wires the check into the provider node's auth gate. But the gate only covers the Layer-1 endpoints. The raw Layer-0 read surface serves the same bytes to anyone, so a Private bucket is members-only through /fs/* and /s3/* and wide open through /node, /read, /mmr_peaks. The L0/L1 mismatch itself predates #330 (#228); visibility is what turns it from an inconsistency into an unenforced protocol rule.

This contradicts the design. The replica sync loop (provider-node/src/replica_sync.rs) walks GET /mmr_peaks then GET /node — both ungated — so a replica can sync a private bucket straight from its primaries. docs/design/scalable-web3-storage.md (Bucket Visibility & Access) states the opposite: a replica provider is not a bucket member, so honest primaries refuse it, and a born-private bucket therefore has no honest sync source. That impossibility is what the design calls the strongest privacy configuration, and what the "no on-chain gate on replica creation" rationale rests on. As implemented, it does not hold.

Current state

In provider-node/src/api.rs, only three Layer-0 endpoints check a role:

  • PUT /node, POST /commitRequiredRole::Writer
  • POST /deleteRequiredRole::Admin

Every Layer-0 read endpoint has no auth at all: GET /node, GET /read, GET /chunk_proof, GET /mmr_proof, GET /commitment, GET /checkpoint-signature, GET /mmr_peaks, GET /mmr_subtree, POST /fetch_nodes, POST /exists, GET /buckets, GET /replica/historical_roots, GET /replica/sync_status. Their Layer-1 equivalents in provider-node/src/fs_api.rs and s3_api.rs all go through check_role(..., RequiredRole::Reader).

They split into two groups, which matters for any fix:

  1. Already bucket-bound — take bucket_id and use it: /mmr_peaks, /mmr_subtree, /commitment, /checkpoint-signature, /mmr_proof, /exists, /replica/historical_roots, /replica/sync_status. These are gateable today with the same check_role the fs/s3 handlers use.
  2. Keyed by hash or data root, with no bucket binding/node, /read, /chunk_proof, and /fetch_nodes (it accepts bucket_id but the handler ignores it). There is no reverse mapping to gate on: crates/providers/storage keeps nodes in one global nodes column family, check_exists takes _bucket_id and ignores it, and the declared root_to_bucket column family is never written or read.

GET /buckets additionally lists every bucket on the node, unauthenticated.

Related

#228 (same question, L0/L1 role mismatch), #304 (auth scheme: binding + replay — orthogonal), #88 (provider validation inventory), #330 (added visibility, gated Layer-1 only), #94 (direct provider protocol).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.