# 6. Your own storage

By default, LeavenIT keeps a folder's tables. A **folder owner** can instead keep them in an **S3-compatible bucket they own**.
Tools, blocks and flows keep working the same way: only the place where table files live changes.

## 6.1 How it works

- The bucket belongs to the folder owner, with a provider they choose and pay.
- LeavenIT keeps a **restricted access key**, encrypted in its vault. It is never shown again, never sent to browsers, never given to a Runner.
- For every read or write, LeavenIT checks the person's rights, then **signs a short-lived address** (AWS Signature V4). The browser (or the Runner) transfers the file directly between the device and the bucket.
- What you gain: **no LeavenIT size limit** per table (up to 5 GB per table file), tables that are yours, and the possibility of a **Runner** (chapter 7).
- What doesn't change: calculations still run in the browser (or on a Runner). Your own storage doesn't make queries on big tables faster.
- **Only table files move.** Small data saved by tools with `window.leavenit.get/set` stays in LeavenIT.

Who can do what:

| Person | Can |
|---|---|
| Owner of the shared folder (or the person, for My Workshop) | connect, test, move, disconnect |
| Other members | see where the data is stored ("Data stored in Scaleway (France)…"), nothing else |

## 6.2 Supported providers

LeavenIT only accepts these providers (the site's security rules only allow their addresses):

| Provider | Field "Region" | Address used |
|---|---|---|
| **Scaleway** (France) | `fr-par`, `nl-ams`, `pl-waw` | `s3.<region>.scw.cloud` |
| **OVHcloud** (France) | `gra`, `sbg`, `rbx`, `de`, `uk`, `waw`, `bhs` | `s3.<region>.io.cloud.ovh.net` |
| **Hetzner** (Germany) | `fsn1`, `nbg1`, `hel1` | `<region>.your-objectstorage.com` |
| **Cloudflare R2** | `auto`, plus your **Account ID** (32 characters) | `<account id>.r2.cloudflarestorage.com` |
| **Backblaze B2** | `eu-central-003`, `us-west-004`, `us-east-005` | `s3.<region>.backblazeb2.com` |

Bucket names: lowercase letters, digits, `.` and `-`, 3 to 63 characters.

## 6.3 What every provider needs (the checklist)

1. **An empty bucket**, private (no public access), dedicated to LeavenIT.
2. **An access key restricted to this bucket**, allowed to read, write and delete objects, nothing more. You get an **Access key** (public identifier) and a **Secret key** (shown once by the provider: store it in a password manager).
3. **A CORS rule** on the bucket, so the browser may use it from leavenit.com. LeavenIT shows it, ready to paste:

```json
[
  {
    "AllowedOrigins": ["https://leavenit.com"],
    "AllowedMethods": ["GET", "PUT", "HEAD", "DELETE"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]
```

If the provider's console has no CORS screen, use the AWS command line tool, which works with every provider (LeavenIT shows this command with your bucket and address filled in):

```
aws s3api put-bucket-cors --bucket <bucket> --cors-configuration file://cors.json --endpoint-url https://<address>
```

4. **No click tracking, no public website, no custom domain** on the bucket: LeavenIT uses the provider's own address.

## 6.4 Connecting a folder, step by step

1. **Workshop** → open the shared folder (or My Workshop) → the **⋯** next to its name → **Storage**.
   For a folder with separate data (lock icon): its **⋯** → **Storage**.
2. **Use my own storage**: choose the **Provider**, the **Region** (and the **Account ID** for Cloudflare R2), then fill **Bucket**, **Access key**, **Secret key**.
3. **Save and test**. LeavenIT writes, reads back and deletes a small test file through signed addresses. The result shows **✓ Write ✓ Read ✓ Delete**.
4. If a step fails, the panel explains why and shows the CORS text (see 6.6). Fix, then **Test again**.
5. **Move my N tables**. A progress bar shows each table (reading, checking, writing, switching). Keep the page open. Tools keep working during the move. If the page closes, the tables already moved stay moved: **Move the remaining N** finishes the rest.
6. Done: the panel shows **in use**. New tables go to the bucket. Copies left in LeavenIT's storage are deleted after 30 days.

**Going back**: **Move back to LeavenIT** copies the current version of every table back. Tables larger than LeavenIT's limit are refused, with their names. **LeavenIT never empties your bucket**: your copies stay there.
**Replacing the key** (rotation, lost or revoked key): **Storage** → **Replace the key**. Provider, region and bucket stay locked; enter the new Access key and Secret key, then **Save and test**. The storage stays in use.
**Disconnect** is possible once no table is left in the bucket (and old files have been cleaned, usually within an hour).

## 6.5 Provider guides

Consoles change often. If a screen differs, look for the same idea under another name, or ask your AI with a screenshot.

### Cloudflare R2 (tested end to end)

1. Cloudflare dashboard → **R2 Object Storage** → **Create bucket**.
   - Name: e.g. `leavenit-data`.
   - Location: **Automatic**, or a **location hint** in Europe. **Do not choose the "EU" jurisdiction**: it uses another address (`<account>.eu.r2.cloudflarestorage.com`) that LeavenIT doesn't accept yet.
   - Keep the bucket **private** (no r2.dev address, no custom domain).
2. Your **Account ID**: on the R2 overview page (32 characters).
3. **Manage R2 API Tokens** (or **API Tokens**) → **Create API token**:
   - Permission: **Object Read & Write**.
   - Specify bucket: **only** `leavenit-data`.
   - Copy the **Access Key ID** and the **Secret Access Key** (shown once).
4. The bucket → **Settings** → **CORS Policy** → **Add CORS policy** → paste the JSON of 6.3.
5. In LeavenIT: Provider **Cloudflare R2**, Region **auto**, your Account ID, bucket, keys.

### Backblaze B2

1. **Buckets** → **Create a Bucket**: private, in the region you'll pick in LeavenIT (e.g. `eu-central-003`).
2. **Application Keys** → **Add a New Application Key**: allow access to **this bucket only**, type **Read and Write**. Copy the **keyID** (Access key) and **applicationKey** (Secret key).
3. CORS: Backblaze's S3 interface accepts the AWS command of 6.3, with `--endpoint-url https://s3.<region>.backblazeb2.com`. (B2's own "CORS rules" screen applies to its native interface; use the command to be sure.)

