NAME

Net::Blossom::Server - Server-side support for the Blossom protocol

SYNOPSIS

use Net::Blossom::Server;

my $server = Net::Blossom::Server->new(
    storage => $storage,
);

DESCRIPTION

Net::Blossom::Server is the framework-neutral server core for the Blossom protocol. Gateway adapters such as PSGI or PAGI should translate native requests into Net::Blossom::Server::Request objects and translate Net::Blossom::Server::Response objects back to their gateway format.

Server support lives in a separate CPAN distribution so client users do not need server, storage, daemon, or web framework dependencies.

CONSTRUCTOR

new

my $server = Net::Blossom::Server->new(%args);

Required arguments:

Optional arguments:

  • chunk_size

    Positive integer read size used when copying stream bodies. Defaults to 65536.

  • clock

    Code reference returning the upload timestamp. Defaults to time.

  • mirror_fetcher

    Optional code reference or object with fetch_blob. This is required for PUT /mirror. Mirror fetchers stream origin bytes into a server-provided sink; no default network fetcher is provided.

  • max_upload_bytes

    Optional positive integer bounding the size of an accepted upload. When set, PUT /upload, PUT /media, and mirrored origin bodies that exceed it are rejected with 413 and the partial upload is aborted. Defaults to unset (no limit).

  • max_list_limit

    Positive integer maximum GET /list/<pubkey> page size. List requests without a limit query parameter use this value as the default. Requests with a larger limit are rejected with 400. Defaults to 100.

Unknown arguments or invalid values croak.

ACCESSORS

storage

Returns the configured storage object.

chunk_size

Returns the stream copy chunk size.

clock

Returns the clock code reference.

mirror_fetcher

Returns the optional mirror fetcher.

max_upload_bytes

Returns the configured maximum upload size, or undef when uploads are not size-limited.

max_list_limit

Returns the configured maximum list page size.

METHODS

receive_blob

my $result = $server->receive_blob($body, %opts);

Copies a scalar or stream body into storage while computing SHA-256 in the server core. Returns a Net::Blossom::Server::UploadResult.

Options:

  • type

    Blob media type. Defaults to application/octet-stream.

  • expected_sha256

    Optional lowercase 64-character SHA-256 hash. When present, the computed hash must match before the upload is committed.

  • allowed_sha256

    Optional array reference of lowercase 64-character SHA-256 hashes. When present, the computed hash must match one of these values before the upload is committed. This is used for deferred BUD-11 PUT /mirror authorization.

  • content_length

    Optional expected body size. When present, the copied byte count must match before the upload is committed.

  • uploaded

    Optional upload timestamp. Defaults to $server->clock->().

  • pubkey

    Optional uploader public key as lowercase 64-character hex. Gateway adapters will normally derive this from BUD-11 authorization.

  • sha256_mismatch_status

    Optional HTTP status code for SHA-256 mismatches. When supplied, mismatches throw Net::Blossom::Server::Error with this status instead of croaking with a plain string.

  • sha256_mismatch_reason

    Optional reason string used with sha256_mismatch_status.

  • content_length_mismatch_status

    Optional HTTP status code for content-length mismatches. When supplied, mismatches throw Net::Blossom::Server::Error with this status instead of croaking with a plain string.

  • content_length_mismatch_reason

    Optional reason string used with content_length_mismatch_status.

The storage upload is aborted if hashing, length validation, SHA-256 validation, or storage writing fails.

Storage commit results that are raw Net::Blossom::BlobDescriptor objects or descriptor hash references are accepted as newly created uploads for compatibility with early storage implementations. New storage implementations should return a Net::Blossom::Server::UploadResult or a hash reference with descriptor and created.

handle_upload

my $response = $server->handle_upload($request, %opts);

Handles a normalized PUT /upload request and returns a Net::Blossom::Server::Response. The request must be a Net::Blossom::Server::Request with a defined body.

The method passes the request body, content type, content length, optional X-SHA-256, and optional pubkey into receive_blob. When X-SHA-256 is present, a hash mismatch throws a typed 409 error before storage commit. Content-Length mismatches throw a typed 400 error before storage commit. The response body is the blob descriptor encoded as JSON. The response status is 201 when the blob was newly stored and 200 when it already existed.

Options:

  • pubkey

    Optional already-verified uploader public key as lowercase 64-character hex. Authorization verification is deliberately outside this method.

handle_head_upload

my $response = $server->handle_head_upload($request);

Handles a normalized HEAD /upload BUD-06 preflight request. It validates the X-SHA-256, X-Content-Type, and X-Content-Length headers and returns an empty 200 response when the metadata is well-formed. Malformed metadata returns 400; missing X-Content-Length returns 411.

handle_get_blob

my $response = $server->handle_get_blob($request);

Handles a normalized GET /<sha256> request and returns a Net::Blossom::Server::Response. The request path must contain one lowercase 64-character SHA-256 hash segment and may include a file extension.

