feat: D19 bounds the review loop; apply nine rulings; close Q46(a) and Q48–Q53
D19 into §3 and swept to all six destinations Pouya named: both agent briefs
(scope + severity filter), /build Phases 2–4 (non-gating questions, scope, stop
signal, two-round cap), /wrap, and CLAUDE.md (comments record decisions, not
history). Sweep verified by command, not recalled.
Rulings applied:
Q46(a) PUBLISHABLE — three §4 Offerings rows flipped; gate 1 records
[Pouya's stated basis], never [verified]. Step 5 unblocked.
Q48 closed, not site-relevant.
Q49 one §4 row, "Mediator"; read as declining Q49(b), so worksFor stays out.
Q50 DEVIATES — ships as name + slogan, not the concatenation. Flagged.
Q51 OBA sections stay; the regulator/voluntary distinction recorded.
Q52 docs/reference/deploy-credential-verification.md — 18 read-only AWS
calls, re-run rather than transcribed, access key ID redacted.
Q53 memberOf emitted on /about/'s Person node.
Two review rounds. The headline finding was this session's own: the Q53 sweep
was asserted and never run, leaving six in-scope records saying memberOf was
withheld — including §12 R10, which is read aloud every session. Round 2 then
found that round 1's simplification had put memberOf on / as well; the per-page
opt-in is restored, because Pouya's ruling turns on /about/'s visible HTML.
Also fixed: MEMBERSHIP_ORGS had orphaned BOUTIQUE's D16 JSDoc; /'s title now
derives from the constants; §7's deploy row stated and retracted three facts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148NztQskLKKApP5SzAA78e
This commit is contained in:
co-authored by
Claude Opus 5
parent
77a7b410b2
commit
fd5f610982
@@ -0,0 +1,559 @@
|
||||
# Deploy credential — verification output
|
||||
|
||||
**What this is.** The tool output behind `AGENTS.md` §7's *Deploy credential —
|
||||
PROVISIONED* row and the two risk downgrades in `AGENTS.md` §10. It exists because the row
|
||||
was written on evidence that lived only in a terminal, which is the
|
||||
**Q24 / Q32 shape** R14 exists for: a claim whose supporting artefact is
|
||||
unreachable is unverifiable by construction, not merely unverified.
|
||||
`AGENTS.md` **Q52**, ruled 2026-08-28: *"YES — commit the simulate results, the
|
||||
inline policy, and the `NoSuchBucketPolicy` response, access key ID redacted."*
|
||||
|
||||
## Provenance
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Subject | IAM user `adr-sml-deploy`, AWS account `327082975128` |
|
||||
| Retrieved | **2026-08-28**, re-run from the repository root |
|
||||
| Calling identity | `arn:aws:iam::327082975128:user/pouya` — the broadly-permissioned personal user, **at an interactive keyboard**. `AGENTS.md` §7 and §10: acceptable interactively, **never** as a CI credential |
|
||||
| Calls | 18 — all **read-only**. No `create`, `put`, `attach`, `delete`, or `update` |
|
||||
| Redaction | The access key ID is replaced with `AKIA…REDACTED` throughout. Nothing else is redacted; `UserId` (`AIDA…`) is retained because it is the durable principal identifier that appears in CloudTrail and grants nothing. The **secret** access key was never requested and cannot be retrieved from the API at all |
|
||||
| Method | `aws-cli/2.34.53`. Every command's exit status was read, and **no `2>/dev/null` anywhere** — see the `NoSuchBucketPolicy` section, where the error *is* the result |
|
||||
|
||||
**Re-run it.** Every command below is copy-pasteable. Nothing here is
|
||||
transcribed from a session; this file was generated from the captured output.
|
||||
|
||||
---
|
||||
|
||||
## 1. The user
|
||||
|
||||
```console
|
||||
$ aws iam get-user --user-name adr-sml-deploy
|
||||
{
|
||||
"User": {
|
||||
"Path": "/",
|
||||
"UserName": "adr-sml-deploy",
|
||||
"UserId": "AIDAUYJ5KD6MHAAC4BOKD",
|
||||
"Arn": "arn:aws:iam::327082975128:user/adr-sml-deploy",
|
||||
"CreateDate": "2026-08-26T15:45:18+00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Created **2026-08-26T15:45:18+00:00**, path `/`, no tags.
|
||||
|
||||
## 2. Nothing attached, no groups, one inline policy
|
||||
|
||||
```console
|
||||
$ aws iam list-attached-user-policies --user-name adr-sml-deploy
|
||||
{
|
||||
"AttachedPolicies": []
|
||||
}
|
||||
|
||||
$ aws iam list-groups-for-user --user-name adr-sml-deploy
|
||||
{
|
||||
"Groups": []
|
||||
}
|
||||
|
||||
$ aws iam list-user-policies --user-name adr-sml-deploy
|
||||
{
|
||||
"PolicyNames": [
|
||||
"adr-sml-deploy-minimal"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**This is the part policy-reading alone gets wrong.** An empty
|
||||
`AttachedPolicies` proves nothing on its own — permissions can arrive through a
|
||||
group, or through a resource-based policy that is invisible from the IAM side.
|
||||
Both are checked: `Groups` is empty here, and **§6 below** checks the resource
|
||||
side — §5 is the identity-side simulation and does not reach a bucket policy.
|
||||
|
||||
## 3. The inline policy — `adr-sml-deploy-minimal`
|
||||
|
||||
```console
|
||||
$ aws iam get-user-policy --user-name adr-sml-deploy \
|
||||
--policy-name adr-sml-deploy-minimal
|
||||
{
|
||||
"UserName": "adr-sml-deploy",
|
||||
"PolicyName": "adr-sml-deploy-minimal",
|
||||
"PolicyDocument": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ListSiteBucket",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": "arn:aws:s3:::adr-smlcompany-site"
|
||||
},
|
||||
{
|
||||
"Sid": "WriteSiteObjects",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:PutObject",
|
||||
"s3:DeleteObject"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::adr-smlcompany-site/*"
|
||||
},
|
||||
{
|
||||
"Sid": "InvalidateOneDistribution",
|
||||
"Effect": "Allow",
|
||||
"Action": "cloudfront:CreateInvalidation",
|
||||
"Resource": "arn:aws:cloudfront::327082975128:distribution/E1OK7G98KNKUTA"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Four actions, one bucket, one distribution. **Never widen it** — `AGENTS.md` §10
|
||||
records why
|
||||
in terms: this is the control standing between a shared Gitea instance and an
|
||||
AWS account that also holds `mlp-clientdb-prod-backups-327082975128`. Treat any
|
||||
request to widen it as a security decision, not a convenience one.
|
||||
|
||||
## 4. The access key — issued, never used
|
||||
|
||||
```console
|
||||
$ aws iam list-access-keys --user-name adr-sml-deploy
|
||||
{
|
||||
"AccessKeyMetadata": [
|
||||
{
|
||||
"UserName": "adr-sml-deploy",
|
||||
"AccessKeyId": "AKIA…REDACTED",
|
||||
"Status": "Active",
|
||||
"CreateDate": "2026-08-26T15:45:19+00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ aws iam get-access-key-last-used --access-key-id AKIA…REDACTED
|
||||
{
|
||||
"UserName": "adr-sml-deploy",
|
||||
"AccessKeyLastUsed": {
|
||||
"ServiceName": "N/A",
|
||||
"Region": "N/A"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
One key, `Active`, created **2026-08-26T15:45:19+00:00**.
|
||||
|
||||
⚠️ **A correction to `AGENTS.md` §7's wording, found by re-running the call.**
|
||||
That row said
|
||||
*"one active access key, `LastUsed` null"*. The API does not return null — it
|
||||
returns `AccessKeyLastUsed` with **`ServiceName: "N/A"` and `Region: "N/A"`**,
|
||||
and there is no `LastUsedDate` key at all. Same conclusion (the key has never
|
||||
authenticated a request), different field shape, and a reader looking for a
|
||||
literal `null` would not find one.
|
||||
|
||||
**Rotation is due 2026-11-26** — key created 2026-08-26, D3 commits to
|
||||
quarterly. `AGENTS.md` §12 R17 carries the date; `docs/06-deployment.md` §Key rotation
|
||||
carries the procedure. Create the second key, update the Gitea secrets, verify a
|
||||
deploy, **then** delete the first.
|
||||
|
||||
## 5. `simulate-principal-policy` — nine checks, verified by execution
|
||||
|
||||
The point of this section is that it tests the **negative** half. A policy that
|
||||
grants four actions is only a control if it can be shown not to grant the rest.
|
||||
|
||||
```bash
|
||||
PRINCIPAL=arn:aws:iam::327082975128:user/adr-sml-deploy
|
||||
sim() { aws iam simulate-principal-policy \
|
||||
--policy-source-arn "$PRINCIPAL" --action-names "$1" --resource-arns "$2" ; }
|
||||
```
|
||||
|
||||
| # | Action | Resource | `EvalDecision` |
|
||||
|---|---|---|---|
|
||||
| 1 | `s3:PutObject` | `arn:aws:s3:::adr-smlcompany-site/index.html` | **allowed** |
|
||||
| 2 | `s3:DeleteObject` | `arn:aws:s3:::adr-smlcompany-site/old.html` | **allowed** |
|
||||
| 3 | `s3:ListBucket` | `arn:aws:s3:::adr-smlcompany-site` | **allowed** |
|
||||
| 4 | `cloudfront:CreateInvalidation` | `arn:aws:cloudfront::327082975128:distribution/E1OK7G98KNKUTA` | **allowed** |
|
||||
| 5 | `s3:ListBucket` | `arn:aws:s3:::mlp-clientdb-prod-backups-327082975128` | **implicitDeny** |
|
||||
| 6 | `s3:GetObject` | `arn:aws:s3:::mlp-clientdb-prod-backups-327082975128/anything` | **implicitDeny** |
|
||||
| 7 | `s3:DeleteBucket` | `arn:aws:s3:::adr-smlcompany-site` | **implicitDeny** |
|
||||
| 8 | `s3:PutObject` | `arn:aws:s3:::meshkinilaw.ca/anything` | **implicitDeny** |
|
||||
| 9 | `iam:CreateUser` | `*` | **implicitDeny** |
|
||||
|
||||
Four `allowed`, five `implicitDeny`. `implicitDeny` rather than `explicitDeny`
|
||||
is the correct and expected shape: the policy contains no `Deny` statement, so
|
||||
everything outside its three `Allow` statements falls through to IAM's
|
||||
default-deny. An `explicitDeny` here would mean a *different* policy was also in
|
||||
play.
|
||||
|
||||
Raw output for each check, including the full `MatchedStatements` and
|
||||
`MissingContextValues`, follows in **§7 of this file**. *(Bare `§n` here means a
|
||||
section of this document; references to the working file are written
|
||||
`AGENTS.md` §n.)*
|
||||
|
||||
## 6. The resource side — `NoSuchBucketPolicy`, and why the contrast call matters
|
||||
|
||||
IAM simulation covers identity-based policy. It does **not** cover a
|
||||
resource-based grant on the backup bucket, which would be invisible from the
|
||||
principal's side. So:
|
||||
|
||||
```console
|
||||
$ aws s3api get-bucket-policy --bucket mlp-clientdb-prod-backups-327082975128
|
||||
# exit status: 254
|
||||
# stdout: 0 bytes
|
||||
# stderr:
|
||||
# aws: [ERROR]: An error occurred (NoSuchBucketPolicy) when calling the GetBucketPolicy operation: The bucket policy does not exist
|
||||
```
|
||||
|
||||
**An error is the result here, and that is only legible because stderr was not
|
||||
suppressed.** `CLAUDE.md`'s convention, from Pouya's own 2026-08-28 correction:
|
||||
`2>/dev/null` converts *"it failed"* into *"it found nothing"*, and those are
|
||||
opposite results. The exit status is **254** and stdout is **empty** — read the
|
||||
status, not just stdout.
|
||||
|
||||
**And an empty result needs a working instrument.** The same command against the
|
||||
site bucket, to prove the call itself resolves a policy when one exists:
|
||||
|
||||
```console
|
||||
$ aws s3api get-bucket-policy --bucket adr-smlcompany-site
|
||||
{
|
||||
"Policy": "{\"Version\":\"2008-10-17\",\"Id\":\"PolicyForCloudFrontPrivateContent\",\"Statement\":[{\"Sid\":\"AllowCloudFrontServicePrincipal\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"cloudfront.amazonaws.com\"},\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::adr-smlcompany-site/*\",\"Condition\":{\"StringEquals\":{\"AWS:SourceArn\":\"arn:aws:cloudfront::327082975128:distribution/E1OK7G98KNKUTA\"}}}]}"
|
||||
}
|
||||
```
|
||||
|
||||
Exit **0**, a real policy returned — the CloudFront OAC grant, scoped to
|
||||
distribution `E1OK7G98KNKUTA`. So `NoSuchBucketPolicy` on the backup bucket is a
|
||||
**genuine absence**, not a command that failed to run. That contrast is the
|
||||
whole reason this call is in the file: without it, an empty result from a broken
|
||||
command looks exactly like an empty result from a bucket with no policy.
|
||||
|
||||
*(This contrast call was **not** part of the original Q22 verification. It was
|
||||
added on re-run, and it closes the gap that made the original evidence weaker
|
||||
than it read.)*
|
||||
|
||||
## 7. Raw `simulate-principal-policy` responses
|
||||
|
||||
### Check 1
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:PutObject",
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site/index.html",
|
||||
"EvalDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
}
|
||||
}
|
||||
],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site/index.html",
|
||||
"EvalResourceDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 2
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:DeleteObject",
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site/old.html",
|
||||
"EvalDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
}
|
||||
}
|
||||
],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site/old.html",
|
||||
"EvalResourceDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 3
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:ListBucket",
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site",
|
||||
"EvalDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 3,
|
||||
"Column": 17
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
}
|
||||
}
|
||||
],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site",
|
||||
"EvalResourceDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 3,
|
||||
"Column": 17
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 6,
|
||||
"Column": 55
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 4
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "cloudfront:CreateInvalidation",
|
||||
"EvalResourceName": "arn:aws:cloudfront::327082975128:distribution/E1OK7G98KNKUTA",
|
||||
"EvalDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 12,
|
||||
"Column": 83
|
||||
}
|
||||
}
|
||||
],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:cloudfront::327082975128:distribution/E1OK7G98KNKUTA",
|
||||
"EvalResourceDecision": "allowed",
|
||||
"MatchedStatements": [
|
||||
{
|
||||
"SourcePolicyId": "user_adr-sml-deploy_adr-sml-deploy-minimal",
|
||||
"SourcePolicyType": "IAM Policy",
|
||||
"StartPosition": {
|
||||
"Line": 9,
|
||||
"Column": 57
|
||||
},
|
||||
"EndPosition": {
|
||||
"Line": 12,
|
||||
"Column": 83
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 5
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:ListBucket",
|
||||
"EvalResourceName": "arn:aws:s3:::mlp-clientdb-prod-backups-327082975128",
|
||||
"EvalDecision": "implicitDeny",
|
||||
"MatchedStatements": [],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::mlp-clientdb-prod-backups-327082975128",
|
||||
"EvalResourceDecision": "implicitDeny"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 6
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:GetObject",
|
||||
"EvalResourceName": "arn:aws:s3:::mlp-clientdb-prod-backups-327082975128/anything",
|
||||
"EvalDecision": "implicitDeny",
|
||||
"MatchedStatements": [],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::mlp-clientdb-prod-backups-327082975128/anything",
|
||||
"EvalResourceDecision": "implicitDeny"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 7
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:DeleteBucket",
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site",
|
||||
"EvalDecision": "implicitDeny",
|
||||
"MatchedStatements": [],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::adr-smlcompany-site",
|
||||
"EvalResourceDecision": "implicitDeny"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 8
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "s3:PutObject",
|
||||
"EvalResourceName": "arn:aws:s3:::meshkinilaw.ca/anything",
|
||||
"EvalDecision": "implicitDeny",
|
||||
"MatchedStatements": [],
|
||||
"MissingContextValues": [],
|
||||
"EvalDecisionDetails": {},
|
||||
"ResourceSpecificResults": [
|
||||
{
|
||||
"EvalResourceName": "arn:aws:s3:::meshkinilaw.ca/anything",
|
||||
"EvalResourceDecision": "implicitDeny"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Check 9
|
||||
|
||||
```json
|
||||
{
|
||||
"EvaluationResults": [
|
||||
{
|
||||
"EvalActionName": "iam:CreateUser",
|
||||
"EvalResourceName": "*",
|
||||
"EvalDecision": "implicitDeny",
|
||||
"MatchedStatements": [],
|
||||
"MissingContextValues": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What this does and does not establish
|
||||
|
||||
**Establishes.** The user exists with the creation date `AGENTS.md` §7 records; it holds one
|
||||
inline policy and nothing else; the policy grants four actions on one bucket and
|
||||
one distribution; the four intended actions are `allowed`; five representative
|
||||
out-of-scope actions — including two against the client-database backup bucket —
|
||||
are denied; the backup bucket carries no resource-based policy that could grant
|
||||
around IAM; and one key exists that has never authenticated a request.
|
||||
|
||||
**Does not establish.** That the key works (it has never been used, and testing
|
||||
it would mean putting a real object in the bucket). That Gitea holds it —
|
||||
`AGENTS.md` §7 records **`GITEA ACTIONS SECRETS: UNSET`**, and Q23 is open on whether a runner
|
||||
exists at all. That the policy is sufficient for a deploy in practice: the
|
||||
`s3:AbortMultipartUpload` omission is covered by asset sizes rather than by a
|
||||
lifecycle rule, and `docs/06-deployment.md` records that with a revisit trigger.
|
||||
|
||||
**Not in scope of this file, and deliberately so.** The secret access key. It
|
||||
must never reach the repository — D3, and `AGENTS.md` §10 on the jointly administered Gitea
|
||||
instance, where an instance administrator can generally reach repository secrets
|
||||
or register a runner that receives them.
|
||||
Reference in New Issue
Block a user