# File storage

Blossom upload, download, list, and delete.

## Store and retrieve files


Blossom is an HTTP file server protocol. It is a separate service alongside the Nostr relays. Select a server origin explicitly:

```ts
const files = db.storage.from("https://files.example.com");

const uploaded = await files.upload("images/avatar.png", imageBlob);
if (uploaded.error) throw uploaded.error;
const { sha256, url } = uploaded.data!;

const downloaded = await files.download(sha256);
const page = await files.list(undefined, { limit: 20 });
const removed = await files.remove([sha256]);
```

The name is display metadata. File objects use their SHA-256 hash as the key. File names do not create folders or affect addressing. Publish the returned descriptor or reference in a Nostr record to associate it with your app.

The implementation follows BUD-01 retrieval, BUD-02 upload, BUD-11 authorization, and BUD-12 management:

- Upload: `PUT /upload` with raw bytes, MIME type, and `X-SHA-256`.
- Download: `GET /<sha256>`, followed by a local hash check.
- Remove: separate `DELETE /<sha256>` requests with per-object success or error results.
- List: `GET /list/<pubkey>` with optional `cursor` and `limit`. Some servers do not implement list.
- Authorization: signed kind `24242` events, short expiry, server hostname scope, and hash scope for upload, download authorization, and deletion.

Uploads, deletion, and listing request the active signer's approval. Public downloads work without a signer. Use `download(hash, { authenticated: true })` for a server that requires a scoped `get` token.

Upload descriptors must match the uploaded hash and byte count. Downloads must match the requested hash. Requests always use the chosen server; the SDK does not send uploads or authorization tokens to descriptor URLs. HTTP redirects are rejected. Use the final file server origin when a provider uses redirects. Servers can return public CDN URLs in validated descriptors, which your app may use separately.

Cancellation and request timeout:

```ts
const controller = new AbortController();
await files.upload("notes.txt", blob, { signal: controller.signal, timeout: 15_000 });
```

Configure a custom fetch implementation and the default storage request timeout on the client:

```ts
const db = createClient({
  namespace: "my-app",
  relays: ["wss://relay.example.com"],
  signer,
  storage: { fetch: customFetch, timeout: 15_000 },
});
```

File bytes are public unless your app encrypts them before upload. Server storage limits, retention, payments, and upload permissions follow the selected server's policy. Private table encryption does not encrypt file bytes automatically.
