Skip to content

v0.5.0 — Object versioning

Choose a tag to compare

@incognick incognick released this 16 Jun 03:15
· 64 commits to main since this release
Immutable release. Only release title and notes can be modified.

The fifth Hamster release: object versioning — the S3 versioning API, served on both the single-node store and the erasure-coded cluster.

Dev preview. Versioning is complete, but the surrounding v0.x limits still hold: on the cluster path the S3 write path commits only on the Raft leader (a non-leader answers 503 SlowDown, clients retry elsewhere), multipart and server-side copy are still not on the cluster path, a single-node serve store still cannot become a cluster in place, and on-disk/on-wire formats may still change between v0 releases (they are additively versioned, but nothing is frozen yet). Please don't trust real data to it.

What's in v0.5

The metadata has modeled every key as an ordered list of versions since v0.1 (it is the third load-bearing invariant), so this release is the S3 surface that finally exposes it — and enabling versioning never migrates a schema.

  • Per-bucket versioning config. PutBucketVersioning / GetBucketVersioning over the ?versioning subresource: Enabled and Suspended. An object-lock bucket cannot be suspended (the guard that protects WORM data, ahead of the lock surface in v0.6). MFA Delete is out of scope — object lock is Hamster's WORM mechanism, and an attempt to enable MFA Delete is refused honestly rather than silently dropped.
  • Version IDs everywhere. x-amz-version-id on PUT, on versioned GET/HEAD, and on DELETE — the hex ID on an enabled bucket, null for the null version under suspension, omitted on a bucket that was never versioned, exactly as S3 does.
  • Reads and deletes by version. GetObject / HeadObject / DeleteObject with ?versionId= (including versionId=null). A DELETE with no ID drops a delete marker; with an ID it permanently destroys that one version and frees its data. A GET or HEAD addressing a delete marker is 405 MethodNotAllowed with x-amz-delete-marker; a missing version is 404 NoSuchVersion.
  • ListObjectVersions. The ?versions subresource: versions and delete markers across keys in S3 order (key ascending, newest version first), with IsLatest, delimiter-grouped CommonPrefixes, and key-marker + version-id-marker pagination.
  • All of it on the cluster, too. The same surface runs over cluster run -s3: a by-version GET fetches that version's shards through the erasure-coded data path, and a permanent version delete reclaims its shards across the cluster. Versions store independent shards — no cross-version sharing.

How it's verified

  • Unit tests at every layer: the paged version-keyspace read (ordering, IsLatest across pages, mid-key resume, prefix filtering) in the metadata model, and the full HTTP surface in the gateway (config round trip, the two-version lifecycle, delete markers, delimiter-grouped listing with pagination).
  • A cluster end-to-end test over real processes and loopback mTLS: enable versioning, store two erasure-coded versions of one key, read the current and each prior version by ID through the data path, list versions, drop a delete marker (current read 404s, old versions still read), and permanently delete a version — freeing its shards while its sibling survives.
  • The real aws CLI under task compat: put-bucket-versioning, get-bucket-versioning, version IDs on put-object, list-object-versions, get-object --version-id, and delete-object with and without a version id. rclone, restic, and s3cmd keep passing, as does the race detector and the deterministic simulation harness.

Binaries below are static (CGO_ENABLED=0), version-stamped (hamster version), with SHA-256 checksums in SHA256SUMS. Next up, v0.6: object lock — GOVERNANCE and COMPLIANCE retention and legal holds, building directly on the versioning this release ships. The metadata already carries the lock fields and the apply-layer guard that no delete path may bypass; v0.6 adds the S3 surface over it.