Authentication
Sign-up, sessions, eight presets
Storage
One bucket per project, links signed by us, and the same access matrix deciding who may read what — no second permission model to keep in step.
A private file behaves like a row on the private preset; a public one like a row on shared. There is no separate bucket policy to write, and no way for the two models to drift apart, because there is only one.
The record a file returns is its id, name, size, type, visibility, link and date — not the storage key, not the bucket, not the owner.
A URL is signed by Manybases and served by Manybases, never by the object store, and the API re-reads the file's record before the first byte. A private link expires — one hour by default. A public link carries a signature and no expiry: it does not stop on its own.
Deleting the file revokes every link at once. And downloads never cost anything: there are no egress fees, on any plan, at any volume.
private
/v1/files/:id/content?exp=1757068800&sig=…
public
/v1/files/:id/content?sig=…
const { data, error } = await mb.storage.upload(file, {
name: file.name,
contentType: file.type,
});
// data.url → the link to serveA declared length is checked before the first byte, so a client that lies about its size fails its own transfer instead of moving the cap.
25 MB a file · private links 1 hour · egress $0 · 500 MB on Free, 10 GB on Builder, 100 GB on Studio
No image resizing and no transformations. A file comes back exactly as it went in.
25 MB per file, and a declared length is required. The cap is a property of the store, not a check we could raise for one route.
A public link has no expiry, so deleting the file is the only way to take it back.
Sign-up, sessions, eight presets
One SQLite base per project
Push to GitHub, and it is live
Give it a backend.