# Your own bucket

> Keep a project's input and output folders in your own S3 or S3-compatible bucket, with keys you enter once per account.

A project can read its input folder from, and write its outputs to, a bucket of yours: AWS S3,
or any S3-compatible store — Cloudflare R2, Wasabi, MinIO. You enter the bucket and an access
key pair once, as a **storage connection** of your account, and name it when you create a
project. Your pipeline or media system then drops files into a folder of your bucket and picks
results up from another, with no SyncWords credentials on your side.

Use it when the system that produces your media can only talk to a bucket with an access key
and secret (no role assumption), or when your media already lives in your bucket and should stay
there. Otherwise the [upload role](/vod-express/uploads/aws-role) and
[API uploads](/vod-express/uploads/api-uploads) into SyncWords' storage need no bucket of yours.

> **Note:**
> The processing service copies each file through SyncWords' own storage while it works, so your
> bucket never has to be reachable from anything but SyncWords' API and processing service. The
> service's own bookkeeping — which file is done, which failed — stays in SyncWords' storage,
> outside your bucket.

## One-time setup

### 1. Create an access key for the bucket

A key pair that can list, read, write and delete objects in the bucket, and nothing else.
On AWS, an IAM user with a policy on the bucket; on Cloudflare R2, an **R2 API token** with
*Object Read & Write* on the bucket (its *Access Key ID* and *Secret Access Key*, and the
account's S3 endpoint `https://<account id>.r2.cloudflarestorage.com`).

### 2. Add the connection

Leave `endpoint` out for AWS S3; set it for an S3-compatible store. The API lists the bucket
with the keys before saving anything: a wrong key, bucket or endpoint is refused here
(`storage_connection_check_failed`, with the store's own words), not hours later.

```bash
curl -X POST "$VOD_API/account/storage-connections" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "name": "Newsroom media (R2)",
    "bucket": "newsroom-media",
    "endpoint": "https://<account id>.r2.cloudflarestorage.com",
    "accessKeyId": "<access key id>",
    "secretAccessKey": "<secret access key>"
  }'
```

The secret is stored encrypted and never answered. `GET /account/storage-connections` lists
your connections; `POST …/{id}/check` lists the bucket again with the stored keys, for after
you change a key or a policy on your side.

### 3. Make it the default (optional)

A project created without `storage` uses your account's default connection. Set it once:

```bash
curl -X PATCH "$VOD_API/account/settings" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"defaultStorageConnectionId": "<connection id>"}'
```

Adding, changing or deleting a connection, and setting the default, is for an administrator of
your organization or its API key; any user reads them.

## A project on your bucket

Name the connection and, if you like, the two folders when you create the project:

```json
{
  "name": "Evening news → Spanish",
  "config": { "...": "..." },
  "storage": {
    "connectionId": "01k81e5m3xq0h7wq2c5r8vj2s9",
    "inputPrefix": "dubbing/in/",
    "outputPrefix": "dubbing/out/"
  }
}
```

- The folders default to `vod-express/<project id>/input/` and `…/output/`. A trailing slash is
  added; no leading slash, no `..`.
- The input folder is yours to fill, at any depth: every file under it is processed once, and
  its outputs land under the output folder with the same relative path. No other project of your
  account may watch the same input folder, or one inside it, on the same connection.
- The output folder may not be the input folder or inside it: the service would process its own
  results.
- A project on your bucket reads a folder and writes folders. It cannot read a
  [feed](/vod-express/concepts/inputs#feeds) or publish one (`storage_connection_folder_only`).
- `storage.connectionId: null` puts the project in SyncWords' storage, whatever the account's
  default.

The project resource says where its files are:

```json
"storage": {
  "connection": { "id": "01k81e5m…", "name": "Newsroom media (R2)", "bucket": "newsroom-media", "endpoint": "https://…" },
  "path": "s3://newsroom-media/dubbing/",
  "inputPath": "s3://newsroom-media/dubbing/in/",
  "outputPath": "s3://newsroom-media/dubbing/out/"
}
```

Everything else is as for any project: [files](/vod-express/concepts/files) are listed from your
input folder, a download link is a presigned URL on your bucket, a delete removes the file from
your bucket, [auto-stop and starting again on a new file](/vod-express/concepts/projects#auto-stop)
watch your input folder, and [API uploads](/vod-express/uploads/api-uploads) go into it. A browser
upload into your bucket needs a CORS rule on the bucket that allows `PUT` from the page's origin
and exposes `ETag`; an upload from a script or the API does not.

## Moving a project

`PATCH /projects/{id}` with a new `storage` moves the project's folders from the next file on: a
running project is restarted with the new location, and nothing already in either place is
copied. A file with the same name as one already processed under the old location is still read
as processed.

## Changing keys

`PATCH /account/storage-connections/{id}` with a new `secretAccessKey` (and `accessKeyId`, if it
changed) checks the new keys against the bucket and restarts the running projects on the
connection: a running project holds the keys it started with. A connection cannot be deleted
while a project of your account uses it (`storage_connection_in_use`); move the project to other
storage first. Deleting a connection touches nothing in your bucket.

## What SyncWords does with the keys

The keys are stored encrypted and reach the processing service's configuration when a project
starts, where they are used to list, read and write the two folders and nowhere else. They appear
redacted in SyncWords' logs and in everything the API answers. Scope the key to the bucket the
projects use, and rotate it with `PATCH` when your policy asks.
