lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 041d7ba7b732ed8f8c1da869172297771435d5ff
parent bb54297d208f8e3817485d1c1a63a386f457349a
Author: triesap <tyson@radroots.org>
Date:   Sun, 13 Sep 2026 04:04:29 +0000

blossom: project the upload URL from a canonical blob reference

- Keep upload destinations separate from blob retrieval references
- Preserve exact origin hash and reference approval without I/O
- Cover endpoint variants across default and minimal features
- Verify the additive API and unchanged coverage and release gates

Diffstat:
Mcontracts/api_baselines/radroots_blossom.txt | 2++
Mcrates/blossom/README.md | 5+++++
Mcrates/blossom/src/url.rs | 12+++++++++++-
Mcrates/blossom/tests/parser_properties.rs | 32++++++++++++++++++++++++++++++++
4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/contracts/api_baselines/radroots_blossom.txt b/contracts/api_baselines/radroots_blossom.txt @@ -212,6 +212,7 @@ pub fn radroots_blossom::url::BlobUrl::is_loopback_http(&self) -> bool pub fn radroots_blossom::url::BlobUrl::parse(&str) -> core::result::Result<Self, radroots_blossom::Error> pub fn radroots_blossom::url::BlobUrl::port(&self) -> core::option::Option<u16> pub fn radroots_blossom::url::BlobUrl::scheme(&self) -> &str +pub fn radroots_blossom::url::BlobUrl::upload_url(&self) -> alloc::string::String impl core::fmt::Display for radroots_blossom::url::BlobUrl pub fn radroots_blossom::url::BlobUrl::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::str::traits::FromStr for radroots_blossom::url::BlobUrl @@ -300,6 +301,7 @@ pub fn radroots_blossom::url::BlobUrl::is_loopback_http(&self) -> bool pub fn radroots_blossom::url::BlobUrl::parse(&str) -> core::result::Result<Self, radroots_blossom::Error> pub fn radroots_blossom::url::BlobUrl::port(&self) -> core::option::Option<u16> pub fn radroots_blossom::url::BlobUrl::scheme(&self) -> &str +pub fn radroots_blossom::url::BlobUrl::upload_url(&self) -> alloc::string::String impl core::fmt::Display for radroots_blossom::url::BlobUrl pub fn radroots_blossom::url::BlobUrl::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::str::traits::FromStr for radroots_blossom::url::BlobUrl diff --git a/crates/blossom/README.md b/crates/blossom/README.md @@ -99,6 +99,11 @@ display, cache, or otherwise act on an unapproved URL. URL approval is a narrow transport policy, not host reputation, content safety, malware scanning, or application media policy. +`BlobUrl::upload_url` projects the BUD-02 `/upload` URL at the same scheme, +host and port. It preserves the canonical blob reference used for retrieval +verification and grants no transport authority. Upload callers must still +enforce their configured endpoint policy and bind authorization to exact bytes. + `ByteVerifiedDescriptor` can only be produced after an approved descriptor's hash, byte length, and approved media type match supplied bytes or a locally computed `ByteCommitment`. It proves local descriptor-to-byte agreement. It is diff --git a/crates/blossom/src/url.rs b/crates/blossom/src/url.rs @@ -6,7 +6,6 @@ //! the reference for transport; it does not perform a request or establish host //! reputation, byte integrity, authenticity, or application media safety. -#[cfg(feature = "serde")] use alloc::string::String; use alloc::string::ToString; use core::{fmt, str::FromStr}; @@ -55,6 +54,17 @@ impl BlobUrl { self.url.as_str() } + /// Returns the BUD-02 upload URL at this blob's exact origin. + /// + /// This is a structural projection, not transport authorization. Callers + /// must still enforce endpoint policy and bind the upload to exact bytes. + /// The canonical blob reference remains unchanged for retrieval verification. + pub fn upload_url(&self) -> String { + let mut url = self.url.clone(); + url.set_path("/upload"); + url.to_string() + } + pub fn scheme(&self) -> &str { self.url.scheme() } diff --git a/crates/blossom/tests/parser_properties.rs b/crates/blossom/tests/parser_properties.rs @@ -10,6 +10,38 @@ const ASCII_MUTATION_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._/:?#%\\ @\t\n\r\0;=+"; #[test] +fn upload_url_preserves_origin_without_replacing_or_approving_the_blob() { + let hash = Sha256::digest(b"bound upload bytes"); + for (origin, expected) in [ + ("https://media.example", "https://media.example/upload"), + ("https://media.example:443", "https://media.example/upload"), + ( + "https://media.example:8443", + "https://media.example:8443/upload", + ), + ("http://127.0.0.1:21100", "http://127.0.0.1:21100/upload"), + ("http://[::1]:21100", "http://[::1]:21100/upload"), + ( + "https://[2001:db8::1]:8443", + "https://[2001:db8::1]:8443/upload", + ), + ("http://insecure.example", "http://insecure.example/upload"), + ] { + for extension in ["", ".png", ".jpeg"] { + let blob = BlobUrl::parse(&format!("{origin}/{hash}{extension}")).unwrap(); + let original = blob.to_string(); + let approved_before = blob.clone().approve(); + assert_eq!(blob.upload_url(), expected); + assert_eq!(blob.upload_url(), expected); + assert_eq!(blob.to_string(), original); + assert_eq!(blob.hash_path().hash(), hash); + assert_eq!(blob.clone().approve(), approved_before); + assert!(BlobUrl::parse(&blob.upload_url()).is_err()); + } + } +} + +#[test] fn deterministic_parser_mutation_corpus_never_panics_and_round_trips_successes() { let fixed = [ "",