How ToGaFor developerFor agencyFor operator

Store files uploaded by your application

Upload, validate, deliver and manage application media without exposing storage credentials.

Last updated 2026-08-29

What you'll achieve

  • Upload end-user media without exposing provider credentials
  • Choose safe public or short-lived private delivery
  • Recover from scanning, quota and transfer failures

What Hosted Object Storage is for

Hosted Object Storage stores images, audio, video and PDF files uploaded by people using your deployed application. It is separate from Project Assets, which are files you add while building in the BlinkHost IDE. Stored objects use the same workspace storage allowance shown for your plan.

Supported types are PNG, JPEG, GIF, WebP, MP3, WAV, OGG, MP4, WebM and PDF. A file can be up to 100 MB. Active formats such as HTML, JavaScript and SVG are not accepted as runtime uploads.

Upload from a backend module

Create an upload from trusted backend code, return the short-lived upload_url and required_headers to your frontend, upload the bytes directly, then ask the backend module to complete the upload. Compute SHA-256 in the browser before creating the upload. Use a unique idempotency key for the logical upload attempt.

reservation = blinkhost.create_object_upload(
    key=f"profiles/{user_id}/avatar.png",
    name="avatar.png",
    media_type="image/png",
    size_bytes=size_bytes,
    checksum_sha256=sha256,
    idempotency_key=request_id,
    visibility="public",
    owner_reference=f"user:{user_id}",
)

Upload the file with an HTTP PUT to upload_url. Send every header in required_headers exactly as returned. Do not store or reuse the URL; it is write-only and expires after 15 minutes.

const digest = [...new Uint8Array(await crypto.subtle.digest("SHA-256", await file.arrayBuffer()))]
  .map((byte) => byte.toString(16).padStart(2, "0"))
  .join("");

// Your application backend creates the reservation after authenticating the user.
const reservation = await createUpload({
  name: file.name,
  mediaType: file.type,
  sizeBytes: file.size,
  checksumSha256: digest,
});

const uploaded = await fetch(reservation.upload_url, {
  method: "PUT",
  headers: reservation.required_headers,
  body: file,
});
if (!uploaded.ok) throw new Error("The file could not be uploaded.");
result = blinkhost.complete_object_upload(reservation["upload_id"])

Completion can return scan_pending. Wait for retry_after_seconds and retry completion with the same upload ID. Never start a second upload merely because scanning is still in progress.

The Go SDK provides typed CreateObjectUpload, CompleteObjectUpload, HeadObject, ListObjects, DeleteObject and ObjectDownloadURL helpers. Rust modules can use object_storage_request with the same operation payload. SDK calls always run within the current deployed project and environment; storage credentials are never available to application code.

Public and private objects

A public object receives a stable, site-scoped public_url after validation. The URL supports browser caching, conditional requests and media byte ranges. A private object never receives a public URL; request a five-minute download URL from trusted backend code when an authorised person needs it.

Preview uploads are private and isolated from production. Production does not read a preview object when a production object is missing.

Keys, replacement and deletion

Keys are paths such as products/42/cover.webp. A completed upload to an existing key atomically replaces its logical value; the previous value remains available until the replacement has passed validation. Listing is prefix-based and paginated. Delete and download operations can use either the object ID or the exact key.

Keep your application user identifier in owner_reference when you need per-user authorization. BlinkHost scopes every operation to the current project and binding, but your application remains responsible for deciding which signed-in user may create, read or delete each object.

Security and limits

Uploads enter private quarantine through a short-lived write-only capability. BlinkHost verifies size, SHA-256, declared media type and file signature, and requires a clean malware scan before promotion to private validated storage. Provider credentials are not exposed to your code or browser.

Committed objects and active upload reservations count against your workspace storage allowance. A replacement reserves only the net amount needed after the current object. Limits and concurrent-upload controls fail before a new capability is issued.

Handle errors

SDK errors include a stable code, a safe message, a retryable flag and a request_id. Preserve the request ID when contacting support, but never include upload URLs because they are temporary credentials.

  • Retry scan_pending, storage_unavailable and other responses marked retryable with bounded exponential backoff.
  • Start a new upload after upload_expired.
  • Ask the user to choose another file after a checksum, media, content or malware rejection.
  • Show storage usage and upgrade options after storage_limit_exceeded.
  • Treat object_not_found the same whether the key is absent or belongs to another project.

Do not log object URLs, private file contents, authentication headers or user-delegation query parameters.

Help improve this page

Sign in to send page-specific feedback. For account-specific help, email support@blinkhost.me.