> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Versioning and Object Lock

> Keeping previous versions, and the retention modes that stop an object being deleted at all.

## Versioning

<Tabs>
  <Tab title="Console">
    The **Versioning** card on the bucket's **Settings** tab shows the current
    state and offers **Enable** and **Suspend**.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    PUT /v1/buckets/{bucket}/versioning
    { "status": "enabled" }
    ```

    `status` takes `enabled` or `suspended` only. There is no way back to
    `disabled` — that state means a bucket that never had versioning, not one
    that had it turned off.
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic storage bucket set-versioning <bucket> --status enabled
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := storage.New(cfg).PutBucketVersioning(ctx, bucket,
        &storage.PutBucketVersioningRequest{Status: "enabled"})
    ```
  </Tab>
</Tabs>

A bucket sits in one of three states:

<ResponseField name="disabled" type="never configured">
  The bucket has no versioning history. On the S3 wire this is reported by
  omitting the status element entirely, which is how S3 itself reports it.
</ResponseField>

<ResponseField name="enabled" type="every write creates a version">
  Deletes write a delete marker instead of removing bytes. `GET
      /v1/buckets/{bucket}/object-versions` lists versions and delete markers
  together.
</ResponseField>

<ResponseField name="suspended" type="was on, now off">
  **Existing versions are kept**; new writes stop creating them. This is
  distinct from `disabled`, and the distinction matters: suspending does not
  delete history.
</ResponseField>

The storage API uses the platform's lowercase vocabulary; the S3 endpoint spells
the same states `Enabled` and `Suspended` in its XML. Either spelling is
accepted on input.

<Warning>
  Versions you no longer need are not free — they count against your stored
  bytes. Pair versioning with a `noncurrent_version_expiration` lifecycle rule,
  or a suspended bucket quietly keeps every version it ever made.
</Warning>

## Object Lock and retention

On a bucket created with `object_lock_enabled`, individual objects can carry a
retention mode and a retain-until date, set at upload with
`X-Amz-Object-Lock-Mode` and `X-Amz-Object-Lock-Retain-Until-Date`, or afterwards
through the `?retention` sub-resource.

<Columns cols={2}>
  <Card title="GOVERNANCE" icon="shield">
    Retention can be shortened or a locked object deleted, but only by a caller
    who sends `X-Amz-Bypass-Governance-Retention: true` **and** holds
    `storage:BypassGovernanceRetention` on the object.
  </Card>

  <Card title="COMPLIANCE" icon="lock">
    Nothing bypasses it. A `?retention` write that would move the date earlier
    is refused, so retention can only ever be extended.
  </Card>
</Columns>

A legal hold (`?legal-hold`) is independent of the retention date: while it is
on, the object cannot be deleted regardless of when retention expires. A delete
blocked by either shows up as `403`, not a silent no-op.

In the console both live on the object itself: its **Properties** tab has a
**Retention** card (**Mode**, **Retain until**) and a **Legal hold** switch,
and **Save changes** applies them together. Shortening a `GOVERNANCE` date
offers the bypass as a switch rather than making you send the header. The
upload page has no object-lock fields, though, so setting retention **at
upload** is API only.