### Scaleway

1. **Object Storage** → **Create bucket**: region `fr-par`, `nl-ams` or `pl-waw`, private.
2. **IAM** → create an **application** with a **policy** limited to Object Storage in this project (read, write, delete objects), then generate an **API key** for it: **Access key** + **Secret key**.
3. CORS: with the AWS command of 6.3 and `--endpoint-url https://s3.<region>.scw.cloud`.

### OVHcloud

1. **Public Cloud** → **Object Storage** → create an **S3** container in a region (e.g. `gra`), private.
2. Create an **S3 user** with read/write rights on this container, then its **S3 credentials**: Access key + Secret key.
3. CORS: with the AWS command of 6.3 and `--endpoint-url https://s3.<region>.io.cloud.ovh.net`.

### Hetzner Object Storage

1. **Object Storage** → **Create Bucket**: location `fsn1`, `nbg1` or `hel1`, private.
2. **Security** → **S3 credentials** → generate: Access key + Secret key. Hetzner's credentials apply to the project: keep a project dedicated to LeavenIT if you want them limited.
3. CORS: with the AWS command of 6.3 and `--endpoint-url https://<region>.your-objectstorage.com`.

## 6.6 Troubleshooting

| What you see | Cause | Fix |
|---|---|---|
| ✕ Write, "The browser couldn't reach the bucket. Paste the CORS setting below…" | no CORS rule (or a wrong origin) | paste the CORS JSON of 6.3 on the bucket, then **Test again** |
| "The storage refused the key. Check the access key, the secret key and its permissions on this bucket." | wrong key, or key not allowed on this bucket | create a new key restricted to this bucket with read + write |
| "The bucket was not found. Check its name and region." | bucket name or region differs | check spelling and the region chosen in LeavenIT |
| "Test the connection first." | moving before a successful test | **Test again** until the three ticks are green |
| "Some tables are still in this storage. Move them back first." | disconnecting while tables are in the bucket | **Move back to LeavenIT**, then **Disconnect** |
| "Your tables are in this bucket: you can replace its key, but not change the provider, region or bucket…" | changing the bucket while it is in use | **Move back to LeavenIT** first, then connect the new bucket |
| "These tables are too large for LeavenIT's storage: …" | moving back tables above LeavenIT's limit | keep them in your bucket, or make them smaller |
| A tool says "The storage refused the download (403)" | the key was deleted, expired or lost its rights | create a new key with the same rights at the provider, then **Storage** → **Replace the key** |
| Cloudflare R2 bucket in the "EU" jurisdiction | address `…eu.r2.cloudflarestorage.com` not accepted | create a new bucket without the EU jurisdiction (a European location hint is fine) |
