Skip to content

docs: propose the S3 object-lock design (per-key version-state tree) - #77

Merged
frrist merged 1 commit into
mainfrom
design/s3-object-lock
Aug 14, 2026
Merged

docs: propose the S3 object-lock design (per-key version-state tree)#77
frrist merged 1 commit into
mainfrom
design/s3-object-lock

Conversation

@frrist

@frrist frrist commented Aug 11, 2026

Copy link
Copy Markdown
Member

Implementation spec for S3 Object Lock, following the versioning-doc workflow: this PR is the design; the implementation follows as a stacked PR on this branch.

The shape of the feature: versitygw's controller layer owns every WORM decision (auth.CheckObjectAccess runs before Put/Copy/Delete/Complete reach the backend), so ingot's job is storage plus exact sentinel errors — no enforcement logic on our write or delete paths (§1–§2).

The decision to review (§3): where per-version retention and legal holds live.
Proposed: a per-key version-state tree — the leaf gains a State sub-tree beside Prev, mapping revSeqKey(seq) to a small VersionState block. Mutations rewrite only positional blocks, so manifests stay immutable and versioning invariant 5 holds verbatim; the state rides the catalog plane to Forge (the replication/recovery argument); stamping and cleanup are atomic with the commits that create and remove versions. The alternative (registry rows: simpler, SQL-queryable, but post-commit crash windows and catalog-incomplete) is specified alongside; manifest rewrite, inline leaf state, and a parallel bucket tree are rejected in Appendix A.

Also of note:

  • VersionState reserves a Tags field now so object tagging (next up) lands as handlers with zero format change; the merge/empty-elision/cleanup rules are written feature-generically (§4.1, §9).
  • Governance bypass defers to the bucket-policy feature: the controller authorizes bypass off the bucket policy document, so until policies land GOVERNANCE denies exactly like COMPLIANCE, with a documented same-mode-replacement escape hatch (§10).
  • Conformance scoping (§11): new itest lock categories, the TestWORMProtection exclusions (upstream's unversioned-gateway semantics that a versioned ingot bucket can never exhibit), and the XFail rows that flip when this lands.
  • §12 records the landing path for each deferred feature (policies, tagging, object ACLs, Batch Ops, lifecycle, materialized defaults) — none reshapes this design.

🤖 Generated with Claude Code

@hannahhoward hannahhoward left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I did do a fairly close read but I don't see anything I object to. This looks like a very sound design. Thank you versitygw for enforcing the rules for us.

Object Lock as storage plus sentinel errors: versitygw's controller layer
owns all WORM enforcement (auth.CheckObjectAccess), so ingot's backend
stores lock state and returns the exact absent-case sentinels. Per-version
retention and legal holds live in a per-key version-state tree beside the
prev tree: state mutations rewrite only positional blocks (state block,
state tree, leaf, top path), so manifests stay immutable and version
identity (invariant 5 of the versioning design) holds verbatim, while the
state rides the catalog plane and ships to Forge. Stamping and cleanup are
atomic with the commits that create and remove versions. The VersionState
block is the general home for mutable per-version service state: lock is
its first tenant, and the reserved Tags field lets object tagging follow as
handlers with no format change. The State field joins ObjectLeaf under the
existing "/objectleaf/0" union key with no compatibility shim, per the
repo's dev-only data posture; the union's format-revision mechanism stays
in reserve for revisions that need loud incompatibility.

The registry-rows alternative is specified alongside for review; manifest
rewrite, inline leaf state, and a parallel bucket tree are rejected in
Appendix A. Governance bypass defers to the bucket-policy feature: the
controller authorizes bypass off the bucket policy document, so until
policies land GOVERNANCE denies exactly like COMPLIANCE.

Conformance details pinned by the implementation run against the versitygw
suite: key existence outranks the lock-enabled gate in the §6 check order
(GetObjectRetention_non_existing_object runs against a lock-free bucket
and expects NoSuchKey, matching posix); the missing-configuration error
splits by path, with the spaced variant on the four per-version methods
and the NoSpaces variant on the creation-time header paths; and the
controller passes an absent retain-until header as a pointer to the zero
time. §11 records the versioning-disabled-mode exclusion and the
versioned-teardown home for lock-enabled-bucket cases from plain-conf
groups.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@frrist
frrist force-pushed the design/s3-object-lock branch from 6a4dc09 to 6ca9436 Compare August 14, 2026 00:56
@frrist
frrist merged commit c296b72 into main Aug 14, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants