Skip to content

Offline share-token creation fails: sharing.rs head_object bypasses offline-fallback #11

@ehsan6sha

Description

@ehsan6sha

Summary

create_share_token and create_share_token_with_mode in crates/fula-flutter/src/api/sharing.rs call guard.inner().head_object(&bucket, &storage_key) directly to retrieve the wrapped DEK from the S3 x-fula-encryption HTTP header. This bypasses the offline-fallback infrastructure (get_object_with_offline_fallback plus forest_entry.user_metadata fallback) that the download path already uses, so share-token creation fails whenever the master S3 endpoint is unreachable — even though the wrapped DEK is already available locally in the AEAD-protected forest_entry.user_metadata["x-fula-encryption"] field that the SDK populates at upload time (crates/fula-client/src/encryption.rs:5955-5968).

Reproduction

  1. Sign in to a v0.5.x SDK consumer (FxFiles) and upload one or more files to a bucket via the encrypted SDK.
  2. Change the gateway endpoint URL to an unresolvable hostname (e.g., s33.cloud.fx.land instead of s3.cloud.fx.land) to simulate master-unreachable. Restart the app.
  3. Trigger any flow that creates share tokens — for example, the automatic tag-share refresh (SharingService._buildManifestEntries in FxFiles).
  4. Observe: listObjects(bucket) succeeds via the warm-cache forest fallback (Forest loaded for bucket: images / listObjects(images, prefix=""): 34 files), but every createShareToken call fails with:
AnyhowException(Failed to fetch object metadata: HTTP error:
  error sending request for url (https://<unreachable-host>/<bucket>/<storage_key>)
  ... dns error ... No address associated with hostname)

followed by [TagManifestUpdate] re-published share <id> with 0 files.

Root Cause (file:line)

  • crates/fula-flutter/src/api/sharing.rs:47 in create_share_token:
let head_result = guard.inner().head_object(&bucket, &storage_key).await
    .map_err(|e| anyhow::anyhow!("Failed to fetch object metadata: {}", e))?;
  • crates/fula-flutter/src/api/sharing.rs:144 in create_share_token_with_mode: identical pattern.

Both call head_object directly on the inner FulaClient. The inner head_object issues a raw HTTP HEAD against the configured master endpoint — no block-cache check, no IPFS gateway race, no forest-entry fallback.

The download path is wired correctly: get_object_decrypted_inner at crates/fula-client/src/encryption.rs:1432-1463 uses get_object_with_offline_fallback_known_cid (or get_object_with_offline_fallback if no CID hint) and has a get_meta helper that falls back from HTTP headers to forest_entry.user_metadata. Share-token creation was written before that pattern landed and never updated.

Expected vs Actual

  • Expected: share-token creation succeeds offline for any file whose forest entry carries the encryption JSON in user_metadata (true for all uploads since encryption.rs:5955-5968 landed). The wrapped DEK can be unwrapped locally without any network call.
  • Actual: every share-token creation issues a HEAD against the configured master, even though all the inputs needed are already in the local AEAD-protected forest.

Suggested Fix

Mirror the pattern already in get_object_decrypted_inner:

  1. Look up the forest entry for the given (bucket, storage_key) via the existing forest_entry_lookup helper.
  2. Read x-fula-encryption from forest_entry.user_metadata first; fall back to head_object only when the forest entry is missing OR has no x-fula-encryption key (legacy uploads pre-encryption.rs:5955).
  3. Parse the same JSON shape currently parsed from the HEAD response.

Approximate diff: ~30 LOC in sharing.rs, no fula-crypto change, no FFI signature change.

Severity

Medium-High. Confirmed broken: any share-token-dependent workflow when master is unreachable, including FxFiles automatic tag-share refresh, manual create-share-link, and any UI that uses share tokens to display files.

The direct file-download path (get_flat) is correctly wired through the offline-fallback infrastructure, so plain downloads work offline for files in the warm cache or reachable via IPFS gateway. Only share-token flows break.

Verification Plan

A failing unit test will be added under tests/ that:

  1. Sets up an in-memory blockstore client and uploads an encrypted file.
  2. Marks the master backend unreachable.
  3. Calls create_share_token on the uploaded file.
  4. Asserts it currently fails — proving the bug.

After the fix lands, the same test should pass.


Reported by FxFiles team during offline-mode audit. Will follow with PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions