Public Buckets

Mark a bucket public to serve its files at stable, edge-cached URLs through the Koolbase CDN. No signing, no expiry — paste a URL into an <img> tag, share it in an email, or embed it anywhere a browser can read.

How public buckets work

Files uploaded to a public bucket land in dedicated public storage backed by the Koolbase CDN. Each file gets a permanent URL at the Koolbase CDN domain, with the project ID, bucket name, and path forming the three segments:

https://cdn.koolbase.com/{project_id}/{bucket}/{path}

That URL serves the file directly from a global edge network — no signing, no time-bound expiry, no round-trip to Koolbase's API on every read. The first request to a region warms the cache; subsequent requests are served from the edge.

When to use them

Public buckets fit anywhere you need a URL that can be:

  • Embedded in HTML (<img>, <video>, <audio>)
  • Linked from email, social media, marketing copy
  • Cached at the edge for fast global delivery
  • Read without authentication or signing

Common use cases:

  • User profile photos and avatars
  • Product images for an e-commerce app
  • Marketing assets — banners, hero images, OG cards
  • Public PDFs, brochures, downloadable installers
  • Static fonts and webfont assets

Don't use public buckets for sensitive data

Public bucket files are accessible to anyone with the URL. Use a private bucket for content that should require authentication — personal documents, private photos, billing records, anything you wouldn't want indexed by a search engine that crawled a leaked URL.

Creating a public bucket

In the dashboard:

  1. Open the project, navigate to Storage
  2. Click the + button next to Buckets
  3. Enter a bucket name
  4. Toggle Public Access on
  5. (Optional) Configure size limits and MIME type restrictions
  6. Click Create bucket

Public access is set at creation time

A bucket's public flag can't be changed after the bucket exists. That protects against accidentally exposing private data by flipping a toggle later. If you need to change visibility, create a new bucket with the right setting and copy the files over.

Getting the public URL

From the dashboard

  1. Open the public bucket from the Storage page
  2. Find the file row
  3. Click the copy icon (between Download and Delete)
  4. The URL is on your clipboard, ready to paste anywhere

From the SDK

The SDK's getDownloadUrl() method already returns the CDN URL for files in public buckets — no method change needed, no branching on bucket type.

Flutter
Dart
final url = await Koolbase.storage.getDownloadUrl(
  bucket: 'avatars',
  path: 'user-123.png',
);

// For public bucket files, url is the CDN URL — long-lived,
// embeddable anywhere a browser can fetch. For private bucket
// files, url is a 1-hour presigned URL.
print(url);
React Native
TypeScript
const { url } = await koolbase.storage.getDownloadUrl({
  bucket: 'avatars',
  path: 'user-123.png',
});

// For public bucket files, url is the CDN URL — long-lived,
// embeddable anywhere a browser can fetch. For private bucket
// files, url is a 1-hour presigned URL.
console.log(url);

Building the URL in-app (Flutter v11.4+)

When you have a bucket and path but no object in hand — an image whose path comes out of a database record, for instance — build the CDN URL synchronously with publicUrlFor(). No network call, no projectId argument: the SDK learns its project identity from the bootstrap it already performs at startup.

Flutter
Dart
final url = Koolbase.storage.publicUrlFor(
  bucket: 'avatars',
  path: record['avatar_path'],
  // Optional edge resize — served transformed from the CDN:
  transform: const KoolbaseImageTransform(width: 200, height: 200),
);

Image.network(url);

It throws KoolbaseStorageProjectIdentityException (code project_identity_unavailable) only when the very first bootstrap has never completed — a fresh install starting offline. That state is transient and heals automatically once the device is online; it is deliberately distinct from KoolbaseObject.publicUrl returning null, which means the object lives in a private bucket and has no public URL at all.

Constructing URLs directly

For consumers outside the SDK — static site generation, server-side rendering, scripts — public bucket URLs follow a predictable pattern (inside a Flutter app, prefer publicUrlFor() above so URL topology stays the SDK's concern):

https://cdn.koolbase.com/{project_id}/{bucket}/{path}

URL-encode each path segment individually (preserving the / separators) so spaces, parentheses, and other special characters don't corrupt the URL.

Embedding public files

Public bucket URLs work directly anywhere a browser fetches a URL:

HTML
HTML
<img src="https://cdn.koolbase.com/proj_abc/avatars/user-123.png" alt="User avatar" />

<video src="https://cdn.koolbase.com/proj_abc/videos/intro.mp4" controls />

<a href="https://cdn.koolbase.com/proj_abc/docs/manual.pdf">Download manual</a>
CSS
CSS
.hero {
  background-image: url('https://cdn.koolbase.com/proj_abc/marketing/hero.jpg');
}

For programmatic fetch() calls from JavaScript on other origins, public buckets are CORS-configured to allow GET requests from any origin out of the box. Custom CORS rules for specific origins or methods can be added per-bucket if you need finer control.

Cache behavior

Public bucket responses are cached at the CDN edge per the standard caching policy. When you overwrite a file at the same path, the new bytes become immediately readable from origin requests, but cached edge copies may persist for some time before refreshing.

Bust the cache with a query string

For frequently-updated content, append a version or timestamp query string to bust the cache predictably: cdn.koolbase.com/.../hero.jpg?v=2. The CDN treats each distinct query string as a different cache key, so updating the value forces a fresh fetch.

Security considerations

Public bucket files are accessible to anyone with the URL. Path obscurity isn't a meaningful access control — assume URLs will eventually be shared, indexed, or guessed.

  • Don't store secrets in public files — API keys, tokens, credentials, password hashes — including in custom metadata fields, which are also publicly readable
  • Don't infer access permission from whether you've shared a URL; once a URL is out, it's out
  • For user-private content, use a private bucket and serve via the SDK's getDownloadUrl() — the returned presigned URL is short-lived and tied to whoever requested it
  • Treat public bucket URLs the same way you'd treat any other public web resource