8000 blossom: implement BUD-05 without optimizations by sudocarlos · Pull Request #45 · fiatjaf/khatru · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

blossom: implement BUD-05 without optimizations #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8000
Merged
merged 6 commits into from
May 8, 2025

Conversation

sudocarlos
Copy link
Contributor

Adds BUD-05 without optimizations by redirecting with HTTP 307 to /upload as suggested here bitvora/haven#75 (comment)

@fiatjaf
Copy link
Owner
fiatjaf commented May 7, 2025

Was this AI-generated?

@sudocarlos
Copy link
Contributor Author

No, i just figured it would look dumb if i only included the redirect 307) that i need and not the others. How bad is it?

@aaccioly
Copy link
Contributor
aaccioly commented May 7, 2025

@sudocarlos , since this has not been merged yet. maybe you can add the headers as well?

Just

w.Header().Set("Content-Length", strconv.Itoa(bd.Size))
w.Header().Set("Accept-Ranges", "bytes")

In handleHasBlob should do the trick if I understood the code and BUD-01 correctly. Fingers crossed, this will fix the blossom.band mirroring compatibility issue as per @fishcakeday comment on Haven's repo.

As for the redirects, I think that going with code 307 is fine by the way. We can add support for other types of redirects as needed. Honestly, in practice, I doubt that we'll need anything, but 307 redirects any time soon.

@sudocarlos sudocarlos force-pushed the master branch 2 times, most recently from 94685d7 to 1f5380f Compare May 7, 2025 18:04
@sudocarlos
Copy link
Contributor Author

that worked to add the content-length

$ curl --head https://nostr.sudocarlos.com/330dedbe5b1e150007d1249b6ada9d3f8034787c8f30c8627b4cd55121cc51d3.jpg
HTTP/2 200 
accept-ranges: bytes
alt-svc: h3=":443"; ma=2592000
date: Wed, 07 May 2025 18:06:26 GMT
server: Caddy
vary: Origin
content-length: 273308

But blossom.band is still returning HTTP 400, new error: Error uploading the file: Unable to determine file type
@fishcakeday - i don't see where this is specified here https://github.com/hzrd149/blossom/blob/master/buds/01.md#head-sha256---has-blob

@aaccioly
Copy link
Contributor
aaccioly commented May 7, 2025

Blossom.band could infer the content type from the extension as per BUD-04:

Servers should re-use the Content-Type header returned from the URL to discover the mime type of the blob. if none is returned it may use the file extension in the URL

