# Send your library to your own storage

> Page: https://docs.plotwork.dev/connectors/storage. Index of every page: https://docs.plotwork.dev/llms.txt

On Pro and Enterprise, an organisation can keep a copy of its library in storage it already runs: a folder in Google Drive, an Amazon S3 bucket, an Azure Blob Storage container, or a Google Cloud Storage bucket. Plotwork sends each diagram published to the library as it is published, changed, moved or deleted, and tries again for a day when the storage is down.

## TL;DR

| Question | Answer |
|---|---|
| Which plan? | Pro and Enterprise. On Free the Storage page says so, nothing is sent, and a destination saved earlier can still be disconnected. |
| Who sets it up? | The owner and the admins, signed in, in the organisation's Connectors, under Storage. |
| Where can it go? | Google Drive, Amazon S3, Azure Blob Storage and Google Cloud Storage, one of each. |
| What is sent? | Only diagrams already in the organisation's private library: the picture (SVG), the `.diagram` source it carries, or both. A diagram someone publishes to their own account is never sent. |
| Does Plotwork hold a key of mine? | No. Google Drive is connected by signing in with Google, for the files Plotwork makes or you choose only. A bucket trusts Plotwork's issuer for your organisation, with no access key, secret or key file. |
| How fast? | About five seconds after a change. Turning a destination on sends the whole library first. |
| What if my storage is down? | Plotwork tries again after 30 seconds, then waits twice as long each time, up to an hour, for a day. The Storage page says why it failed, in one sentence, with Retry now. |
| Are deletions sent? | Only when you tick "Remove files when diagrams are deleted or moved out". Off, files stay in your storage after they leave the library. |

## 1. Set it up

1. Open the organisation in the portal, choose **Connectors**, and open **Storage**.
2. Choose **Set up** on Google Drive, Amazon S3, Azure Blob Storage or Google Cloud Storage. A dialog walks through four steps.
3. **Connect**: sign in with Google, or tell your cloud to trust Plotwork. The sections below list what to paste where.
4. **Folder**, **Bucket** or **Container**: where the files go.
5. **What to send**: the folders, the files, the color, and two choices about deletions and Confidential diagrams.
6. **Test**: Plotwork writes a small file, `plotwork-test.svg`, where the library will go, and gives you a link to open it. Delete it afterwards if you like.
7. **Turn on**. The whole library is sent at once, then each change as it happens.

Nothing is sent while a destination is not finished or is paused.

## 2. Google Drive

| Step | What to do |
|---|---|
| Connect | Choose **Sign in with Google** and sign in with the account that owns the folder. Plotwork asks for one permission, `drive.file`: to see and change only the files it makes or that you choose. It cannot see the rest of your Drive. |
| Folder | **Make a new folder** at the top of My Drive (you can move it anywhere later and Plotwork keeps writing to it), or **Choose a folder in Drive**, which opens Google Drive in a small window. Plotwork gets access to the folder you pick and nothing else. |

Library folders become subfolders inside the chosen folder. A diagram's file keeps its id in Drive: a change updates the same file and a move moves it, so a link to it in Drive keeps working. When deletions are mirrored, a deleted diagram's files go to Drive's bin, where Drive keeps them for thirty days.

Disconnecting Google Drive revokes Plotwork's access at Google as well as in Plotwork.

## 3. Amazon S3

Plotwork signs in to AWS as your organisation with a five-minute identity token, which AWS exchanges for credentials that last fifteen minutes. The Storage dialog writes every value below for your organisation; `<ORG_ID>` is your organisation's id and `<ACCOUNT_ID>` your twelve-digit AWS account.

1. In IAM, open **Identity providers** and add an **OpenID Connect** provider:

| Field | Value |
|---|---|
| Provider URL | `https://api.plotwork.dev/storage/oidc/<ORG_ID>` |
| Audience | `sts.amazonaws.com` |

2. Create a role with a custom trust policy. Only Plotwork's token for this organisation's S3 destination can take it:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/api.plotwork.dev/storage/oidc/<ORG_ID>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "api.plotwork.dev/storage/oidc/<ORG_ID>:aud": "sts.amazonaws.com",
          "api.plotwork.dev/storage/oidc/<ORG_ID>:sub": "org:<ORG_ID>:storage:s3"
        }
      }
    }
  ]
}
```

3. Give the role this permissions policy, so it can write under the prefix of one bucket and nowhere else:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::<BUCKET>/plotwork/*"
    }
  ]
}
```

4. Paste the role's ARN, then the bucket, its region and the prefix.

```film
slug: add-storage-connection
```

## 4. Azure Blob Storage

1. In Microsoft Entra ID, open **App registrations** and make a new registration, or pick one. Paste its **Directory (tenant) ID** and **Application (client) ID** from the Overview page.
2. Under **Certificates & secrets**, then **Federated credentials**, add a credential, choose **Other issuer**, and enter:

| Field | Value |
|---|---|
| Issuer | `https://api.plotwork.dev/storage/oidc/<ORG_ID>` |
| Subject identifier | `org:<ORG_ID>:storage:azure_blob` |
| Audience | `api://AzureADTokenExchange` |

