Skip to main content

Postgres-Native Cloud Storage

Before reading this section, see Postgres-Native overview for the high-level differences between Postgres-Native and the classic mode.

In Postgres-Native, cloud storage shares the same permission model as the database — file metadata lives in PostgreSQL's storage schema, and access control is fully described by RLS policies. There is no need for built-in permission tags such as READONLY / PRIVATE, nor for storage-specific JSON security rules. The very same SQL policies that protect your business tables also control file read/write.

Other cloud storage types

CloudBase also provides Classic Cloud Storage. Pick the one that matches your environment type.

Core differences from the classic mode

Postgres-Native differs from classic mode in three fundamental ways:

1. Bucket is a first-class citizen, with SQL-level per-bucket control

  • Classic mode: the environment is tightly bound to an underlying COS bucket. Developers mostly work with path prefixes rather than truly independent buckets, and permissions can only be chosen from a built-in mode at bucket creation time.
  • Postgres-Native: storage.buckets is a regular table in PostgreSQL, and each bucket is a row. You can:
    • configure per-bucket metadata such as public / file_size_limit / allowed_mime_types
    • reference bucket_id = 'xxx' directly inside RLS policies
    • manage bucket metadata via plain SQL — no console or dedicated API required.

2. Object permissions are entirely RLS-based — no more JSON security rules

  • Classic mode: permissions are expressed via built-in tags (READONLY / PRIVATE / ADMINWRITE / ADMINONLY / CUSTOM) or a separate JSON security-rule DSL, with granularity limited to bucket / path prefix / simple roles.
  • Postgres-Native: all read/write permissions are expressed via RLS policies on storage.objects / storage.buckets. storage.objects is GRANT ALL TO anon, authenticated, service_role by default — RLS is the sole gate. You can use arbitrary SQL expressions and JOIN business tables across schemas, sharing the same language and semantics as the database.

3. No direct client-to-COS upload — everything goes through the Storage API

To guarantee strong consistency across metadata, object bytes, and RLS checks, all reads and writes in Postgres-Native flow through the Storage API. It transactionally coordinates writes to storage.objects with COS operations, eliminating the "object exists in COS but no row in storage.objects" (or vice versa) orphan state.

DimensionClassic modePostgres-Native
Client upload pathSDK gets a temporary signature and uploads directly to COS; the backend does not handle bytesSDK → Storage API → COS, coordinated by a single entry point
Metadata write timingCOS callback / application backfillStorage API writes storage.objects inside the same transaction
owner_id injectionMaintained by the applicationStorage API injects the JWT sub automatically — cannot be forged by the client
RLS enforcementN/AWITH CHECK on INSERT blocks at the database layer; unauthorized requests never reach COS
Delete consistencySelf-checked by the applicationThe protect_delete trigger forces deletion through the Storage API: object first, then row
Large-file multipartDirect COS multipart uploadCoordinated via the storage.s3_multipart_uploads table
Reading file metadata (size / mimetype, ...)Extra calls to the COS APISELECT metadata FROM storage.objects, joinable with business tables
SDK recommendation

In Postgres-Native mode, prefer SDKs / APIs that use native bucket semantics, such as app.storage.from(bucketId) in JS SDK, the tcb storage objects command group in CLI, or the Postgres-Native Cloud Storage HTTP API. See SDK Guide for complete examples.

Differences at a glance

DimensionClassic cloud storagePostgres-Native cloud storage
Bucket conceptTightly bound to environment, mainly used as a path prefixA row in storage.buckets, fully manageable via SQL
Permission descriptionBuilt-in modes or JSON security rulesRLS policies (SQL)
GranularityBucket / path prefix / simple rolesAny SQL expression, with cross-table JOINs
Metadata accessConsole / SDKDirect SQL on storage.objects
Linkage with business dataSynced manuallySame instance, same schema; JOIN directly
Upload pathClient uploads directly to COSClient → Storage API → COS
How it is selectedPick a permission mode when creating a bucketDetermined automatically by the environment type

In a Postgres-Native environment, storage permission checks automatically go through RLS based on the environment type. Developers do not need to switch anything manually.

Architecture

Client (Web / Mini Program / Cloud Function)
│ Authorization: Bearer <Token>

CloudBase Gateway
│ Parse JWT → set role + request.jwt.claims

Storage API
│ Read / write metadata in the storage schema

PostgreSQL · storage schema ←┐
├─ storage.buckets │ RLS policy check
├─ storage.objects │
└─ storage.s3_multipart_uploads ←┘


Object storage backend (COS) ← actual file bytes

Key facts:

  • File bytes live in the object storage backend; metadata lives in the storage schema.
  • storage.buckets / storage.objects are by default GRANT ALL TO anon, authenticated, service_rolethe only gate is the RLS policy. (This is the key difference from business tables, which use a "GRANT + RLS" two-layer model.)
  • You cannot run DELETE FROM storage.objects directly: the table has a protect_delete trigger. Deletion must go through the Storage API to avoid orphaned objects in the storage backend.

How to read this section

GoalRecommended reading
First time here, want to run Hello WorldQuick experience
Design buckets, public / private access, and upload limitsBucket Management
Upload files, handle overwrites and custom metadataUpload Files
Download files, create signed URLs or public URLsAccess and Download Files
List, copy, move, or delete filesManage Files
Understand the schema and permission modelPermission management
Copy-paste RLS templatesRLS policy patterns
Check naming, size, MIME, and overwrite recommendationsLimits and Best Practices
Understand CDN cache and Public / Private cache differencesCDN Acceleration and Caching
Migrate from Supabase StorageMigrate from Supabase Storage
Server-side / cross-language HTTP API callsPostgres-Native Cloud Storage HTTP API
Hit a 403 / authorization issue / DELETE errorFAQ
Content moderation, CI features, image processingShared with classic mode — see Classic cloud storage

HTTP API

Postgres-Native cloud storage exposes a complete RESTful HTTP API, callable from any backend language:

Full API list: Postgres-Native Cloud Storage HTTP API.

Next steps