Object Lock
Object Lock makes object versions immutable - write once, read many, often called WORM. A locked version stays exactly as it was written: nobody can delete or overwrite it until its lock ends. It exists for the data that has to survive no matter what happens to the keys and scripts around it. Object Lock is switched on per bucket, and from then on versions are locked either automatically, through a default retention, or one by one through the S3 API.
When you'd use it
These are the situations Object Lock is made for:
| Ransomware protection | A stolen access key cannot delete or encrypt away locked backups: every locked version stays readable until its lock ends |
| Protection from mistakes | A script or a person deleting the wrong prefix just writes delete markers over locked versions; the data stays where it was |
| Retention obligations | Records a regulation says must be kept for a fixed period stay in place until that period is over |
- Enabling Object Lock is permanent. Once it is enabled on a bucket, it stays enabled for as long as the bucket exists. There is no way to turn it off - in the dashboard or through the S3 API.
- Locked object versions cannot be deleted until their retention expires. In compliance mode nobody can unlock one early - not you, not any access key, not STRG support. A retention period set too long cannot be corrected; the only way out is to wait.
- Locked data is billed until the lock expires, even when you no longer need it. With a default retention, every object uploaded is billed for at least the retention period - one deleted the day after its upload included. Lifecycle rules cannot remove a locked version either; one that comes due while its lock is still running is skipped and removed once the lock has expired.
- A default retention applies to new objects only. Objects already in the bucket are not affected, and shortening or removing the default releases nothing that is already locked.
How it works
Every bucket keeps all versions of every object. A delete without a version ID writes a delete marker, and an upload under an existing key writes a new version - neither touches a locked version, which stays, and stays billed. Removing data means deleting one specific version, and that is exactly what a lock prevents.
A version is protected while any one of these applies:
| Protection | Ends | Who can remove it early |
|---|---|---|
| Governance mode retention | At its retain-until date | An access key with the Bypass governance retention permission, sending x-amz-bypass-governance-retention: true - nobody else. Extending it needs no bypass |
| Compliance mode retention | At its retain-until date | Nobody. The date can be extended, never shortened |
| Legal hold | When the hold is lifted | Any access key with the Set legal holds permission. It has no end date and is independent of the two modes |
Governance mode suits the cases where a mistake should still be fixable by whoever holds a specially trusted key; compliance mode is for the records that must outlast everyone's good intentions. A legal hold is the tool for an investigation or a dispute, when the end date is not known yet.
Retention and legal holds are set per object version. A bucket's default retention is the retention every object uploaded or copied into the bucket inherits from then on:
| Setting | Values |
|---|---|
| Mode | Governance or compliance |
| Period | 1 to 3,000 days, or 1 to 8 years - one or the other, not both |
A legal hold is never a bucket default: it belongs to individual objects.
Retention and legal holds need a bucket with Object Lock enabled.
Pairing Object Lock with lifecycle rules
Object Lock protects versions, and lifecycle rules keep what is left over from piling up. We recommend a bucket with Object Lock always has two of them, whatever the retention period:
| Rule | Suggested period | Why |
|---|---|---|
| Permanently delete previous versions after | 14 days - or 1 day behind a backup tool such as Kopia or restic that prunes its own data | Overwritten and deleted versions otherwise stay stored and billed for good, once their lock has ended |
| Abort incomplete multipart uploads after | 7 days | A large upload that never finished leaves parts behind that are stored and billed until something clears them away |
The two clocks are independent, which is what makes the pairing safe:
- A lifecycle rule never removes a locked version, whatever its number of days. A version still under its lock is skipped and removed once the lock has expired. The previous-versions period is therefore no protection for the data and no risk to the lock, and it is best not set equal to the retention period - cost and recovery needs are what decide it.
- The two start at different moments. The lock runs from the upload; the lifecycle clock runs from when the version stopped being current. A backup tool that deletes its data only long after writing it hands the rule versions whose lock has already lapsed, which is why a short period such as 1 day fits there.
- Locked versions stay billed until their lock expires, with or without a rule.
For ransomware protection through a backup tool, the tool's own snapshot retention should be longer than the lock, so that the tool only asks for deletes once the lock has expired.
Enabling Object Lock in the dashboard
The Object Lock card on a bucket's page in the dashboard at https://app.strg.com is where
it is switched on and where its default retention lives. On a narrow screen, the card is on the
bucket's Settings tab.
-
The card's Enable button opens the dialog - or Edit, once Object Lock is on.
-
Under Default retention, the dialog offers three choices:
Choice What the dialog says No default retention "Nothing is locked automatically. Objects can still be locked one by one through the S3 API." Governance mode "Every new object is locked. Only an access key allowed to bypass governance can delete it early or shorten its lock." Compliance mode "Every new object is locked, and nobody can delete it early or shorten its lock - not you, and not STRG." -
With a mode chosen, Lock each new object for takes a number of Days or Years.
-
Enable Object Lock (or Save default retention) saves the choice.
Because a save that locks anything cannot be taken back - enabling Object Lock at all, or setting a default retention - the dialog first asks for a tick beside:
I understand that Object Lock cannot be turned off, and that locked objects cannot be deleted and keep being billed until their lock expires.
(When Object Lock is already on, the first clause is left out.) Compliance mode also asks for
the bucket's name to be typed, under "Type my-bucket to confirm compliance mode". Removing a
default retention needs neither: it only stops locking new objects.
Once enabled, the card shows a Permanent tag, and a Compliance tag while the default retention is in compliance mode.
In the object browser, an object's details show its retention and legal hold. While a version is under retention or a legal hold, the dashboard's permanent delete for it is disabled. The dashboard never bypasses governance retention: a folder deletion skips locked versions and reports how many it could not delete.
Enabling Object Lock through the S3 API
PutObjectLockConfiguration enables Object Lock on an existing bucket and sets, changes or removes its default retention. It needs the Manage Object Lock permission and Read Object Lock settings beside it, because the bucket's current configuration is read before every change.
This example enables Object Lock with a 30-day governance-mode default retention:
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api put-object-lock-configuration \
--bucket my-bucket \
--object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"GOVERNANCE","Days":30}}}' \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
s3.put_object_lock_configuration(
Bucket="my-bucket",
ObjectLockConfiguration={
"ObjectLockEnabled": "Enabled",
"Rule": {"DefaultRetention": {"Mode": "GOVERNANCE", "Days": 30}},
},
)
import { PutObjectLockConfigurationCommand, S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
await s3.send(
new PutObjectLockConfigurationCommand({
Bucket: "my-bucket",
ObjectLockConfiguration: {
ObjectLockEnabled: "Enabled",
Rule: { DefaultRetention: { Mode: "GOVERNANCE", Days: 30 } },
},
}),
);
A configuration with ObjectLockEnabled set to Enabled and no Rule removes the default
retention. Object Lock stays enabled.
STRG checks each configuration before it is saved, so a mistake shows up straight away with one of these answers:
| Request | Answer |
|---|---|
A configuration without ObjectLockEnabled set to Enabled | 400 InvalidRequest - Object Lock can be enabled, never disabled |
| A default retention over 3,000 days or 8 years | 400 InvalidArgument |
Both Days and Years, a period below 1, a mode other than GOVERNANCE or COMPLIANCE, a Rule without ObjectLockEnabled set to Enabled, or an element S3 does not define | 400 MalformedXML |
No Content-MD5 or x-amz-checksum-* header | 400 InvalidRequest. The AWS SDKs and CLI send a checksum by default |
GetObjectLockConfiguration reads the configuration, with
Read Object Lock settings. A bucket without Object Lock answers
404 ObjectLockConfigurationNotFoundError.
Object Lock at bucket creation
Object Lock is switched on after a bucket exists rather than as part of creating it:
CreateBucket with x-amz-bucket-object-lock-enabled: true answers
501 NotImplemented, so a client knows straight away. Creating the bucket and then sending
PutObjectLockConfiguration, as above, gives the same
result.
Locking individual objects
Through the S3 API, a version can be locked as it is uploaded, or an existing version locked later. Without a version ID, a request applies to the current version.
| To | Operation or header | Permission |
|---|---|---|
| Upload a version with a retention | x-amz-object-lock-mode and x-amz-object-lock-retain-until-date on PutObject, CopyObject or CreateMultipartUpload | Set retention, beside Write objects |
| Upload a version under a legal hold | x-amz-object-lock-legal-hold: ON on the same operations | Set legal holds, beside Write objects |
| Set or extend a version's retention | PutObjectRetention | Set retention |
| Shorten or remove a governance-mode retention | PutObjectRetention with x-amz-bypass-governance-retention: true | Set retention and Bypass governance retention |
| Read a version's retention | GetObjectRetention | Read retention |
| Place or lift a legal hold | PutObjectLegalHold, ON or OFF | Set legal holds |
| Read a version's legal hold | GetObjectLegalHold | Read legal holds |
Locking here is by retention date or legal hold. S3's event holds (x-amz-object-lock-event-hold
and related headers) answer 501 NotImplemented, so a client knows straight away.
This example uploads an object locked in governance mode until 1 January 2027, places a legal hold on it, and reads both back:
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api put-object \
--bucket my-bucket \
--key photos/2024/cat.jpg \
--body cat.jpg \
--object-lock-mode GOVERNANCE \
--object-lock-retain-until-date 2027-01-01T00:00:00Z \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
aws s3api put-object-legal-hold \
--bucket my-bucket \
--key photos/2024/cat.jpg \
--legal-hold Status=ON \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
aws s3api get-object-retention \
--bucket my-bucket \
--key photos/2024/cat.jpg \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
aws s3api get-object-legal-hold \
--bucket my-bucket \
--key photos/2024/cat.jpg \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
from datetime import datetime, timezone
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
with open("cat.jpg", "rb") as body:
s3.put_object(
Bucket="my-bucket",
Key="photos/2024/cat.jpg",
Body=body,
ObjectLockMode="GOVERNANCE",
ObjectLockRetainUntilDate=datetime(2027, 1, 1, tzinfo=timezone.utc),
)
s3.put_object_legal_hold(
Bucket="my-bucket",
Key="photos/2024/cat.jpg",
LegalHold={"Status": "ON"},
)
print(s3.get_object_retention(Bucket="my-bucket", Key="photos/2024/cat.jpg")["Retention"])
print(s3.get_object_legal_hold(Bucket="my-bucket", Key="photos/2024/cat.jpg")["LegalHold"])
import { readFile } from "node:fs/promises";
import {
GetObjectLegalHoldCommand,
GetObjectRetentionCommand,
PutObjectCommand,
PutObjectLegalHoldCommand,
S3Client,
} from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
const Bucket = "my-bucket";
const Key = "photos/2024/cat.jpg";
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body: await readFile("cat.jpg"),
ObjectLockMode: "GOVERNANCE",
ObjectLockRetainUntilDate: new Date("2027-01-01T00:00:00Z"),
}),
);
await s3.send(new PutObjectLegalHoldCommand({ Bucket, Key, LegalHold: { Status: "ON" } }));
const { Retention } = await s3.send(new GetObjectRetentionCommand({ Bucket, Key }));
const { LegalHold } = await s3.send(new GetObjectLegalHoldCommand({ Bucket, Key }));
console.log(Retention, LegalHold);
Bypassing governance retention
Governance mode leaves one door open, for the day a lock turns out to be a mistake: an access key
with the Bypass governance retention permission can permanently delete a version locked in
governance mode, or shorten or remove its retention, by sending
x-amz-bypass-governance-retention: true on:
| Operation | Also needs |
|---|---|
DeleteObject with a versionId | Delete object versions |
| DeleteObjects with version IDs | Delete object versions |
| PutObjectRetention | Set retention |
The permission comes with Delete object versions or Set retention beside it, since on its own
it would have nothing to bypass. A request carrying the header from a key without the permission
answers 403 AccessDenied. For a key without the permission, governance mode is as immovable as
compliance mode. No key can bypass compliance mode or a legal hold, and the dashboard never
bypasses governance retention.
- AWS CLI
- Python (boto3)
- JavaScript (AWS SDK v3)
aws s3api delete-object \
--bucket my-bucket \
--key photos/2024/cat.jpg \
--version-id "$VERSION_ID" \
--bypass-governance-retention \
--endpoint-url https://s3.eu-central-1.strg.com \
--region eu-central-1
import os
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.eu-central-1.strg.com",
region_name="eu-central-1",
)
s3.delete_object(
Bucket="my-bucket",
Key="photos/2024/cat.jpg",
VersionId=os.environ["VERSION_ID"],
BypassGovernanceRetention=True,
)
import { DeleteObjectCommand, S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.eu-central-1.strg.com",
region: "eu-central-1",
});
await s3.send(
new DeleteObjectCommand({
Bucket: "my-bucket",
Key: "photos/2024/cat.jpg",
VersionId: process.env.VERSION_ID,
BypassGovernanceRetention: true,
}),
);
VERSION_ID holds the version to delete, as listed by
ListObjectVersions.
Object Lock and agents
Agents can work with Object Lock too, and a human stays in charge of every lock. Through the MCP server, an agent whose key lacks the Act without confirmation permission asks you to confirm each call that sets retention, a legal hold or a bucket's Object Lock, and each one bypassing governance retention. The Ask our AI Assistant in the dashboard can fill in the Object Lock dialog for you; the acknowledgement, the typed bucket name and the save are always yours.
Permissions
These permissions are granted on an access key on the Access Keys page - Access keys has the details.
| Permission | Allows | Needs beside it |
|---|---|---|
| Read Object Lock settings | GetObjectLockConfiguration | - |
| Manage Object Lock | PutObjectLockConfiguration - enabling Object Lock and changing the default retention | Read Object Lock settings |
| Read retention | GetObjectRetention | - |
| Set retention | PutObjectRetention, and uploading a version with a retention | - |
| Bypass governance retention | x-amz-bypass-governance-retention on DeleteObject, DeleteObjects and PutObjectRetention | Delete object versions or Set retention |
| Read legal holds | GetObjectLegalHold | - |
| Set legal holds | PutObjectLegalHold, and uploading a version under a legal hold | - |
Related
- Lifecycle policies - they cannot remove a locked version, and pair with Object Lock as above
- Access keys
- S3 API overview