3. On the storage account, give the app the **Storage Blob Data Contributor** role on the container.
4. Paste the storage account, the container and the prefix.

The same credential with the Azure CLI:

```sh
az ad app federated-credential create --id <CLIENT_ID> --parameters '{"name":"plotwork-library","issuer":"https://api.plotwork.dev/storage/oidc/<ORG_ID>","subject":"org:<ORG_ID>:storage:azure_blob","audiences":["api://AzureADTokenExchange"]}'
```

## 5. Google Cloud Storage

1. In IAM & Admin, open **Workload Identity Federation** and create a pool.
2. Add an **OpenID Connect** provider to it with the issuer URL `https://api.plotwork.dev/storage/oidc/<ORG_ID>`, the default audience, and the attribute mapping `google.subject=assertion.sub`.
3. On the bucket, grant **Storage Object Admin** to `principal://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/<POOL_ID>/subject/org:<ORG_ID>:storage:gcs`.
4. Paste the project number, the pool ID and the provider ID, then the bucket and the prefix. Name a service account only when the bucket grants a service account instead of the pool; then give the principal the Workload Identity User role on that account.

The same with gcloud:

```sh
gcloud iam workload-identity-pools create <POOL_ID> --location=global --display-name="Plotwork"
gcloud iam workload-identity-pools providers create-oidc <PROVIDER_ID> --location=global --workload-identity-pool=<POOL_ID> --issuer-uri="https://api.plotwork.dev/storage/oidc/<ORG_ID>" --attribute-mapping="google.subject=assertion.sub"
gcloud storage buckets add-iam-policy-binding gs://<BUCKET> --role=roles/storage.objectAdmin --member="principal://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/<POOL_ID>/subject/org:<ORG_ID>:storage:gcs"
```

## 6. What is sent

| Choice | Options |
|---|---|
| Folders | **All folders**, which leaves out folders with access rules, or **Chosen folders**, each with the folders inside it |
| Files | The picture (SVG), the source (`.diagram`), or both. The source is sent only for diagrams published with their source kept. |
| Color | **As published**, **Light** or **Dark**. A picture that follows the reader's color scheme is pinned to light or dark; a light or dark picture is sent as it is. |
| Remove files when diagrams are deleted or moved out | Off by default |
| Leave out diagrams labelled Confidential | On by default |

A diagram waiting for approval is neither sent nor removed until it is approved.

| Where | File name |
|---|---|
| Google Drive | `<name>.svg` and `<name>.diagram`, in subfolders like the library's |
| A bucket | `<prefix><folder>/<name>.<first 8 characters of the diagram's id>.svg`, and `.diagram` |

A diagram with no name is `Untitled` and the start of its id. The id in a bucket key keeps two diagrams of one name in one folder apart. When a diagram is renamed or moved in a bucket, the new key is written and the old one is deleted only when deletions are mirrored.

## 7. Day to day

Each destination has a card on the Storage page: where it writes, what it sends, the last delivery, how many diagrams it holds, how many wait, and the last failure in one sentence.

| Task | How |
|---|---|
| Try a failing destination again now | **Retry now** on its card |
| Stop sending for a while | **Pause**. Nothing is sent while it is paused; **Resume** sends the whole library again, skipping what is current. |
| Send everything again | **Send the library again**. Files that are already current are not uploaded again. |
| Check the connection | **Send a test file** |
| Change folders, files or color | **Edit settings** |
| Stop for good | **Disconnect**. What waits is dropped and the files already sent stay in your storage. For a bucket, remove the role or the credential in your cloud to end the trust. |

Connecting, changing, sending again and disconnecting are recorded in the organisation's audit log as `orgs.storage.connect`, `orgs.storage.update`, `orgs.storage.sync` and `orgs.storage.disconnect`, never with a token. When sending starts failing, and when diagrams are given up on after a day, Plotwork records `orgs.storage.failing` and `orgs.storage.gave_up`.

## 8. Privacy

Your storage is a destination your organisation chooses and controls, like a log streaming tool. Plotwork sends only what is already in your organisation's library, at your request. While a change waits, Plotwork's queue holds which diagram changed, never the picture or the source: the picture is read from the library when it is sent. To update the same file later, Plotwork keeps the ids of the files and folders it wrote in Drive, or the object keys in a bucket. What your storage keeps is under your agreement with its provider. The [privacy policy](https://docs.plotwork.dev/privacy-policy.md) lists every field.

## See also

| Page | Why |
|---|---|
| [Connectors](https://docs.plotwork.dev/connectors/overview.md) | Every service an organisation can connect |
| [Send audit events and drift to your tools](https://docs.plotwork.dev/siem-delivery.md) | The other place an organisation's data is sent at its request |
| [Organisations](https://docs.plotwork.dev/organisations.md) | The library, its folders and access rules |
| [Classification and approval](https://docs.plotwork.dev/classification-and-approval.md) | Confidential labels and diagrams waiting for approval |
| [Audit log](https://docs.plotwork.dev/audit-log.md) | Where every change to a destination is recorded |
| [Plans and billing](https://docs.plotwork.dev/plans-and-billing.md) | What Pro and Enterprise include |
| [Privacy policy](https://docs.plotwork.dev/privacy-policy.md) | What is stored to send, and for how long |
