# Storage

Source: https://modulify.ai/docs/data/storage

Per project file storage served from your own CDN link.

Every project gets its own object storage: a private bucket fronted by a public CDN. Use it for anything created or changed at runtime, such as form submissions, user uploads, generated files and datasets. Static assets that ship with the design belong in the site's `public` folder instead.

## Open the Storage panel

Open a project and pick the **Storage** icon in the editor's toolbar, in the same pill as **Preview**, **CMS** and **Analytics**. The panel has three sub-tabs: **Storage**, **Analytics** and **Configuration**.

## Browse and upload files

The **Storage** sub-tab is a file browser. Breadcrumbs run along the top starting from **Storage**, and folders open on click.

The folder you are in is part of the page address, under `/storage/files/`. A link to a folder reopens it, a refresh keeps you where you were, and the browser's back button walks you back out.

There are three ways to get files in:

- Drag files onto the panel. A dashed overlay appears reading "Drop files to upload".
- Open the three dots menu in the toolbar and choose **Upload files**.
- Use the same menu's **New folder** first, if you want somewhere to put them.

Uploads run one file at a time and a single file is capped at 100 MB. When it finishes you get a toast: "Uploaded", with a count. If some files fail you get "Some uploads failed" with the split.

An empty folder reads **No files**, "Drag files here or upload them instead."

## Work with what is there

The search box is placeholdered "Search all files…" and searches the whole bucket, not just the folder you are in.

Each row has a three dots menu with **View file**, **Download file** and **Delete file**. A folder's menu has **Open** and **Delete folder**. Tick several rows and a bar appears above the list reading "3 selected", with its own menu holding **Download** and **Delete**.

Drag a file or a selection onto a folder row, or onto a breadcrumb, to move it. Moving into the folder it already sits in, or a folder into itself, is ignored.

The **Modified** column reads in your own timezone, down to the minute.

Deleting asks first. A folder delete warns that everything inside goes too, and none of it can be undone.

Files load fifty at a time. The footer reads "Showing 50 of 312" with a **Load more** button while there is more to fetch.

## Public and private

Everything you write is publicly readable at its CDN URL. There is no per file access control, no signed link mode for public reads, and no way to mark one file private.

Treat that as the rule when deciding what goes in: do not store secrets or personal data you would not want readable at a guessable URL. For private data, keep it under its own prefix and read it back from your server code with the storage API rather than linking to it.

Downloads you start from the panel use a temporary signed link, but that is a convenience for the download, not a protection on the file.

## The CDN link and the storage key

The **Configuration** sub-tab holds both, under **Connection**.

**CDN link** is the public base URL for this project's storage. Append a file path to it to link to a file, for example `https://your-cdn-link/logo.png`. Copy and open buttons sit beside it. If storage has not been provisioned yet, the card reads "A CDN link has not been provisioned for this site yet."

**Private Storage Key** is the server only key your site uses to read and write at runtime. It is masked as `msk_••••` and never shown in full in the panel. The copy button puts the real key on your clipboard and clears it again after a minute, telling you so in the toast.

Your site reads the key as `CDN_PRIVATE_KEY` and the base URL as `CDN_URL`, both injected into the environment. Both are server only. Never reference either from a client component.

### Rotating the key

The rotate button next to copy asks "Rotate storage key?" and warns that the current key stops working immediately and anything using the old key fails until you republish. On success: "Key rotated", with the reminder to republish the site to apply it.

## Reading and writing from your site

Sites created from the Modulify starter ship with a server only helper at `src/lib/modulify/storage`:

```typescript
import { putObject, listObjects, getObjectText } from '@/lib/modulify/storage'

await putObject('submissions/123.json', JSON.stringify(data))
const { files } = await listObjects('submissions/')
const raw = await getObjectText('submissions/123.json')
```

Import it only from API routes, `getServerSideProps` or other server code. It throws in the browser by design.

Caching is handled for you. Objects are cached hard at the edge, but every write, move and delete purges that object, so the next fetch returns the latest bytes. Reuse the same key to update a file in place rather than adding a cache busting query string.

You rarely need to write this yourself. The Configuration sub-tab has a collapsible **Using your storage** section with two tabs. **Modulify** explains that describing what you want in chat is enough, and its **Add it with AI** button drops a ready made prompt into the composer for you to review and send. **External** holds the raw endpoint reference with your own host filled in.

For calling the endpoints directly, from outside the site or from another language, see [Storage HTTP API](/docs/data/storage-http-api).

## Usage and limits

Each site can store up to 500 GB, and that ceiling can be raised on request.

The **Analytics** sub-tab shows **Total data**, **Files** and **Folders** for the bucket, then CDN delivery over a period you pick from Last day, Last week, Last month, Last 3 months or Last year: **Bandwidth**, **Requests** and **Cache hit rate**, with a requests over time chart. Before the CDN has served anything the chart reads **No traffic**, "Requests appear once the CDN serves files."

## CMS uploads land here too

Image and file fields in the CMS upload into this same storage, under a `CMS/` prefix organised by collection and row. They appear in the file browser like anything else, and they are public at their CDN URL like anything else.

## From an AI client over MCP

Everything on this page is also reachable from a connected AI client. `list_storage_files`, `read_storage_file`, `get_storage_stats`, `get_storage_info` and `get_storage_file_url` cover browsing and reading; `upload_storage_file`, `create_storage_folder`, `move_storage_files`, `delete_storage_files`, `delete_storage_folder` and `clear_storage` cover the writes.

The private storage key sits behind its own `credentials:reveal` scope, unticked by default, because `get_storage_key` and `rotate_storage_key` return it in plain text into the client's transcript. See [MCP tools](/docs/mcp/tools) and [Tokens and scopes](/docs/mcp/tokens-and-scopes).

## Next

- [Storage HTTP API](/docs/data/storage-http-api) documents the endpoints and their bodies.
- [Clearing and exporting](/docs/data/clearing-and-exporting) covers the storage export and the clear action.