# Storage

URL: /code/platform/storage · Id: `page:code/platform/storage`

> Storage keeps the bytes of every File, addressed by content hash, while the File record lives in the database with its access rules.

Storage keeps the bytes of every [File](/business/nouns/file), addressed by content hash. The File's record lives in [the database](/code/platform/database) like any other [Thing](/business/nouns/thing).

## Upload
`File.create` returns the record and a signed URL; large uploads go straight to storage through it.

```ts
import { readFile } from 'node:fs/promises'
import { $ } from 'startup-builder'

const pdf = await readFile('lease-4B-signed.pdf')
const file = await $.File.create({ name: 'lease-4B-signed.pdf', contentType: 'application/pdf', size: pdf.byteLength, visibility: 'Customer' })
await fetch(file.uploadUrl!, { method: 'PUT', body: new Uint8Array(pdf) })
```

## Read
```ts
const signed = await $.File.get({ id: 'file_1' })
```

Every read checks visibility against the caller's [Permissions](/business/nouns/permission).

## Rules
| Rule | Detail |
| --- | --- |
| Immutable | Bytes never change; a new version is a new File linked to the one it replaces |
| Visibility | `Owner` by default, or `Customer`, or `Public` |
| [Share](/business/nouns/share) links | Signed, and expire after 7 days by default |
| Deletes | A delete marks the File; masking and [Suppression](/business/nouns/suppression) keep history and legal holds intact |
| Metering | Stored and served bytes count into a [Meter](/business/nouns/meter) |
| [Agents](/business/nouns/agent) | Pass Files by reference, never as raw bytes in a model's context |