Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions