Skip to content

fix(content): return storage-authoritative MIME after upload - #36

Open
dzdidi wants to merge 5 commits into
masterfrom
fix/content-type-mismatch-v2
Open

fix(content): return storage-authoritative MIME after upload#36
dzdidi wants to merge 5 commits into
masterfrom
fix/content-type-mismatch-v2

Conversation

@dzdidi

@dzdidi dzdidi commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • read guarded resource back after Pubky homeserver upload;
  • return storage-authoritative MIME, size, path, and hash in registration response;
  • fail closed when uploaded bytes cannot be read back with expected hash;
  • add repository and production HTTP regressions for canonicalized MIME;
  • document storage-authoritative descriptor semantics.

This replaces #29, whose branch was based on the pre-squash PR #10 history and included unrelated branch-wide changes.

Contract and risk impact

  • Public API or SDK contract
    • response shape is unchanged, but guarded_resource.content_type now reflects storage readback rather than the request header.
  • Persisted data or migration
  • Authentication, authorization, identity, or secret handling
  • Payment or entitlement behavior
  • Runtime, deployment, or observability
  • No contract/risk impact

Verification

cargo test --locked -p locks-service --lib register_guarded_resource
  4 passed

cargo test --locked -p locks-service --lib infrastructure::pubky::priv_resources::tests::
  10 passed

cargo nextest run --locked -p locks-e2e --test production_creator_publishing_http
  3 passed

cargo fmt --all --check
  passed

cargo check --locked --workspace --all-targets
  passed

cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
  passed

git diff --check
  passed

Documentation

Updated docs/API.md to state that upload Content-Type is validated, while returned descriptor MIME is authoritative from storage readback and may differ from the request header.

Checklist

  • The change is focused and self-reviewed.
  • Regression tests were added where practical.
  • No credentials, identities, private content, payment material, or generated local state are included.
  • Formatting and relevant tests/lints pass.

Signed-off-by: dzdidi <dzdidi@users.noreply.github.com>
@dzdidi
dzdidi requested a review from Taewa August 28, 2026 11:25
@dzdidi

dzdidi commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

@Taewa I thought it was included into PR#10 before it was merged to master, but I was not. This should fix the issue you mentioned 🙏

@Taewa Taewa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved but AI found out two issues which is not a blocker but good to have. Feel free to update in follow-up PRs :)

A

Every upload downloads the whole file again.

The readback pulls the full resource but uses only the content type. path is
our own argument; hash and size cannot differ, since the lookup returns
Some only when the hash matches. So a 10 MB upload moves 20 MB and holds two
copies in memory — on every upload, not just failures.

Fix: have upsert_guarded_resource return the stored record, so the Pubky
adapter reports the content type without shipping bytes back. This also drops
the second port call from the use case.

B

A successful upload is reported as a 400.

get_guarded_resource returns Ok(None) for missing object, write not visible
yet, concurrent overwrite, and hash mismatch. None of these mean the request was
invalid, but all become 400 invalid_request — which tells clients not to retry,
exactly when retrying is right. The bytes also stay on the homeserver with no
descriptor and no delete, so nothing can reference them.

Concurrent case: req1 writes A, req2 writes B, req1 reads back B, hash mismatch,
req1 gets 400 for a write that succeeded.

Fix: map readback failure to ApplicationError::Storage (5xx, retryable), and
delete the object on the hash-mismatch branch. Telling those cases apart needs a
three-state result from the port — found / missing / mismatch.

Signed-off-by: dzdidi <dzdidi@users.noreply.github.com>
Signed-off-by: dzdidi <dzdidi@users.noreply.github.com>
Signed-off-by: dzdidi <dzdidi@users.noreply.github.com>
@dzdidi

dzdidi commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

@Taewa updated

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants