# Storage

Files live in buckets; each bucket belongs to a project and is its own bucket in object storage. A file's key is its path (`avatars/42.png`); folders are just the part of the key before a slash.

Files are private. To upload or download, your server asks Harakumo for a signed URL for one key, and the client sends or fetches the bytes directly. There are no public bucket URLs and no S3-style access keys yet.

## Upload from the browser

1. Your server signs a PUT for a key, naming the Content-Type.
2. The browser PUTs the file to that URL with the same Content-Type header. Buckets accept browser uploads from any origin.

**Server**

```js
const { url, expiresAt } = await hk.storage.presign(bucketId, {
  key: 'avatars/' + userId + '.png', method: 'put', contentType: 'image/png',
});
// hand url to the browser
```

**Browser**

```js
await fetch(url, { method: 'PUT', headers: { 'content-type': 'image/png' }, body: file });
```

## Download links

Sign a GET for a key and send the person (or an img tag) to the URL. Links last 15 minutes by default; pass `expiresIn` from 60 seconds to 7 days (604800). For files shown to many people, sign a longer link once and reuse it instead of signing on every view.

**curl**

```bash
curl -X POST https://harakumo.com/api/storage/7 \
  -H "Authorization: Bearer $HARAKUMO_API_KEY" -H "content-type: application/json" \
  -d '{ "key": "reports/q3.pdf", "method": "get", "expiresIn": 86400 }'
# → { "url": "…", "expiresIn": 86400, "expiresAt": "…" }
```

## Listing and deleting

- `GET /api/storage/:id` lists objects: `prefix` narrows to a path, `delimiter=/` groups keys into `folders`, and `limit` (up to 1,000, default 100) with `cursor` pages through; `nextCursor` is set while more remain.
- `POST { key, method: "delete" }` removes one object. `DELETE /api/storage/:id` empties and deletes the bucket.
- Sizes and file counts are measured daily (or on `?measure=1`) and shown as a dash until a full count exists.

## Who can do what

- Viewers and read-only keys can list and get download links.
- Uploading and deleting need the developer role (a full key).
- A key limited to the project works for all of it.

## Limits and scaling

- One upload is at most 5 GB (one signed PUT; multipart is not offered yet).
- Keys are up to 1,024 characters.
- Buckets per plan: 2 hobby, 20 pro, 500 enterprise. There is no storage-bytes quota yet.
- The bytes scale without limit and never pass through Harakumo; each signed URL is one API call.

## Reference

**SDK**

```js
const { bucket } = await hk.storage.create(project.id, { name: 'uploads' });
const { url } = await hk.storage.presign(bucket.id, { key: 'file.png', method: 'put', contentType: 'image/png' });
const { url: download } = await hk.storage.presign(bucket.id, { key: 'file.png', method: 'get', expiresIn: 3600 });
const { objects, folders, nextCursor } = await hk.storage.objects(bucket.id);   // first page
await hk.storage.deleteObject(bucket.id, 'file.png');
```

**CLI**

```bash
harakumo storage 12              # list buckets
harakumo storage create 12 uploads
```

**REST**

```http
POST   /api/projects/:id/storage     { "name": "uploads" }
POST   /api/storage/:id              { "key": "file.png", "method": "put"|"get"|"delete", "contentType"?, "expiresIn"? }
GET    /api/storage/:id?prefix=&delimiter=/&limit=&cursor=     → { objects, folders, nextCursor, truncated }
DELETE /api/storage/:id              # empties and deletes the bucket
```