The method calls $server->storage->get_blob($sha256). It returns 404 when storage returns undef. Otherwise, storage must return a Net::Blossom::Server::BlobResult whose descriptor sha256 matches the request path. The response status is 200, the response body is the blob body, and Content-Type and Content-Length come from the descriptor.

handle_head_blob

my $response = $server->handle_head_blob($request);

Handles a normalized HEAD /<sha256> request and returns the same Content-Type and Content-Length headers as GET /<sha256> without returning the blob body. The request path may include a file extension.

If storage provides an optional head_blob($sha256) method, that method is used and may return either a Net::Blossom::BlobDescriptor or a Net::Blossom::Server::BlobResult. Otherwise get_blob is used and the body is discarded.

handle_media

my $response = $server->handle_media($request, %opts);

Handles a normalized PUT /media request. The current implementation uses the same identity byte path as PUT /upload: it stores the received bytes without media transformation and returns a blob descriptor JSON response with 201 or 200. When X-SHA-256 is present, a hash mismatch throws a typed 409 error before storage commit. Content-Length mismatches throw a typed 400 error before storage commit.

Options:

  • pubkey

    Optional already-verified uploader public key as lowercase 64-character hex.

handle_head_media

my $response = $server->handle_head_media($request);

Handles a normalized HEAD /media BUD-05 preflight request. It validates the X-SHA-256, X-Content-Type, and X-Content-Length headers and returns the same status behavior as handle_head_upload.

handle_mirror

my $response = $server->handle_mirror($request, %opts);

Handles a normalized PUT /mirror request. The request body must be a JSON object with a url field. The URL must use http or https, have a host, must not include userinfo, and must not include a fragment.

The server calls the configured mirror_fetcher with a streaming sink and stores origin bytes as the fetcher writes them. SHA-256 calculation and storage commit remain owned by the server core. A missing mirror_fetcher returns 503. Malformed mirror requests return 400. Origin fetch failures, unusable fetch results, or origin content-length mismatches return 502.

The fetcher may be a code reference called with $url and sink => $sink, or an object called as $fetcher->fetch_blob($url, sink => $sink). It must call $sink->start(%metadata) before writing bytes, then call $sink->write($chunk) for each scalar byte chunk. %metadata may include type and content_length; missing type defaults to application/octet-stream. For empty origin bodies, the fetcher may instead return a metadata hash reference and let the server start the sink. Returning a body value is not supported.

Options:

  • pubkey

    Optional already-verified uploader public key.

  • authorization

    Optional Net::Blossom::Server::AuthorizationResult. When provided, the downloaded blob hash must match one of the authorized x tag hashes before storage commit, otherwise a typed 409 error is thrown.

handle_delete_blob

my $response = $server->handle_delete_blob($request, pubkey => $pubkey);

Handles a normalized DELETE /<sha256> request and returns a Net::Blossom::Server::Response. The request path must contain one lowercase 64-character SHA-256 hash segment.

pubkey is required and must be the already-verified owner public key as lowercase 64-character hex. Authorization event verification is deliberately outside this method.

The method calls $server->storage->delete_blob($sha256, pubkey => $pubkey). It returns 204 when storage deletes an owner relationship and 404 when storage returns false.

handle_list_blobs

my $response = $server->handle_list_blobs($request);

Handles a normalized GET /list/<pubkey> request and returns a Net::Blossom::Server::Response. The request path must contain one lowercase 64-character public key segment.

The method reads optional cursor and limit query parameters. cursor must be a lowercase 64-character SHA-256 hash. limit must be a positive integer no larger than max_list_limit. Requests without limit use max_list_limit as the default page size. Storage receives a bounded limit for every list request.

The method calls $server->storage->list_blobs($pubkey, %opts) and returns status 200 with a JSON array of blob descriptors. Storage must return an array reference of Net::Blossom::BlobDescriptor objects.

handle_request

my $response = $server->handle_request($request, %opts);

Dispatches a normalized Net::Blossom::Server::Request and returns a Net::Blossom::Server::Response. This is the framework-neutral routing entry point for future gateway adapters.

Currently implemented routes:

  • PUT /upload

    Delegates to handle_upload.

  • HEAD /upload

    Delegates to handle_head_upload.

  • PUT /media

    Delegates to handle_media.

  • HEAD /media

    Delegates to handle_head_media.

  • PUT /mirror

    Delegates to handle_mirror.

  • GET /<sha256>

    Delegates to handle_get_blob.

  • HEAD /<sha256>

    Delegates to handle_head_blob.

  • DELETE /<sha256>

    Delegates to handle_delete_blob.

  • GET /list/<pubkey>

    Delegates to handle_list_blobs.

Unknown paths return 404. Known paths with unsupported methods return 405.

Options:

  • pubkey

    Optional already-verified uploader public key. Passed through to upload, media, mirror, and delete handlers as needed.

  • authorization

    Optional Net::Blossom::Server::AuthorizationResult. Passed through to handle_mirror for deferred hash authorization.

STATUS

The server core is under active development. It implements the protocol handlers documented above and remains framework-neutral.