Provider / object storage
ValidatedCloudflare R2 / S3-compatible blocks
R2 is the remote block plane in the current split-store design. It is deliberately not used as the metadata store by the Node factory or theR2BlockStore API.
The chunked R2 adapter owns no namespace metadata. Select SQLite, PGlite, TiDB, FoundationDB, or another metadata provider and publish references there. The older R2Fs snapshot factory has a transitional state object and is not the split-store layout.
Immutable blocks are uploaded below the caller-owned prefix asprefix/b<opaque-id>. Creates are conditional, reads are exact-key GETs, and delete is explicit after a HEAD check.
Storage anatomy
Follow one file through both planes.
A mount-rs namespace stores names, attributes, directory entries, layouts, and block references. The bytes stay in the independent block plane. The example below is illustrative; the provider-specific schema, keys, and prefixes are the inspectable contract.
inode 42 · size 8,192NodeMetadata → NodeData::File(FileLayout)BlockId → raw bytesNamespace {
nodes[42] = NodeMetadata {
stats: { ino: 42, size: 8192, mode: regular },
data: File(FileLayout {
chunker: { algorithm: "fixed-size", version: 1, chunk_size: 4096 },
extents: [
{ file_offset: 0, block: "b012...", block_offset: 0, length: 4096 },
{ file_offset: 4096, block: "bfed...", block_offset: 0, length: 4096 }
]
})
}
}
BlockId "b012..." -> [4 KiB raw bytes]
BlockId "bfed..." -> [4 KiB raw bytes]Object storage
Cloudflare R2 / S3-compatible bucket prefix map
R2 is the block plane in the current split-store path. The bucket does not contain a mount-rs namespace table or manifest.
mount-rs-tests/vol-a/blocks/b0123456789abcdef0123456789abcdefbfedcba9876543210fedcba987654321Stores the namespace and the references that make each object part of a file.
Stores only raw bytes at exact keys. LIST shows a prefix; it does not reveal a directory tree.
vol-a/volume scopelogical prefix boundary; not a stored directory
vol-a/blocks/block prefixLIST prefix used to enumerate this block plane
vol-a/blocks/b0123456789abcdef0123456789abcdefexact object keyraw bytes for one immutable block
vol-a/blocks/bfedcba9876543210fedcba987654321exact object keyraw bytes for another block
The current R2 adapter generates a 33-character lowercase ID: b + 32 hexadecimal characters. It is an opaque allocation ID, not a filesystem path or a content hash. The file name, inode, offsets, and chunk layout are not encoded in the object key; inspect the metadata plane to map them.
Authenticated live R2 block, configuration-driven CLI, both metadata-provider composition gates, the public-NAPI benchmark, and a complete budgeted hosted live-R2 workflow passed; broader native and release gates remain separate. The current tracker records authenticated R2 filesystem checks, five-seed differential traces, Node and CLI coverage, ranged reads, reopen, owned-prefix cleanup, and both supported metadata-provider compositions. The latest isolated rerun also passed the signed-HTTP adapter contract (10/10 unit tests and 1/1 HTTP test), including eight concurrent publications, prefix isolation, and exact cleanup; its live Cloudflare rows stopped at credential preflight withR2_ENDPOINT unset. The portable provider matrix also adds seeded positional writes, truncate, flush, and reopen checks. Local object-store tests and RustFS results are not substituted for recorded live Cloudflare results. The current live rerun additionally passed the complete Node/N-API suite with live R2 and isolated PGlite, all3,105/3,105 operations across five seeded differential traces, the configuration-driven live-R2 CLI, and the public-NAPI benchmark's 8/8 iterations across 1, 4, 10, and 16 MiB with fixed 64 KiB chunks and verified cleanup. Hosted CI does not receive the dedicated R2 credentials, so those results remain a separately authenticated acceptance boundary. The repository now also records an exact-current local packet at db431f4 with Rust SDK 6/3/0, Node SDK 5/3/0, CLI12/2, upstream 1200 passed / 82 skipped, and all 40 five-seed/eight-backend traces at 621 operations; provider rows without credentials remain explicit skips. It wires a budget-gated Live Cloudflare R2 workflow with pinned Rust/Node tooling, a pinned mountx checkout, AWS CLI cleanup, the full Rust/Node/CLI/PGlite/trace packet, and an uploaded benchmark artifact. Hosted run 35579174675 on revisionc0aa081 passed the budget gate, Rust backend, PGlite SDK/CLI, the bounded remote R2 trace for seed 4182 with621 operations, Rust CLI, Node SDK, and service-evidence cleanup, emitting CLOUDFLARE_R2_FULL_PASS. Its local five-seed differential packet also passed all 3,105/3,105operations, and the benchmark artifact was uploaded. This supersedes the earlier 35575940720 ESTALE failure for hosted acceptance while preserving that failure as historical evidence. The newer workflow admission run 35670469503 atd570ab4 passed trigger coverage but stopped at the hosted monthly budget guard with count=243 limit=20; it provides no new live-R2 pass. The transport tracker also records 64 concurrent unique-object PUTs followed by 64 concurrent GETs across two session-owned buckets with exact readback, matching counters, and no retained assertions; this is same-process N-API session evidence, not live-provider or power-loss durability. The newer R2 admission run35675116591 passed trigger coverage but failed the bounded usage admission, so its live job was skipped. Local process-restart evidence in the S3 tracker is deliberately not promoted to live R2 durability.
Layout and chunking
Files are split into fixed-size chunks by ChunkedFs. The metadata provider records the block IDs; R2 stores bytes only. A completed object upload is the block barrier before metadata publication, but automatic orphan collection is not implemented.
Durability and consistency
The adapter reports durability only when the caller declares it. A configured R2 endpoint normally uses the durable path. Endpoint, bucket, credential, and state-key shapes are validated before client construction, so malformed configuration fails closed. The live tests verify confirmed writes, ranges, reopen, conditional behavior, and owned-prefix cleanup. Those results do not turn every object-store deployment into a universal power-loss claim. When the selected driver advertises durable_writes, successful mutations now await its syncfs barrier and return an error if that barrier fails; volatile drivers remain unchanged. This is a local mutation acknowledgment boundary, not proof of provider or power-loss durability. The subsequent local S3 qualification packet at exact revision 71972b28 passed the release N-API build, 990 pinned-oracle tests with 79 explicit skips, 4 unit, 6 chunked, 29 gateway, and 5 public-API Rust tests, 64-way direct-session/CAS concurrency, process restart, structural-factory parity, and the 40-case S3/WebDAV HTTP differential. It remains local/oracle evidence; the follow-up local hook packet at d43f5ea also wires RustS3SessionHooks and the N-API now,requestId, onError, andonAssertion controls with callback keepalive/release; its callback-observability, package, Rust, and strict-Clippy checks passed. These remain local controls, not live-provider acceptance. The latest AWS admission 35693941024 on published sourcefcf1d547 stopped atAWS_S3_CI_CONFIG_BLOCKED missing_bucket. The latest R2 admission 35693941037 on the same source stopped atcount=329 limit=20 and skipped its live integration job, so neither produced a live service PASS. The current S3 transport packet also closes bounded HEAD Content-Length/no-body, chunked PUT411 MissingContentLength, Expect: 100-continue, pipelined-response ordering, short streamed-response, and rejected-body keep-alive reuse cases; the full local Rust packet passed 40 gateway cases alongside strict Clippy, formatting, and diff checks. The same packet also proves bounded graceful close for an in-flight response. These are local transport boundaries, not live-provider acceptance.
Across every split-store composition, new blocks are flushed before metadata references are published. A provider acknowledgement is not automatically a power-loss or distributed-locking guarantee.
List, head, range-read, and delete an owned prefix
export R2_ENDPOINT='https://<account>.r2.cloudflarestorage.com'
export R2_BUCKET='mount-rs-tests'
export R2_PREFIX='mount-rs-tests/trace-owned'
aws s3api list-objects-v2 --endpoint-url "$R2_ENDPOINT" --bucket "$R2_BUCKET" --prefix "$R2_PREFIX/"
aws s3api head-object --endpoint-url "$R2_ENDPOINT" --bucket "$R2_BUCKET" --key "$R2_PREFIX/<block-id>"
aws s3api get-object --endpoint-url "$R2_ENDPOINT" --bucket "$R2_BUCKET" --key "$R2_PREFIX/<block-id>" --range bytes=0-63 /tmp/block-prefix.binCredentials, cleanup, and safe operations
Keep endpoint, access key, and secret values outside source control. Give each run a unique prefix, list it before cleanup, and delete only objects under that prefix. Never use a shared state key or bucket-wide delete as a test cleanup shortcut.
Current limits
R2 is block-only here; it does not solve metadata fencing or garbage collection. Orphan blocks can remain after uncertain publication, and native/hosted benchmark and full release matrices remain separate from the authenticated live gate. The main-only workflow admits at most 20 runs per month under a worst-case $100 monthly envelope (up to $4 per run). Hosted run 35579174675 completed the full R2 packet after the earlier 35575940720 ESTALE failure; this still does not establish native mount, power-loss, or full release acceptance.
Sources: R2 block adapter · Shared object-store block adapter · Live R2 evidence in the tracker · Configuration-driven provider matrix · Budgeted live-R2 workflow · R2 progress ledger · S3 transport durability boundary · Hosted R2 acceptance run · Hosted R2 benchmark artifact · Latest hosted R2 admission · S3 session-hook change