Buckets
Buckets are top-level containers for your files. Create them from the dashboard before uploading.
Creating a bucket
Navigate to your project in the dashboard, select Storage, and click New Bucket. Give the bucket a name — this is what you'll reference in the Flutter SDK.
Buckets must be created before uploading
Koolbase.storage.upload() with a bucket name that doesn't exist will return a 404 error. Always create buckets from the dashboard first.Access model
Every bucket has one of three access behaviors, chosen at creation and immutable after:
Public
Anyone with the URL can read. Files get stable CDN URLs — right for assets that are meant to be seen: product images, published content, avatars in a social app. See Public Buckets.
Private — Project mode (default)
Any authenticated user of your project can read and write any path. Reads go through time-limited signed URLs. Right for shared app content where all signed-in users may see everything.
Private — Owner mode
Each user can only read, overwrite, or delete files they uploaded — enforced on every SDK operation, including version history. Uploads require a signed-in user. Right for per-user private files: documents, health data, private photos.
In Project mode, paths are not privacy
avatars/{user_id}.jpg in a project-mode bucket is readable by every signed-in user of your project — anyone can construct another user's path. If files are per-user sensitive, use Owner mode. If you stay in Project mode, store objects under random, unguessable keys and reference them from database rows protected by read: owner rules — the database, not the object path, is your ownership boundary.Owner-mode details worth knowing: denials answer 404 not-found — never confirming whether a path exists to someone who doesn't own it. Attempting to upload over another user's path answers 409. Uploads without a signed-in user are rejected with a clear error. Project admins working through the dashboard see and manage all files regardless of mode. Public + Owner is contradictory and rejected at creation.
Naming rules
Bucket limits
Each bucket can be configured with three optional limits that the server enforces at upload time, before file bytes ever reach R2. Set them in the bucket creation modal in the dashboard (or via the bucket API).
max_size_bytesTotal bucket size cap. The sum of all file sizes in the bucket cannot exceed this. Leaving it unset means no quota.
max_file_size_bytesPer-file size cap. Each upload is checked against this independently. Leaving it unset means no per-file limit.
allowed_mime_typesList of allowed content-types for uploads. Supports family wildcards: image/* matches every image type (PNG, JPEG, WebP, AVIF, ...). An empty or unset list accepts any type.
Limits are enforced at presign time
KoolbaseStorageQuotaException, KoolbaseStorageFileTooLargeException, KoolbaseStorageMimeTypeException in Flutter; the matching Error classes in React Native and the browser) so no bandwidth is wasted on uploads that won't be accepted. See Upload Files for handling.Recommended bucket structure
Organise buckets by file type or feature rather than by user. Use the file path within the bucket to scope files per user:
avatars/user_{userId}.jpgProfile photos, one per user
attachments/posts/{postId}/{filename}Post attachments, scoped by post
documents/{userId}/{filename}User documents, scoped by user ID
thumbnails/products/{productId}.jpgProduct images, scoped by product
Deleting a bucket
Buckets can be deleted from the dashboard. Deleting a bucket removes all files within it from R2 and removes all file metadata from the database. This action is permanent and cannot be undone.