This may be actually be a bug since Neither Content-Type nor Content-Length are guaranteed to be be there assuming that "should" in Blossom specs has the same "recommended" meaning as in RFC 2119 (@hzrd149, correct me if I'm wrong).

However, providing a proper /HEAD implementation sounds like the right thing to do, and it is an easy enough fix on our side. Can you add the Content-Type header in the same place?

@sudocarlos
Copy link
Contributor Author

yeaaaa we did it! (mostly you)

@aaccioly
Copy link
Contributor
aaccioly commented May 7, 2025

I wrote zero code 🤣. Congrats! 🎉 I'll wait for this to be merged and fiatjaf to release a new version and update Haven accordingly. Out of curiosity, did this fix the Primal / IOS bug as well?

@sudocarlos
Copy link
Contributor Author

After adding Content-length and Content-type headers to handleHasBlob() there are no errors observed with Primal. Other commits resolved other issues described in previous comments

@sudocarlos
Copy link
Contributor Author

No, the iOS bug still exists

@aaccioly
Copy link
Contributor
aaccioly commented May 7, 2025

Good stuff. Congrats again.

No, the iOS bug still exists

I'm a bit a lost with this one then. Is there anything obviously in the response body and headers of PUT /{SHA256}, GET /{SHA256} or HEAD /{SHA256} between Khatru 's Blossom server vs Primal and Nostr.band (assuming that both of them are working correctly with Primal on iOS?). If not I think that we'll need to fight this fight another day.

@sudocarlos
Copy link
Contributor Author

I am using Proxyman on iOS to see whats going on with the packets. Just as with primal.net, primal.iOS is using PUT /media, not PUT /upload. primal.iOS is successful in putting the media on my server and blossom.band but the thumbnail disappears from the note draft like it failed. i don't have this problem in primal.net and the media is successfully mirrored to all blossom servers.

@sudocarlos
Copy link
Contributor Author

@mbrat1

@sudocarlos
Copy link
Contributor Author

primal.iOS works fine with blossom.primal.net or blossom.band as primary.

@aaccioly
Copy link
Contributor
aaccioly commented May 7, 2025

Just as with primal.net, primal.iOS is using PUT /media, not PUT /upload. primal.iOS is successful in putting the media on my server and blossom.band but the thumbnail disappears from the note draft like it failed. i don't have this problem in primal.net and the media is successfully mirrored to all blossom servers.

Can you post the response body + headers from Primal Blossom /PUT MEDIA {SHA256} vs the same for Khatru? Are they doing any other requests just after the upload? (I.e., OPTION/HEAD/GET /{SHA256} or maybe listing the media for your pubkey). Is so, any differences in body or headers to Khatru?

@fishcakeday
Copy link

Blossom.band could infer the content type from the extension as per BUD-04:

Servers should re-use the Content-Type header returned from the URL to discover the mime type of the blob. if none is returned it may use the file extension in the URL

This may be actually be a bug since Neither Content-Type nor Content-Length are guaranteed to be be there assuming that "should" in Blossom specs has the same "recommended" meaning as in RFC 2119 (@hzrd149, correct me if I'm wrong).

However, providing a proper /HEAD implementation sounds like the right thing to do, and it is an easy enough fix on our side. Can you add the Content-Type header in the same place?

Extension is optional, having content-type/length is required in majority of the cases (ex.: Amethyst uses content type to decide how to render the media), and it is just a good practice overall to serve all relevant headers to tell downloader what they are getting before they start downloading.

@fishcakeday
Copy link
8000 fishcakeday commented May 7, 2025

Check CORS headers, not sure if you are adding them to any of the endpoints.

@aaccioly
Copy link
Contributor
aaccioly commented May 8, 2025

Extension is optional, having content-type/length is required in majority of the cases

I do agree with you. the specs probably should use MUST here. However… and I know I’m sidetracking a bit into something unrelated to the issue (Khatru will be a good citizen and serve both headers with today’s patch), here’s my two cents from a practical standpoint: I would strongly encourage you not to rely solely on or mandate Content-Length and Content-Type headers for the following reasons:

  1. Encrypted attachments for Gift-wrapped DMs
  2. Blossom servers redirecting to blob storage and CDNs: Content-Type is often incorrect in this case, especially if the client didn’t provide a file extension and the Blossom server didn’t add one during the upload to third-party storage or CDN.
  3. Reverse proxies, content negotiation, and compression. For example, the patch above will return the wrong content length if something like gzip compression is negotiated upstream and the middleman doesn’t override the header.
  4. And most importantly: clients will be clients, users will be users, and non-formal or machine-executable specs will always be a bit loose. I’ve had to tweak bits and bobs on both Haven and Khatru for each major Nostr client implementation of Blossom so far, and there’s always something new 🤣. Since you’re running a much larger Blossom infrastructure, the more lenient and flexible your server is, the fewer random issues you’ll need to deal with.

If you ask me, while it’s a bit more work for Blossom server developers, I would likely change the specs to assume clients will do the least amount of work possible, and servers should be as lenient as they can. Ideally, the specs would enforce that Content-Type and Content-Length be inferred from the file contents during upload. If you can't trust client headers, you can’t really trust the file extension either. In my view, the type field in the blob descriptor should be mandatory and default to application/octet-stream if server-side type inference fails.

But this is a much broader discussion, with its own trade-offs.

CORS

CORS headers are already being served with a fairly lenient policy:

khatru/handlers.go

Lines 30 to 40 in 3da898c

corsMiddleware := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{
http.MethodHead,
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
},
AllowedHeaders: []string{"Authorization", "*"},

Unfortunately, the issue isn’t reproducible in the web version of Primal, and there’s plenty of weirdness in the HTTP spec (including additional CORS-related stuff like credentials and exposed headers) that might be necessary depending on what Primal is doing. That’s why I’m curious about the specific Primal requests and Khatru responses on iOS, in comparison to blossom.band or Primal’s own Blossom server. I don’t have an iPhone myself, but hopefully Carlos or one of the Primal devs can shed some light on exactly what their upload service is doing on iOS.

@fiatjaf
Copy link
Owner
fiatjaf commented May 8, 2025

Extension is not optional. Most clients use the extension to decide how to render a media file and if they will download it at all, and that's how it should be because it's simpler and more efficient for everybody.

@aaccioly
Copy link
Contributor
aaccioly commented May 8, 2025

Extension is not optional.

This is another thing I’d like to change in the specs. I'm sure @hzdr149 had valid reasons for making extensions optional, like encrypted content in Gift Wrapped messages where we might not want to reveal the file type, but the whole “optional extension” approach is... confusing.

For example, a user might upload a JPEG image without an extension and later request {image SHA256}.pdf. What do we do in that case? Serve the image with a Content-Type: application/pdf?

It would be much better if extensions were mandatory. Though I still wouldn’t rely on them for Content-Type anyway. IMO, the most reliable approach here is to always introspect the file during upload, store the type in the Blob Descriptor and serve the blob using the detected Content-type.

By the way, @barrydeen: as part of Haven’s breaking changes / major release where we’ll be separating the Blossom DB, I’ll take the opportunity to also rename the blobs in the Blossom folder to include the file extension. Since I’ll have to move every blob descriptors to the new database anyway, we might as well make our lives easier for when Haven supports uploading / redirecting blobs to S3-like services.

@fiatjaf fiatjaf merged commit 1ab44ab into fiatjaf:master May 8, 2025
@aaccioly
Copy link
Contributor
aaccioly commented May 8, 2025

@sudocarlos, congrats for the merge. If you want to troubleshoot the iOS stuff open another bug. I'll get the new version of Khatru updated on Haven as soon as I'm back home today.

@sudocarlos
Copy link
Contributor Author

@fiatjaf Can you tag this one?

@hzrd149
Copy link
hzrd149 commented May 13, 2025

@fiatjaf sorry for jumping in late, but I feel its worth responding to your comment.

Extension is not optional. Most clients use the extension to decide how to render a media file and if they will download it at all, and that's how it should be because it's simpler and more efficient for everybody.

File extensions SHOULD be included in the returned "blob descriptor" in the url field, because this is what clients will use to embed the blob into nostr notes and other stuff.

However making the file extension mandatory wont prevent clients from requesting the blob with different or random file extensions. and if servers start rejecting requests because "the ext is wrong" it will start to undermine clients ability to quickly fallback to other servers to find missing blobs

In the example @aaccioly mentioned

For example, a user might upload a JPEG image without an extension and later request {image SHA256}.pdf. What do we do in that case? Serve the image with a Content-Type: application/pdf?

Its best if the server returns the content-type it thinks the blob is ( either saved on initial upload or guessed based on introspection ) and then falls back to application/octet-stream
The alternative would be for the server to return a HTTP 400 which would hurt the users ability to find a missing blob if they only had the hash and forgot the correct file e 9E88 xtension

@aaccioly
Copy link
Contributor

Its best if the server returns the content-type it thinks the blob is ( either saved on initial upload or guessed based on introspection ) and then falls back to application/octet-stream

I agree with this. But then again, making the file extension mandatory doesn't mean we can't serve the same blob without an extension (or with a different one) and override the correct Content-Type as you said above. When redirecting to non-Blossom servers, we can also change the file extension.

Nevertheless, for this to work in practice, I still believe we need to update the specs:

  1. Make the file extension mandatory during upload. Or at least, the server should always detect and add a file extension to the blob unless it can't detect one (in which case it should only return the hash in the Blob descriptor and fall back to application/octet-stream).

  2. The type field should no longer be optional in the BlobDescriptor (falling back to application/octet-stream).

  3. Server-side file detection must be the authoritative source for Content-Type in all situations (GET, HEAD, MIRROR, etc).

This would resolve the current ambiguity without making discoverability harder.

@fiatjaf
Copy link
Owner
fiatjaf commented May 13, 2025

File extensions SHOULD be included in the returned "blob descriptor" in the url field, because this is what clients will use to embed the blob into nostr notes and other stuff.

However making the file extension mandatory wont prevent clients from requesting the blob with different or random file extensions. and if servers start rejecting requests because "the ext is wrong" it will start to undermine clients ability to quickly fallback to other servers to find missing blobs

Thank you, makes sense and sounds good to me, except I think the blob descriptor should be MUST. I think it should be the server's job to inspect the blob and determine the content-type, if the user doesn't provide one. I don't know what to do if the user provides an invalid extension. Maybe it's ok if the server trusts that or if it overrides that, it will depend on the server and that's ok.

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.

5 participants
2A81
0