# PutBucketVersioning

PutBucketVersioning sets a bucket's versioning state - whether overwriting or deleting an object
keeps the earlier versions. Every STRG bucket is versioned from the moment it is created and
stays that way, so there is nothing to switch on: a request asking for versioning, with `Status`
`Enabled` or no `Status` at all, answers `200 OK` and leaves the bucket as it is. Tools that
enable versioning as part of their setup, such as Terraform and backup software, therefore work
unchanged. [GetBucketVersioning](https://docs.strg.com/s3-api/GetBucketVersioning) reads the state back.

**Info: Good to know on STRG**

- `Status` `Enabled` succeeds and changes nothing: the bucket is already versioned.
- Versioning stays on for good, so `Status` `Suspended` is answered `501 NotImplemented` and a
  client knows straight away.
- STRG has no MFA delete: `MfaDelete` `Enabled` and the `x-amz-mfa` header are answered
  `501 NotImplemented`.
- Setting the versioning state is the work of an access key for all buckets holding
  **Manage buckets**.
- `x-amz-expected-bucket-owner` is welcome and has nothing to check.

Because every bucket keeps every version, overwriting or deleting an object leaves its earlier
versions in place: a delete writes a delete marker, and every version stays billed as stored data
until it is permanently deleted or a [lifecycle policy](https://docs.strg.com/lifecycle-policies) removes it.

## When you'd use it

- An infrastructure-as-code tool such as Terraform describes a bucket with versioning enabled and
  sends PutBucketVersioning when it applies the configuration. STRG answers `200 OK`, and the
  bucket matches the configuration from the start.
- Backup software turns versioning on for its target bucket during setup, since versioning is
  what lets a backup be rolled back. The request succeeds and the backup carries on as usual.
- A setup script written for Amazon S3 enables versioning right after creating a bucket. It runs
  against STRG as it is, with one step that is already done.

## Request

```http
PUT /?versioning HTTP/1.1
Host: my-bucket.s3.eu-central-1.strg.com
Authorization: AWS4-HMAC-SHA256 Credential=...
x-amz-date: 20260926T120000Z
x-amz-content-sha256: <SHA-256 of the body, hex>
Content-MD5: <MD5 of the body, base64>
Content-Type: application/xml

<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <Status>Enabled</Status>
</VersioningConfiguration>
```

Path-style, the same request is `PUT /my-bucket?versioning` on `s3.eu-central-1.strg.com`.
[Addressing](https://docs.strg.com/s3-api/overview#addressing) describes both styles.

### Permissions

The access key needs **Manage buckets** (`buckets:write`), which a key for all buckets can hold.
A key without it gets `403 AccessDenied`. [Access keys](https://docs.strg.com/access-keys#buckets) has the
details.

### URI parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Bucket` | String | Yes | The bucket whose versioning state is set. |
| `versioning` | Flag | Yes | Selects the versioning subresource. Sent without a value. |

Any other query parameter is answered `501 NotImplemented`, so a client knows straight away that
it has no effect here.

### Request headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Content-MD5` | String | No | The base64 MD5 digest of the body. When it is sent, STRG checks it and answers `400 BadDigest` if it does not match. The AWS SDKs and CLI compute it, or a checksum, on their own. |
| `x-amz-sdk-checksum-algorithm` | String | No | Accepted, along with the checksum header the SDK sends beside it. |
| `x-amz-mfa` | String | No | Answered `501 NotImplemented`: STRG has no MFA delete. |
| `x-amz-expected-bucket-owner` | String | No | Welcome, and has nothing to check: an access key only ever reaches its own account's buckets. |

### Request body

A `VersioningConfiguration` document of at most 2 MiB.

| Element | Type | Required | Description |
| --- | --- | --- | --- |
| `VersioningConfiguration` | Container | Yes | The root element. It may carry the `xmlns` attribute, and contains only `Status` and `MfaDelete`, each at most once. |
| `Status` | String | No | `Enabled`, or omitted: succeeds, with nothing to change. `Suspended`: answered `501 NotImplemented`, since versioning stays on. Any other value: `400 MalformedXML`. |
| `MfaDelete` | String | No | `Disabled`, or omitted: succeeds, with nothing to change. `Enabled`: answered `501 NotImplemented`. Any other value: `400 MalformedXML`. |

The document is plain XML. A comment, `CDATA` section, `DOCTYPE`, processing instruction other
than the XML declaration, namespace prefix, attribute other than `xmlns` or unknown element is
answered `400 MalformedXML`.

## Response

A successful request answers `200 OK` with an empty body.

### Response headers

| Name | Description |
| --- | --- |
| `x-amz-request-id` | The request ID, for support requests. |

### Response body

None.

## Errors

These are the errors this operation can answer, and the situation behind each.

| Code | HTTP status | When |
| --- | --- | --- |
| `NotImplemented` | 501 | `Status` is `Suspended`, `MfaDelete` is `Enabled`, the request carries `x-amz-mfa`, it carries a query parameter other than `versioning`, or it sends the body as a streaming `aws-chunked` upload. |
| `MalformedXML` | 400 | The body is missing, is not a `VersioningConfiguration` document, carries an unknown or repeated element, uses one of the constructs listed under [Request body](https://docs.strg.com/s3-api/PutBucketVersioning#request-body), or has a `Status` or `MfaDelete` value S3 does not define. |
| `MaxMessageLengthExceeded` | 400 | The body is larger than 2 MiB. |
| `BadDigest` | 400 | `Content-MD5` does not match the body. |
| `XAmzContentSHA256Mismatch` | 400 | `x-amz-content-sha256` does not match the body. |
| `NoSuchBucket` | 404 | The bucket does not exist, it is in a region other than the endpoint's, or your account has no storage in this endpoint's region yet. |
| `AccessDenied` | 403 | The access key lacks **Manage buckets**, the request is unsigned or carries an `x-amz-*` header that is not signed, or your account is not active. |
| `InvalidAccessKeyId` | 403 | The access key does not exist, has expired or been revoked, or was created so recently that it is not yet usable in this endpoint's region - a moment later it is. |
| `SignatureDoesNotMatch` | 403 | The request signature is wrong. |
| `RequestTimeTooSkewed` | 403 | The request time is more than 15 minutes from the server's. |
| `AuthorizationHeaderMalformed` | 400 | The signature's credential scope names a region other than the endpoint's. |
| `SlowDown` | 503 | Too many requests at once; a retry after the `Retry-After` interval goes through. |
| `ServiceUnavailable` | 503 | A temporary failure; a retry usually succeeds. |
| `InternalError` | 500 | An unexpected failure; a retry usually succeeds. |

## Differences from Amazon S3

A few things work a little differently on STRG, each listed here with what it means for your
requests.

| Aspect | Amazon S3 | STRG |
| --- | --- | --- |
| New bucket | Unversioned until versioning is first enabled | Versioned from creation, with nothing to turn on |
| `Status` `Enabled` | Turns versioning on, which can take up to 15 minutes to take full effect | Succeeds straight away with nothing to change: versioning is already on |
| `Status` `Suspended` | Suspends versioning: new objects get the version ID `null` | `501 NotImplemented`, and every version is kept. A [lifecycle policy](https://docs.strg.com/lifecycle-policies#keeping-30-days-of-old-versions) takes care of removing noncurrent versions |
| MFA delete | `MfaDelete` with the `x-amz-mfa` header | STRG has no MFA delete: `501 NotImplemented` |
| Permission | Bucket owner | **Manage buckets**, on an access key for all buckets |
| Bucket in another region | `301` redirect with `x-amz-bucket-region` | `404 NoSuchBucket`, also from a region where your account stores nothing yet. Each bucket answers on its own regional endpoint |
| `x-amz-expected-bucket-owner` | Checked against the bucket owner | Welcome, with nothing to check: a key only ever reaches buckets of its own account |

## Examples

Each example enables versioning on `my-bucket` - a request that succeeds and changes nothing, just
as tools that enable versioning expect.

**AWS CLI**

```bash
aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled \
  --endpoint-url https://s3.eu-central-1.strg.com \
  --region eu-central-1
```

**Python (boto3)**

```python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.eu-central-1.strg.com",
    region_name="eu-central-1",
)

s3.put_bucket_versioning(
    Bucket="my-bucket",
    VersioningConfiguration={"Status": "Enabled"},
)
```

**JavaScript (AWS SDK v3)**

```javascript
import { PutBucketVersioningCommand, S3Client } from "@aws-sdk/client-s3";

const client = new S3Client({
  endpoint: "https://s3.eu-central-1.strg.com",
  region: "eu-central-1",
});

await client.send(
  new PutBucketVersioningCommand({
    Bucket: "my-bucket",
    VersioningConfiguration: { Status: "Enabled" },
  }),
);
```

A request to suspend versioning is answered `501 NotImplemented`. Keeping storage from growing
with old versions is the work of a [lifecycle policy](https://docs.strg.com/lifecycle-policies) that removes
noncurrent versions, as in
[Keeping 30 days of old versions](https://docs.strg.com/lifecycle-policies#keeping-30-days-of-old-versions).

## Related

- [GetBucketVersioning](https://docs.strg.com/s3-api/GetBucketVersioning)
- [ListObjectVersions](https://docs.strg.com/s3-api/ListObjectVersions) - every version and delete marker
- [DeleteObject](https://docs.strg.com/s3-api/DeleteObject) - delete markers and permanent deletes
- [PutBucketLifecycleConfiguration](https://docs.strg.com/s3-api/PutBucketLifecycleConfiguration) - expiring noncurrent versions
- [Lifecycle policies](https://docs.strg.com/lifecycle-policies)
- [Access keys](https://docs.strg.com/access-keys)
- [S3 API overview](https://docs.strg.com/s3-api/overview)
