Your coding agent uploaded tenant-42/patch.diff at 14:02, then a second tool turn “fixed” it at 14:07 and overwrote the evidence you needed for a customer dispute. Or worse: a prompt-injected agent called DeleteObject on last week’s eval traces. S3 Conditional Writes stop accidental clobber races with If-None-Match. S3 Object Lock stops authorized overwrite and delete for a retention period — WORM storage for agent artifacts, audit packs, and model-eval baselines. Pair with Macie for secret scanning and versioning always on.
⚡ TL;DR: Enable Object Lock (requires versioning) on artifact / audit buckets at create time. Prefer Governance mode for most agent artifacts; Compliance mode for regulated audit packs. Set default retention days; agents PutObject only — never BypassGovernanceRetention in task roles. Related: Conditional Writes, Macie, SCPs, Budgets.
Conditional writes vs Object Lock (do not conflate)
| Mechanism | Protects against | Agent can still… |
|---|---|---|
If-None-Match: * |
Two writers racing the same new key | Overwrite after success; DeleteObject |
| Versioning alone | Silent data loss (old versions kept) | Delete markers; lifecycle purge |
| Object Lock Governance | Overwrite/delete until retention ends (bypass with special perm) | New versions if allowed by lock settings |
| Object Lock Compliance | Overwrite/delete until retention ends (no user bypass) | Only wait out retention |
Use conditional writes for idempotent tool uploads. Use Object Lock for immutability SLAs (audit, legal hold, ransomware resistance). Many teams want both on the same bucket.
Enable Object Lock on artifact buckets
Object Lock must be enabled at bucket creation (or on a new bucket you migrate into). Versioning is mandatory.
# ✅ create artifact bucket with Object Lock enabled
aws s3api create-bucket \
--bucket coding-agent-artifacts-prod \
--object-lock-enabled-for-bucket \
--region us-east-1
aws s3api put-object-lock-configuration \
--bucket coding-agent-artifacts-prod \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "GOVERNANCE",
"Days": 30
}
}
}'
// ✅ CDK: Object Lock + default governance retention for agent artifacts
const bucket = new s3.Bucket(this, "AgentArtifacts", {
bucketName: "coding-agent-artifacts-prod",
versioned: true,
objectLockEnabled: true,
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
});
// Set default retention via CfnBucket ObjectLockConfiguration or custom resource
❌ Enabling Lock on a bucket that already holds mutable scratch pads without a migration plan: agents that expected overwrite semantics will start failing PutObject in confusing ways — split scratch vs immutable-audit prefixes/buckets.
Agent IAM: write once, never bypass
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AgentPutOnly",
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::coding-agent-artifacts-prod",
"arn:aws:s3:::coding-agent-artifacts-prod/tenants/${aws:PrincipalTag/tenant_id}/*"
]
},
{
"Sid": "DenyLockBypassAndDeletes",
"Effect": "Deny",
"Action": [
"s3:BypassGovernanceRetention",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObjectRetention",
"s3:PutObjectLegalHold"
],
"Resource": "arn:aws:s3:::coding-agent-artifacts-prod/*"
}
]
}
# ✅ agent upload with explicit retention headers (optional per-object)
import boto3
from datetime import datetime, timedelta, timezone
s3 = boto3.client("s3")
def put_immutable_artifact(bucket: str, key: str, body: bytes, days: int = 30):
retain_until = datetime.now(timezone.utc) + timedelta(days=days)
# ❌ Do not grant the task role s3:BypassGovernanceRetention "just in case"
return s3.put_object(
Bucket=bucket,
Key=key,
Body=body,
ContentType="application/octet-stream",
ObjectLockMode="GOVERNANCE",
ObjectLockRetainUntilDate=retain_until,
# Optional: combine with conditional create for race safety
# IfNoneMatch="*", # when using conditional writes API shape
)
For brand-new keys, prefer also conditional writes so two parallel tool turns do not create dueling versions unintentionally — Lock preserves both versions; your UX may still want single-writer semantics.
Governance vs Compliance for agent workloads
| Mode | Who can shorten retention | Use for |
|---|---|---|
| Governance | Principals with s3:BypassGovernanceRetention |
Default agent artifacts, eval traces, patch diffs |
| Compliance | Nobody (root included) until date passes | Regulated audit exports, signed customer delivery packs |
| Legal Hold | Independent of retention date | Active incident / lawsuit freeze |
Start with Governance + deny Bypass on agent roles + allow Bypass only on a break-glass SecOps role protected by SCPs and MFA.
Lifecycle, cost, and Macie
Immutability is not infinite storage:
- Lifecycle noncurrent version expiration after retention elapses
- Separate short-lived scratch bucket (no Lock) for intermediate tool files
- Macie still scans locked objects — findings do not require overwrite; quarantine via copy-out + IAM deny
# ✅ lifecycle: expire noncurrent after retention window + buffer
aws s3api put-bucket-lifecycle-configuration \
--bucket coding-agent-artifacts-prod \
--lifecycle-configuration '{
"Rules": [{
"ID": "expire-old-versions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {"NoncurrentDays": 37},
"Filter": {"Prefix": "tenants/"}
}]
}'
Watch storage with Budgets / Cost Anomaly — a runaway agent writing GB of locked logs will not be deletable for 30 days.
Production checklist
- [ ] Artifact/audit buckets created with Object Lock + versioning
- [ ] Default retention set (e.g. 30d Governance); Compliance only where required
- [ ] Agent task roles: Put/Get/List only; Deny Bypass + Delete*
- [ ] Break-glass SecOps role for Governance bypass; MFA + SCP
- [ ] Scratch/mutable data in a different bucket without Lock
- [ ] Conditional writes on create paths to reduce version spam
- [ ] Macie enabled on artifact buckets; Security Hub aggregation
- [ ] Runbook: legal hold for incidents; customer export packs use Compliance
FAQ
Q: Can I add Object Lock to an existing bucket?
A: Not retrospectively in-place for the feature flag — create a new Lock-enabled bucket and replicate/migrate. Plan prefixes before agents go live.
Q: Does Lock replace backups?
A: No. It prevents tampering/deletion in-account for a window. Still replicate critical audit packs cross-account.
Q: What about Overwrite via new version?
A: Object Lock retention applies per object version. Putting a “new” key version does not erase the locked old version — good for forensics, costly if agents rewrite constantly. Design keys as append-only (…/turns/0007.diff).
Key design: append-only paths for multi-turn agents
Agents rewrite the same logical artifact across turns. If you reuse tenants/42/final.patch, Governance Lock will either reject the overwrite or accumulate versions forever. Prefer append-only keys:
tenants/{tenant_id}/tasks/{task_id}/turns/{turn:04d}/patch.diff
tenants/{tenant_id}/tasks/{task_id}/turns/{turn:04d}/test.log
tenants/{tenant_id}/tasks/{task_id}/manifest.json # points at latest turn
Keep manifest.json in a mutable control bucket (or accept Governance + SecOps bypass for manifests only). Lock the turn payloads. Query history with Athena on S3 over the turn prefix — immutability makes eval reproducibility boring in the best way.
// ✅ key helper — never clobber turn N
export function artifactKey(tenant: string, task: string, turn: number, file: string) {
return `tenants/${tenant}/tasks/${task}/turns/${String(turn).padStart(4, "0")}/${file}`;
}
Ransomware / malicious agent deletes then become no-ops on locked versions; recovery is “point manifest at last known good turn,” not “hope backups finished.”
Related reading
- S3 Conditional Writes: Idempotent Agent Artifact Uploads
- Amazon Macie: Scan Artifact Buckets for Secrets
- AWS Organizations SCPs: Hard Caps
- Athena: Query Coding-Agent Eval Traces
S3 Object Lock turns coding-agent artifact buckets from mutable scratch pads into WORM evidence stores. Combine with conditional writes for races, deny Bypass on task roles, and keep scratch elsewhere — so agents can ship patches without rewriting history.
Last updated on September 26, 2026
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
- Spec-First AI Development: OpenAPI Remains the Only Source of Truth
- Java Virtual Threads vs Traditional Threads: What Nobody Tells You
- Distributed Locks Reality Check: When Redis Redlock Is the Wrong Tool
Newly added
- Amazon Bedrock Model Evaluation: Score Coding-Agent Outputs Before You Promote a Prompt
- AWS App Runner: Host Coding-Agent HTTP APIs Without Babysitting Containers
- Amazon S3 Object Lock: Immutable Artifact Buckets Coding Agents Cannot Overwrite
- AWS Security Hub: Aggregate Coding-Agent Security Findings Across Accounts
- Amazon Detective: Investigate Compromised Coding-Agent Credentials After GuardDuty
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.