NAME

Net::Blossom::Server::Storage - Storage contract for Blossom servers

SYNOPSIS

package My::Storage;

sub begin_upload { ... }
sub get_blob     { ... }
sub delete_blob  { ... }
sub list_blobs   { ... }

Net::Blossom::Server::Storage->assert_implements($storage);

DESCRIPTION

Net::Blossom::Server::Storage documents and validates the role-style storage contract used by Net::Blossom::Server. It is not a base class. Storage implementations only need to provide the required methods.

The Blossom server core owns SHA-256 calculation. Storage implementations do not calculate or trust blob hashes. For uploads, storage receives bytes through an upload writer and receives the computed sha256, size, type, uploaded, and optional pubkey values at commit time.

IMPLEMENTATION NOTES

Backends normally store blob bytes, descriptor metadata, and owner metadata. The descriptor URL is deployment policy, so a storage constructor will usually take a public base_url and use it when building descriptors.

A minimal upload writer records bytes in write, makes the blob and owner relationship visible in commit, and discards unfinished bytes in abort:

sub begin_upload { ...; return My::Upload->new(...) }
sub write        { ... }
sub commit       { ...; return { descriptor => $blob, created => $created } }
sub abort        { ... }

Database backends can implement pagination by ordering owner rows with:

ORDER BY uploaded DESC, sha256 ASC

When cursor is present, first find that descriptor for the pubkey, then return rows after it in the same order.

Filesystem backends should write uploads to a temporary path and move bytes into place only during commit, reusing the existing SHA-256 path for duplicate bytes. They still need a metadata index for owners, descriptors, ordering, and cursor lookup; scanning blob files alone is not enough.

STORAGE METHODS

begin_upload

my $upload = $storage->begin_upload(%context);

Starts an upload and returns an upload writer object. The context includes type and may include expected_sha256, allowed_sha256, content_length, and pubkey. The upload writer must provide write, commit, and abort.

No blob or owner relationship may become visible until commit succeeds.

get_blob

my $result = $storage->get_blob($sha256, %opts);

Returns a Net::Blossom::Server::BlobResult for an available blob, or undef when the blob is not available. The result contains both the Net::Blossom::BlobDescriptor and the blob body as a scalar, an array reference of scalar chunks, or a stream object with read or getline.

head_blob

my $descriptor = $storage->head_blob($sha256);

Optional method used by HEAD /<sha256>. Storage implementations with large blobs should provide this to avoid loading blob bodies for metadata-only requests. It may return a Net::Blossom::BlobDescriptor, a Net::Blossom::Server::BlobResult, or undef when the blob is unavailable. When absent, the server falls back to get_blob.

delete_blob

my $deleted = $storage->delete_blob($sha256, %opts);

Deletes one owner relationship for a blob. When pubkey is passed in %opts, only that pubkey's ownership is removed. Storage may remove the underlying bytes after the final owner is deleted. Returns true when something was deleted and false when the blob or owner relationship was not available.

list_blobs

my $blobs = $storage->list_blobs($pubkey, %opts);

Returns an array reference of Net::Blossom::BlobDescriptor objects uploaded by $pubkey, sorted by uploaded descending and sha256 ascending. cursor is the SHA-256 hash of the last blob from the previous page and must be excluded from the returned page. The next page starts after that descriptor in the ordered list. An unknown cursor returns an empty page. limit, when present, caps the number of descriptors returned and must be honored.

UPLOAD WRITER METHODS

write

$upload->write($bytes);

Writes a byte chunk. The server core calls this while it hashes the same bytes.

commit

my $result = $upload->commit(%metadata);

Commits the upload after the server core has computed and validated the SHA-256 hash. %metadata includes sha256, size, type, uploaded, and optionally pubkey. The method should return either a Net::Blossom::Server::UploadResult object or a hash reference with descriptor and created. descriptor may be a Net::Blossom::BlobDescriptor object or a hash reference suitable for Net::Blossom::BlobDescriptor->from_hash. created must be 1 when the blob bytes were newly stored and 0 when the blob already existed.

For pre-release compatibility, raw Net::Blossom::BlobDescriptor objects and descriptor hash references are accepted and treated as created => 1.

commit must be atomic from the caller's view. After a successful commit, the blob and owner relationship must be visible. If commit fails, neither may be visible.

abort

$upload->abort;

Aborts an upload after validation or write failure. abort should be safe to call more than once. Aborted uploads must not become visible through get_blob or list_blobs.

METHODS

required_methods

my @methods = Net::Blossom::Server::Storage->required_methods;

Returns the required storage method names.

required_upload_methods

my @methods = Net::Blossom::Server::Storage->required_upload_methods;

Returns the required upload writer method names.

assert_implements

Net::Blossom::Server::Storage->assert_implements($storage);

Croaks unless $storage is an object that provides the required storage methods. Returns true otherwise.

assert_upload

Net::Blossom::Server::Storage->assert_upload($upload);

Croaks unless $upload is an object that provides the required upload writer methods. Returns true otherwise.

BACKEND TESTS

Backend distributions should use "run_storage_contract_tests" in Net::Blossom::Server::Storage::Test to verify that their storage implementation satisfies this contract.