Skip to content

feat(s3/bucket): allow s3:BypassGovernanceRetention in policies - #88

Merged
alanshaw merged 1 commit into
mainfrom
ash/feat/bypass-governance-action
Oct 22, 2026
Merged

alanshaw merged 1 commit into
mainfrom
ash/feat/bypass-governance-action

Conversation

@alanshaw

Copy link
Copy Markdown
Member

S3 lets a bucket policy grant s3:BypassGovernanceRetention, the right to delete or shorten a governance-locked version with the x-amz-bypass-governance-retention header. This adds it to the policy action allowlist so a principal can hold it.

Hilt's follow-up prices that header on DeleteObject, DeleteObjects and PutObjectRetention against the permission, for both key kinds. Ingot's gateway layer keeps refusing governance bypass for now, whatever the key holds, so the permission has no effect on the wire until that side is done; this and the Hilt change settle the vocabulary and the authorization decision ahead of it.

馃 Generated with Claude Code

A principal may be granted the right to delete or shorten a
governance-locked version, as S3 allows in a bucket policy. Hilt prices
the bypass header on the delete and retention operations against it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alanshaw
alanshaw merged commit bcdd5ea into main Oct 22, 2026
8 checks passed
@alanshaw
alanshaw deleted the ash/feat/bypass-governance-action branch October 22, 2026 12:12
alanshaw added a commit to fil-forge/hilt that referenced this pull request Oct 22, 2026
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
alanshaw added a commit to fil-forge/hilt that referenced this pull request Oct 22, 2026
A delete or a retention write that asks to bypass governance retention,
with `x-amz-bypass-governance-retention: true`, needs
`s3:BypassGovernanceRetention` in S3. Hilt had no such permission, so
the header carried no decision here. This is the Hilt half of governance
bypass; the gateway half stays as it is.

### What changes

`s3:BypassGovernanceRetention` joins the vocabulary as a policy action,
added by fil-forge/libforge#88 and now on libforge main, which this
pins, and as a service-key permission. It maps to no Forge commands of
its own, since the delete or retention write it rides on carries them.

The header is priced like the other header-added permissions: on
DeleteObject, DeleteObjectVersion, DeleteObjects and PutObjectRetention,
read on its first value case-insensitively as the gateway reads it, and
only when the signature covers it. A service key must hold the
permission and a principal's effective set must grant it.

### What does not change

Ingot's gateway layer decides the bypass itself against the bucket's
lock state, and today refuses it whatever the key holds, since it looks
for the grant in a bucket policy document Ingot does not serve
internally. So the permission has no effect on the wire yet: a request
carrying the header is refused by Hilt without the permission, and by
the gateway with it. The gateway hook that would honour Hilt's answer is
the parked other half.

### Behaviour changes to know about

- A request carrying the bypass header from a key or principal without
the permission is now refused by Hilt as not permitted, where before it
reached the gateway and was refused there. The outcome is the same; the
refusing side moves.
- A bypass header the signature does not cover is refused as
`UnsignedHeader`, like the lock headers.
- Keys issued with explicit permission lists, and policies that list
actions, can now name `s3:BypassGovernanceRetention`.

馃 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant