Managed files
OpenCloud Files stores browser uploads behind opaque IDs. Applications do not receive buckets, object paths, S3 credentials, or Storage request headers.
Declare access
files:
access: user
maxUploadBytes: 52428800userisolates files by the current user and is the safe default.appshares files between authenticated members of the app.maxUploadBytesaccepts 1 byte through 100 MiB and defaults to 50 MiB.
Omit files when the app has no file workflow; app.info() then reports the capability as unavailable.
Upload directly
const file = await opencloud.files.upload({
data: fileInput.files[0],
name: fileInput.files[0].name,
onProgress: ({ percent }) => renderUploadProgress(percent),
});
await opencloud.files.save(file);The SDK checks the configured size limit before sending bytes, normalizes the name and content type, creates an idempotency key, retries one transient failure with that same private key, and returns { id, name, contentType, size, createdAt, updatedAt }. Application code does not create retry keys or replay an ambiguous upload.
Upload and create metadata together
Create an RLS-protected attachment table with conventional columns:
create table item_attachments (
id uuid primary key default gen_random_uuid(),
owner_id uuid not null default auth.uid(),
item_id uuid not null references items(id) on delete cascade,
file_id uuid not null unique,
file_name text not null,
file_type text not null,
file_size bigint not null check (file_size >= 0),
created_at timestamptz not null default now()
);
create policy item_attachments_owner_access
on item_attachments for all
using (owner_id = auth.uid())
with check (owner_id = auth.uid());Then use the compound helper:
await opencloud.auth.requireUser();
const { file, record } = await opencloud.files.attach({
data: selectedFile,
name: selectedFile.name,
table: "item_attachments",
values: { item_id: itemId },
});attach writes file_id, file_name, file_type, and file_size. It reconciles an ambiguous metadata write by opaque file ID and removes the upload after a definite row failure. A FILE_ATTACHMENT_INCOMPLETE error includes the file ID when cleanup must be resolved explicitly.
Lifecycle
const download = await opencloud.files.download(file);
useBlob(download.data);
const replacement = await opencloud.files.replace(file, {
data: newBlob,
name: "replacement.pdf",
});
await opencloud.files.remove(replacement.id);Use the returned object when a filename matters. Treat the ID as opaque and store it in RLS data. Never derive ownership from a filename, construct a bucket/object path, call Storage endpoints, or put secrets in metadata.
