From 0b43f6c60f175663d4551e7f97624a455b809585 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 18 Jun 2026 18:11:53 -0400 Subject: [PATCH 1/2] gateway: default GET responses to Cache-Control no-cache (fix stale web listing) The web app showed a STALE folder/bucket listing after an upload: it runs on browser fetch, which heuristically HTTP-caches GET responses that carry no Cache-Control. The forest index (the listing source) is read at an encrypted, per-user-derived key the gateway cannot single out, so the browser served a cached old index for minutes. Native is unaffected (reqwest has no browser cache) -- exactly why native refreshed instantly and web did not. Fix: default GET responses to Cache-Control: no-cache (revalidate-always). The browser now re-checks every object via its ETag: - the index changes on each write -> conditional GET misses -> 200 FRESH index -> listing always current; - unchanged file content -> ETag matches -> 304 (already handled at the If-None-Match block) -> browser serves the cached body, NO re-download. Listings stay fresh while downloads stay cached -- no blanket cache-disable, no client change. A stored per-object cache_control still wins. Gateway-only: no SDK publish, no web redeploy. Deploy the gateway to take effect. Co-Authored-By: Claude Opus 4.8 --- crates/fula-cli/src/handlers/object.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/fula-cli/src/handlers/object.rs b/crates/fula-cli/src/handlers/object.rs index 6aa9c9b..b4eb421 100644 --- a/crates/fula-cli/src/handlers/object.rs +++ b/crates/fula-cli/src/handlers/object.rs @@ -863,9 +863,21 @@ pub async fn get_object( response = response.header("Content-Type", ct); } - if let Some(ref cc) = metadata.cache_control { - response = response.header("Cache-Control", cc); - } + // Default GET responses to `no-cache` so a browser HTTP cache (the web app + // runs on `fetch`, which obeys it; native reqwest has no such cache) ALWAYS + // revalidates via the ETag instead of serving a heuristically-cached copy. + // This is what fixes the web app showing a STALE folder/bucket listing after + // an upload: the forest index lives at an encrypted key the gateway can't + // single out, but `no-cache` makes the browser re-check EVERY object — + // - the index: its content (hence ETag) changes on each write, so the + // conditional GET misses and returns 200 with the FRESH index; + // - unchanged file content: ETag matches, so the conditional GET returns + // 304 (handled at the If-None-Match block above) and the browser serves + // the cached body with NO re-download. + // Net: listings stay fresh while downloads stay cached. A stored per-object + // cache_control still wins (e.g. an explicit `immutable` hint set on write). + let cache_control = metadata.cache_control.as_deref().unwrap_or("no-cache"); + response = response.header("Cache-Control", cache_control); if let Some(ref cd) = metadata.content_disposition { response = response.header("Content-Disposition", cd); From f57d20c29c5d4a6ea6fcc6ee4bdbbae9710633c3 Mon Sep 17 00:00:00 2001 From: ehsan shariati Date: Thu, 18 Jun 2026 18:23:26 -0400 Subject: [PATCH 2/2] v u --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 2 +- crates/fula-flutter/Cargo.toml | 2 +- crates/fula-js/Cargo.toml | 2 +- packages/fula_client/ios/fula_client.podspec | 2 +- packages/fula_client/pubspec.yaml | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5152be4..1ab3902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "fula-api" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "axum", @@ -1765,7 +1765,7 @@ dependencies = [ [[package]] name = "fula-blockstore" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "async-trait", @@ -1803,7 +1803,7 @@ dependencies = [ [[package]] name = "fula-cli" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "async-trait", @@ -1857,7 +1857,7 @@ dependencies = [ [[package]] name = "fula-client" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "async-trait", @@ -1900,7 +1900,7 @@ dependencies = [ [[package]] name = "fula-core" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "async-trait", @@ -1935,7 +1935,7 @@ dependencies = [ [[package]] name = "fula-crypto" -version = "0.6.15" +version = "0.6.16" dependencies = [ "aes-gcm", "anyhow", @@ -1980,7 +1980,7 @@ dependencies = [ [[package]] name = "fula-flutter" -version = "0.6.15" +version = "0.6.16" dependencies = [ "anyhow", "async-lock", @@ -2004,7 +2004,7 @@ dependencies = [ [[package]] name = "fula-js" -version = "0.6.15" +version = "0.6.16" dependencies = [ "base64 0.22.1", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 42788a8..c1d405e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ name = "encrypted_upload_test" path = "examples/encrypted_upload_test.rs" [workspace.package] -version = "0.6.15" +version = "0.6.16" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/functionland/fula-api" diff --git a/crates/fula-flutter/Cargo.toml b/crates/fula-flutter/Cargo.toml index 594e441..cf687bf 100644 --- a/crates/fula-flutter/Cargo.toml +++ b/crates/fula-flutter/Cargo.toml @@ -5,7 +5,7 @@ description = "Flutter bindings for Fula decentralized storage - works on Androi # to parse `*.workspace = true` keys in its own manifest scan. Keep # these in sync with `[workspace.package]` in the root Cargo.toml. # (Same workaround as crates/fula-js.) -version = "0.6.15" +version = "0.6.16" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/functionland/fula-api" diff --git a/crates/fula-js/Cargo.toml b/crates/fula-js/Cargo.toml index 04a8df0..50ad812 100644 --- a/crates/fula-js/Cargo.toml +++ b/crates/fula-js/Cargo.toml @@ -4,7 +4,7 @@ description = "JavaScript/TypeScript SDK for Fula decentralized storage - WASM b # Hard-coded (not workspace-inherited) because wasm-pack <= 0.13 fails # to parse `*.workspace = true` keys in its own manifest scan. Keep # these in sync with `[workspace.package]` in the root Cargo.toml. -version = "0.6.15" +version = "0.6.16" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/functionland/fula-api" diff --git a/packages/fula_client/ios/fula_client.podspec b/packages/fula_client/ios/fula_client.podspec index 1a17881..9927b5c 100644 --- a/packages/fula_client/ios/fula_client.podspec +++ b/packages/fula_client/ios/fula_client.podspec @@ -6,7 +6,7 @@ Pod::Spec.new do |s| s.name = 'fula_client' - s.version = '0.6.15' + s.version = '0.6.16' s.summary = 'Flutter SDK for Fula decentralized storage' s.description = <<-DESC A Flutter plugin providing client-side encryption, metadata privacy, diff --git a/packages/fula_client/pubspec.yaml b/packages/fula_client/pubspec.yaml index 9cbdd0b..c4c0763 100644 --- a/packages/fula_client/pubspec.yaml +++ b/packages/fula_client/pubspec.yaml @@ -1,6 +1,6 @@ name: fula_client description: Flutter SDK for Fula decentralized storage with client-side encryption, metadata privacy, and secure sharing. -version: 0.6.15 +version: 0.6.16 homepage: https://fx.land repository: https://github.com/functionland/fula-api issue_tracker: https://github.com/functionland/fula-api/issues