Repository navigation
Conversation
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06b3229ff3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
06b3229 to
2a810d4
Compare
7dd416b to
51b19bd
Compare
|
@codex review |
51b19bd to
bed7fc2
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51b19bdd05
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d68d3f3 to
4f776ff
Compare
4f776ff to
919e46f
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
919e46f to
1443e96
Compare
3c66ea4 to
e66184b
Compare
The gateway renders a refused x-bucket-policy header with the code a refused PutBucketPolicy body gets, MalformedPolicy, from fil-forge/ingot#194 on; InvalidArgument stays accepted until that is on main. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
e66184b to
7db9029
Compare
…d answered An object GET whose query carries a policy key kept being labeled application/json. The middleware now requires the ETag holder the policy backend fills. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/s3/bucket/policy now returns the policy as a libforge BucketPolicy. GetBucketPolicy marshals it to the AWS JSON document, the same bytes Hilt stores. Pins hilt ee26a9b (#96 branch) and libforge 12ca555 (#80 branch), temporary pins to re-pin before merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The InvalidBucketPolicy error name and the ARN helpers the policy itest uses now come from libforge's commands/s3/bucket, where Hilt moved them. Pins hilt 24fef2d (#96 branch) and libforge 64602c9 (#80 branch), temporary pins to re-pin before merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on the wire) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ngSet) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n hilt 23320b8d, libforge 44def4f Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ingot tags the document itself with libforge's ETag, the same function Hilt stores and checks If-Match with: a GET tags the document Hilt returned, a PUT the body Hilt accepted. The /s3/bucket/policy receipt carries no tag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ge 222cd95 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
f098999 to
1b99c33
Compare
Published from fil-forge/ingot#194 - Digest: `sha256:0fefac05d4bf51eac58b1ce4a20e938ec0cdfcdf112b1835bfc93c198a197d32` - Commit: fil-forge/ingot@1c47c14 - Publish run: https://github.com/fil-forge/ingot/actions/runs/38558109059 Merging rewrites [`nodes/dev/apps/versions.env`](https://github.com/fil-forge/infra-nodes/blob/main/nodes/dev/apps/versions.env) and queues the image. The node picks it up on its next reconcile pass, waits for a safe proving window and then restarts the service, so the deploy happens well after this merges. Co-authored-by: fil-forge-bot[bot] <318653112+fil-forge-bot[bot]@users.noreply.github.com>
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:d686c7d`](https://github.com/orgs/fil-forge/packages/container/ingot/1385513652) What fil-forge/ingot brings over staging's pin, [`6071feb..431ad37`](fil-forge/ingot@6071feb...431ad37): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37))
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:e890d57`](https://github.com/orgs/fil-forge/packages/container/ingot/1385622164) What fil-forge/ingot brings over staging's pin, [`6071feb..8a9e399`](fil-forge/ingot@6071feb...8a9e399): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37)) - fil-forge/ingot#260 ([8a9e399](fil-forge/ingot@8a9e399))
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:24f1dfd`](https://github.com/orgs/fil-forge/packages/container/ingot/1385707121) What fil-forge/ingot brings over staging's pin, [`6071feb..7fab0fb`](fil-forge/ingot@6071feb...7fab0fb): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37)) - fil-forge/ingot#260 ([8a9e399](fil-forge/ingot@8a9e399)) - fil-forge/ingot#258 ([7fab0fb](fil-forge/ingot@7fab0fb))
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:aa89c75`](https://github.com/orgs/fil-forge/packages/container/ingot/1385968292) What fil-forge/ingot brings over staging's pin, [`6071feb..7b28323`](fil-forge/ingot@6071feb...7b28323): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37)) - fil-forge/ingot#260 ([8a9e399](fil-forge/ingot@8a9e399)) - fil-forge/ingot#258 ([7fab0fb](fil-forge/ingot@7fab0fb)) - fil-forge/ingot#257 ([8f9fc3f](fil-forge/ingot@8f9fc3f)) - fil-forge/ingot#261 ([7b28323](fil-forge/ingot@7b28323))
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:f629535`](https://github.com/orgs/fil-forge/packages/container/ingot/1386342616) What fil-forge/ingot brings over staging's pin, [`6071feb..6331454`](fil-forge/ingot@6071feb...6331454): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37)) - fil-forge/ingot#260 ([8a9e399](fil-forge/ingot@8a9e399)) - fil-forge/ingot#258 ([7fab0fb](fil-forge/ingot@7fab0fb)) - fil-forge/ingot#257 ([8f9fc3f](fil-forge/ingot@8f9fc3f)) - fil-forge/ingot#261 ([7b28323](fil-forge/ingot@7b28323)) - fil-forge/ingot#263 ([6331454](fil-forge/ingot@6331454))
The gateway renders a refused x-bucket-policy header with the code a refused PutBucketPolicy body gets, MalformedPolicy, from fil-forge/ingot#194 on; InvalidArgument stays accepted until that is on main. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sets every staging pin in `nodes/staging/eu-central-3/apps/versions.env` to what `nodes/dev/apps/versions.env` pins on `main`. Merging this is the promotion: the node picks it up on its next reconcile pass, waits for a safe proving window and restarts each service that changed. This pull request is rebuilt on every push to `main` and closed once staging matches dev, so edits to it are overwritten unless they are pushed to `bot/promote-staging` by hand. ## ingot [`sha256:4fdff18`](https://github.com/orgs/fil-forge/packages/container/ingot/1378629238) → [`sha256:f629535`](https://github.com/orgs/fil-forge/packages/container/ingot/1386342616) What fil-forge/ingot brings over staging's pin, [`6071feb..6331454`](fil-forge/ingot@6071feb...6331454): - fil-forge/ingot#177 ([66e9659](fil-forge/ingot@66e9659)) - fil-forge/ingot#178 ([5b3e0b6](fil-forge/ingot@5b3e0b6)) - fil-forge/ingot#194 ([1c47c14](fil-forge/ingot@1c47c14)) - fil-forge/ingot#259 ([431ad37](fil-forge/ingot@431ad37)) - fil-forge/ingot#260 ([8a9e399](fil-forge/ingot@8a9e399)) - fil-forge/ingot#258 ([7fab0fb](fil-forge/ingot@7fab0fb)) - fil-forge/ingot#257 ([8f9fc3f](fil-forge/ingot@8f9fc3f)) - fil-forge/ingot#261 ([7b28323](fil-forge/ingot@7b28323)) - fil-forge/ingot#263 ([6331454](fil-forge/ingot@6331454)) Co-authored-by: fil-forge-bot[bot] <318653112+fil-forge-bot[bot]@users.noreply.github.com>
The gateway renders a refused x-bucket-policy header with the code a refused PutBucketPolicy body gets, MalformedPolicy, from fil-forge/ingot#194 on; InvalidArgument stays accepted until that is on main. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
<!-- rfc-preview-link:start --> [📖 Preview](https://github.com/fil-one/RFC/blob/srdjan%2Frfc-aws-policy-document/rfcs/2026-09-forge-s3-tenant-iam.md) <!-- rfc-preview-link:end --> The Forge bucket policy becomes an AWS bucket policy document, so AWS tooling can build and check it and Ingot can serve the policy operations through versitygw's own S3 handlers instead of answering them ahead of versitygw's route table. The changes in the tenant IAM RFC: - The document gains `Version` and `Resource`. Principals are written `arn:aws:iam:::user/<principalId>` with an empty account field, because the bucket fixes the tenant. `{"AWS": "*"}` is a second spelling of `"*"`. - `Resource` names the whole bucket: the bucket ARN for `s3:ListBucket*` and the `/*` ARN for object actions. Forge grants authority per bucket, so key prefixes are refused. - The refusal list now covers `Version`, `Resource`, ARN shape, `Condition` and `NotPrincipal`. - The canonical form and the ETag cover `Version` and `Resource`. Compare-and-set is unchanged. - The Ingot section says the operations go through versitygw's handlers and that versitygw's own validator runs before Hilt's. - The default Owner and Admin statements are gone. The console writes one statement naming a bucket's creator under an ordinary label, a role change touches no policy, the migration creates principals only, and the orchestrator serving a region fixes its access model. ## References - #30, the RFC this amends. - fil-forge/ingot#194, the review thread that asked for AWS conformance. - fil-forge/hilt#96, Hilt's implementation. - fil-one/fil-one#696, the bucket access ADR. Its document shape is updated on the console stack. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…cket (#742) * feat(backend): send the bucket's first policy in x-bucket-policy on create On a region serving the `iam` access model, the create request carries the bucket's first policy in the signed `x-bucket-policy` header, as the tenant IAM RFC specifies: the storage system validates the document, creates the bucket and stores the policy together, so no bucket outlives a failed policy write. The header is added by a `build`-step middleware on the one command, so SigV4 signs it and nothing else the client sends carries it. The document names the org's Owners with every action, and its Admins and the creator with every action but the two retention writes. A refused document answers 400 with the storage system's message; no bucket exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(backend): a refused create-time policy arrives as MalformedPolicy The gateway renders a refused x-bucket-policy header with the code a refused PutBucketPolicy body gets, MalformedPolicy, from fil-forge/ingot#194 on; InvalidArgument stays accepted until that is on main. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(backend): a create-time policy is audited as created when configuration fails The create stores the bucket and its policy together; versioning and retention setup run after it. A BucketConfigurationError therefore means the policy exists, so the bucket_policy.created completion records it as succeeded. The 500 response stays. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(backend): name the create-time policy helper getOrgDefaultPolicy firstPolicyFor read as "the first of several"; the helper builds the org's default policy for a new bucket, so it is named for that, as the review asked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(backend): name the create-time audit helper auditDefaultBucketPolicy It records the default policy the console writes with a new bucket, and its name now says so, matching getOrgDefaultPolicy beside it, as the review asked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(backend): count a create-time policy's statements by their AWS name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(backend): write a new bucket's first policy after the create RFC#30 drops the x-bucket-policy header. The console creates the bucket, then writes the default policy with PutBucketPolicy and If-None-Match: * before the versioning and object-lock calls, and answers the member after both. A refused document still answers 400; any other failure of the write answers 500. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(backend): name the first policy write and its tests by what they do Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(backend): write the first bucket policy through the shared write retry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(backend): write a bucket's first policy from the one Forge orchestrator Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(backend): start a new bucket's policy with its creator alone The first policy names the member who created the bucket and nobody else, whatever their role. No member list is read on create. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * test(backend): name members and principals by UUID in the bucket creation tests Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(backend): inline the first-policy helpers The create-bucket handler picks the creator statement where the region serves policies, and createBucket writes it under If-None-Match: * with the usual retry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Serves
GET,PUTandDELETE /{bucket}?policythrough versitygw's own route table, auth and controllers. ingot's backend forwards each operation to Hilt's/s3/bucket/policy. The policy document is now an AWS bucket policy (fil-forge/hilt#96), so versitygw'sPutBucketPolicyvalidator accepts it and rejects anything that isn't AWS-shaped before Hilt sees it. Hilt's stricter checks still decide.s3frontend/policy.go:PutBucketPolicy,GetBucketPolicyandDeleteBucketPolicyforward the signed request fromreqscope.GetBucketPolicyforwards only a signedGET ?policy. versitygw's own access checks getNoSuchBucketPolicy, so they never reach Hilt or evaluate a policy, and an anonymous request isAccessDenied.server.go: apolicyHeadersmiddleware sets theETagHilt returned andapplication/jsonon aGET, because the backend interface returns no headers. Preconditions reach Hilt in the signed request.iam:GetUserAccountresolvesarn:aws:iam:::user/<id>, so versitygw's validator accepts ARN principals. Hilt checks that the principal exists.policy_routes.goand its test are deleted.itest/forge_bucket_policy_test.gosends AWS-shaped documents and covers a bare-name principal (refused by versitygw) and a missingVersion(refused by Hilt).A policy request now reaches Hilt twice: versitygw's auth calls
/s3/request/authorize, then the backend calls/s3/bucket/policy.Change log
chore: temporary pin of hilt to feat(bucketpolicy): make the bucket policy an AWS bucket policy document hilt#96 (b2da449), to be re-pinned to main before merge. libforge stays pinned to feat(s3): add the /s3/bucket/policy command libforge#80.feat(iam): resolve user ARNs for versitygw's policy validation.feat(s3frontend): serve the bucket policy operations through the backend.refactor(s3): serve?policythrough versitygw's controllers; delete the route intercept.test(itest): drive the bucket policy scenario with AWS-shaped documents.e2e: the
policies-localsuite passed against a fresh smelt running hilt#96 and ingot#194, with the console from fil-one/fil-one#812 (102 of 103 passed; PU3 was flaky on its first attempt while the fresh Owner org was still unnamed).References
🤖 Generated with Claude Code