diff --git a/README.md b/README.md index 0ccd5a7..0b276a7 100644 --- a/README.md +++ b/README.md @@ -488,7 +488,7 @@ issue-type change before the first mutation. ## Metaserver development credentials The `development` environment of `atrinik/metaserver-worker` (repository ID -1324297032) holds the three value-free lifecycle entries consumed only by +1324297032) holds the two value-free lifecycle entries consumed only by `.github/workflows/deploy-development.yml`. Zoey Seraphina Rose, an Atrinik organization owner, is accountable for verification and rotation. @@ -496,11 +496,10 @@ organization owner, is accountable for verification and rotation. D1 Read permissions. Its account-wide edit authority can also edit PROD; the workflow's exact DEV configuration and resource fences provide the target boundary. `ATRINIK_DELIVERY_RESOURCE_READ_TOKEN` is account-owned with intended -Workers Scripts Read. `ATRINIK_PRODUCTION_BUILDS_READ_TOKEN` is user-scoped -with intended Workers CI Read and Workers Scripts Read for the retained-writer -fence. Do not describe any of these credentials as restricted to one Worker. +Workers Scripts Read. Do not describe either credential as restricted to one +Worker. -All three environment names were installed and verified on 2026-10-11; bounded +Both retained credential names were installed and verified on 2026-10-11; bounded runtime GET checks passed. Effective provider permission metadata is unreadable, and the deployment token's write permission has not yet been proven by its owning workflow. Name metadata does not verify values, scope or expiry. @@ -1190,7 +1189,7 @@ The names-only `production` environment inventory declares the reviewed production-promotion contract: protected branches enabled, custom policies disabled, no custom patterns and no required reviewers. Solo owner dispatch of the exact reviewed inputs remains the approval boundary. -The eight secret names and two variable names are desired state, not proof that +The seven secret names and one variable name are desired state, not proof that the environment or effective credentials exist. The recovery public-key input remains pending the owning workflow merge; provision it only after that reviewed consumer is merged. Provision through supported @@ -1246,9 +1245,9 @@ database identity. Required `Workers Scripts Edit` and `D1 Read` permissions remain the intended deployment contract; provider permission metadata and write capability are unproven. Provider creation and expiry remain unknown. Its 2027-01-10 rotation deadline is internal 90-day policy, not provider expiry. -The two owner-authorized shared read-only control-plane credentials retain their -2026-10-11 qualification and 2027-01-09 internal deadlines; expiry remains unknown. -Their separate production lifecycle entries declare desired placement, not +The owner-authorized shared `ATRINIK_DELIVERY_RESOURCE_READ_TOKEN` retains its +2026-10-11 qualification and 2027-01-09 internal deadline; expiry remains unknown. +Its separate production lifecycle entry declares desired placement, not installation or successful use by the production workflow. Provision and verify only after this inventory is reviewed and accepted. @@ -1487,14 +1486,19 @@ environment. The environment permits only the exact `main` branch and has no required reviewers, secrets, or variables. The desired Pages build type is `workflow`; the consuming workflow is `.github/workflows/daily-client-performance.yml`, bound by the immutable -`actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e` marker. +`actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346` marker. -The live legacy `main`-root source is a bounded pre-activation state only while +A legacy `main`-root source is a bounded pre-activation state only while that marker is absent from the default-branch workflow. `bin/verify-manual-settings` reports it as `PENDING`. Once the reviewed Classic workflow is merged, the same verifier requires the Actions source and fails on any lingering legacy source. It never changes the source itself. +Classic PR #547 merged the v5.0.1 deployment action update on 2026-10-03. +The inventory marker follows that reviewed immutable action. Read-only metadata +on 2026-10-31 showed the site already uses the Actions source; this verifies +configuration, not successful execution of the latest Pages deployment. + Before activation, verify the repository, current Pages site, environment, and the merged workflow without changing live state: @@ -1513,7 +1517,7 @@ gh api repos/atrinik/classic/environments/github-pages/variables \ --jq '{total_count,names:[.variables[].name]}' gh api 'repos/atrinik/classic/contents/.github/workflows/daily-client-performance.yml?ref=main' \ --jq -r .content | base64 --decode | \ - grep -F 'actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e' + grep -F 'actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346' ``` Require the stable repository identity and `main` default branch, exact site diff --git a/bin/validate b/bin/validate index a45ab4f..4d885e6 100755 --- a/bin/validate +++ b/bin/validate @@ -828,9 +828,6 @@ jq -e \ elif .secret_name == "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN" then .credential_type == "cloudflare_account_token" and .required_scopes == ["Workers Scripts Read"] - elif .secret_name == "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN" then - .credential_type == "cloudflare_user_token" and - .required_scopes == ["Workers CI Read", "Workers Scripts Read"] else false end) end) ) and diff --git a/config/manual-settings.json b/config/manual-settings.json index e1f02ff..be15e6b 100644 --- a/config/manual-settings.json +++ b/config/manual-settings.json @@ -313,13 +313,10 @@ "ATRINIK_DEVELOPMENT_CORE_CONFIG", "ATRINIK_DEVELOPMENT_PUBLISHER_CONFIG", "ATRINIK_DEVELOPMENT_RENDEZVOUS_CONFIG", - "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN" ], - "variable_names": [ - "ATRINIK_PRODUCTION_WRITER_FENCE_POLICY" - ] + "variable_names": [] }, { "deployment_branch_policy": { @@ -334,7 +331,6 @@ "secret_names": [ "ATRINIK_DELIVERY_RESOURCE_POLICY", "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", - "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "ATRINIK_PRODUCTION_CORE_CONFIG", "ATRINIK_PRODUCTION_PUBLISHER_CONFIG", "ATRINIK_PRODUCTION_RENDEZVOUS_CONFIG", @@ -342,14 +338,13 @@ "CLOUDFLARE_API_TOKEN" ], "variable_names": [ - "ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY", - "ATRINIK_PRODUCTION_WRITER_FENCE_POLICY" + "ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY" ] } ], "github_pages_sites": [ { - "activation_marker": "actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e", + "activation_marker": "actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346", "build_type": "workflow", "environment": "github-pages", "https_enforced": true, @@ -459,28 +454,6 @@ "secret_name": "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "secret_scope": "environment" }, - { - "credential_type": "cloudflare_user_token", - "consumers": [ - ".github/workflows/deploy-development.yml" - ], - "environment": "development", - "last_verified_on": "2026-10-11", - "owner": "Zoey Seraphina Rose (Atrinik organization owner)", - "purpose": "Read retained Builds and Worker settings to prove the stopped-writer fence without deployment.", - "repository": "atrinik/metaserver-worker", - "repository_id": 1324297032, - "required_scopes": [ - "Workers CI Read", - "Workers Scripts Read" - ], - "rotate_by": "2027-01-09", - "rotation_cadence_days": 90, - "rotation_owner": "Zoey Seraphina Rose (Atrinik organization owner)", - "runbook": "README.md#metaserver-development-credentials", - "secret_name": "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", - "secret_scope": "environment" - }, { "consumers": [ ".github/workflows/promote-production.yml" @@ -523,28 +496,6 @@ "runbook": "README.md#metaserver-production-credentials", "secret_name": "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "secret_scope": "environment" - }, - { - "consumers": [ - ".github/workflows/promote-production.yml" - ], - "credential_type": "cloudflare_user_token", - "environment": "production", - "last_verified_on": "2026-10-11", - "owner": "Zoey Seraphina Rose (Atrinik organization owner)", - "purpose": "Read retained Builds and Worker settings to prove the stopped-writer fence without deployment. Reuse in the production control-plane workflow is owner-authorized; this record declares production placement, which remains pending. Provider expiry is unknown and rotate_by is internal policy.", - "repository": "atrinik/metaserver-worker", - "repository_id": 1324297032, - "required_scopes": [ - "Workers CI Read", - "Workers Scripts Read" - ], - "rotate_by": "2027-01-09", - "rotation_cadence_days": 90, - "rotation_owner": "Zoey Seraphina Rose (Atrinik organization owner)", - "runbook": "README.md#metaserver-production-credentials", - "secret_name": "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", - "secret_scope": "environment" } ], "github_packages_actions_access": [ diff --git a/tests/validate-manual-credentials.sh b/tests/validate-manual-credentials.sh index 8d4d545..db660c2 100755 --- a/tests/validate-manual-credentials.sh +++ b/tests/validate-manual-credentials.sh @@ -198,7 +198,7 @@ jq -e ' jq -e ' [.github_actions_credentials[] | select(.environment == "development")] as $credentials | - ($credentials | length) == 3 and + ($credentials | length) == 2 and all($credentials[]; .repository == "atrinik/metaserver-worker" and .repository_id == 1324297032 and .environment == "development" and .consumers == [".github/workflows/deploy-development.yml"] and @@ -206,13 +206,13 @@ jq -e ' .rotation_cadence_days == 90 and .owner == "Zoey Seraphina Rose (Atrinik organization owner)" ) and ([$credentials[].secret_name] | sort) == [ - "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "CLOUDFLARE_API_TOKEN" + "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "CLOUDFLARE_API_TOKEN" ] ' "${root}/config/manual-settings.json" >/dev/null jq -e ' [.github_actions_credentials[] | select(.environment == "production")] as $credentials | - ($credentials | length) == 3 and + ($credentials | length) == 2 and all($credentials[]; .repository == "atrinik/metaserver-worker" and .repository_id == 1324297032 and .consumers == [".github/workflows/promote-production.yml"] and @@ -223,7 +223,7 @@ jq -e ' else .last_verified_on == "2026-10-11" and .rotate_by == "2027-01-09" end) ) and ([$credentials[].secret_name] | sort) == [ - "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "CLOUDFLARE_API_TOKEN" + "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", "CLOUDFLARE_API_TOKEN" ] ' "${root}/config/manual-settings.json" >/dev/null @@ -244,7 +244,8 @@ for invalid in \ '.github_actions_credentials[-1].consumers = [".github/workflows/deploy-development.yml"]' \ '.github_actions_credentials[-1].runbook = "README.md#metaserver-development-credentials"' \ '.github_actions_credentials[-1].required_scopes += ["Workers Scripts Edit"]' \ - '.github_actions_credentials[-1].credential_type = "cloudflare_account_token"'; do + '.github_actions_credentials[-1].credential_type = "cloudflare_user_token"' \ + '.github_actions_credentials[-1] |= (.secret_name = "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN" | .credential_type = "cloudflare_user_token" | .required_scopes = ["Workers CI Read", "Workers Scripts Read"])'; do jq "$invalid" "${temporary}/production-credentials.json" >"${temporary}/config/manual-settings.json" if ATRINIK_VALIDATION_TODAY=2026-10-18 "${temporary}/bin/validate" >/dev/null 2>&1; then echo "error: validator accepted malformed PROD credential lifecycle: ${invalid}" >&2 @@ -255,7 +256,7 @@ done for invalid in \ '.github_actions_credentials[1].required_scopes = ["Workers Scripts Read"]' \ '.github_actions_credentials[2].credential_type = "cloudflare_user_token"' \ - '.github_actions_credentials[3].environment = "production"' \ + '(.github_actions_credentials[] | select(.environment == "development" and .secret_name == "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN")).environment = "production"' \ '.github_actions_credentials[1].repository_id = 1' \ '.github_actions_credentials[1].consumers = [".github/workflows/publish.yml"]' \ '.github_actions_credentials[1].credential_value = "secret"'; do diff --git a/tests/validate-manual-environments.sh b/tests/validate-manual-environments.sh index a41a0e4..75e3d8f 100755 --- a/tests/validate-manual-environments.sh +++ b/tests/validate-manual-environments.sh @@ -103,11 +103,10 @@ jq -e ' "ATRINIK_DEVELOPMENT_CORE_CONFIG", "ATRINIK_DEVELOPMENT_PUBLISHER_CONFIG", "ATRINIK_DEVELOPMENT_RENDEZVOUS_CONFIG", - "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN" ], - variable_names: ["ATRINIK_PRODUCTION_WRITER_FENCE_POLICY"] + variable_names: [] }) and any(.github_actions_environments[]; . == { deployment_branch_policy: {custom_branch_policies: false, patterns: [], protected_branches: true}, @@ -117,14 +116,14 @@ jq -e ' required_reviewers: [], secret_names: [ "ATRINIK_DELIVERY_RESOURCE_POLICY", "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN", - "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN", "ATRINIK_PRODUCTION_CORE_CONFIG", + "ATRINIK_PRODUCTION_CORE_CONFIG", "ATRINIK_PRODUCTION_PUBLISHER_CONFIG", "ATRINIK_PRODUCTION_RENDEZVOUS_CONFIG", "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN" ], - variable_names: ["ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY", "ATRINIK_PRODUCTION_WRITER_FENCE_POLICY"] + variable_names: ["ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY"] }) and .github_pages_sites == [{ - activation_marker: "actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e", + activation_marker: "actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346", build_type: "workflow", environment: "github-pages", https_enforced: true, diff --git a/tests/verify-manual-settings.sh b/tests/verify-manual-settings.sh index 1411aa6..54516ec 100755 --- a/tests/verify-manual-settings.sh +++ b/tests/verify-manual-settings.sh @@ -312,7 +312,7 @@ repos/atrinik/classic/pages) workflow='name: Daily Classic client performance' if [[ ${FAKE_GH_SCENARIO} == pages-active || ${FAKE_GH_SCENARIO} == pages-activation-missing ]]; then - workflow='uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e' + workflow='uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346' fi if [[ ${FAKE_GH_SCENARIO} == pages-workflow-metadata-drift ]]; then jq -n '{type: "dir", encoding: "none", content: null}' @@ -619,17 +619,16 @@ repos/atrinik/metaserver-worker/environments/production) exit 79 ;; "repos/atrinik/metaserver-worker/environments/production/secrets?per_page=100&page=1") - jq -n '{total_count: 8, secrets: [ + jq -n '{total_count: 7, secrets: [ {name: "ATRINIK_DELIVERY_RESOURCE_POLICY"}, {name: "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN"}, - {name: "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN"}, {name: "ATRINIK_PRODUCTION_CORE_CONFIG"}, + {name: "ATRINIK_PRODUCTION_CORE_CONFIG"}, {name: "ATRINIK_PRODUCTION_PUBLISHER_CONFIG"}, {name: "ATRINIK_PRODUCTION_RENDEZVOUS_CONFIG"}, {name: "CLOUDFLARE_ACCOUNT_ID"}, {name: "CLOUDFLARE_API_TOKEN"} ]}' ;; "repos/atrinik/metaserver-worker/environments/production/variables?per_page=100&page=1") - jq -n '{total_count: 2, variables: [ - {name: "ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY"}, - {name: "ATRINIK_PRODUCTION_WRITER_FENCE_POLICY"} + jq -n '{total_count: 1, variables: [ + {name: "ATRINIK_PRODUCTION_RECOVERY_PUBLIC_KEY"} ]}' ;; repos/atrinik/metaserver-worker/environments/development) @@ -652,19 +651,18 @@ repos/atrinik/metaserver-worker/environments/development) jq -n '{total_count: 0, secrets: []}' exit 0 fi - jq -n '{total_count: 8, secrets: [ + jq -n '{total_count: 7, secrets: [ {name: "ATRINIK_DELIVERY_RESOURCE_POLICY"}, {name: "ATRINIK_DELIVERY_RESOURCE_READ_TOKEN"}, {name: "ATRINIK_DEVELOPMENT_CORE_CONFIG"}, {name: "ATRINIK_DEVELOPMENT_PUBLISHER_CONFIG"}, {name: "ATRINIK_DEVELOPMENT_RENDEZVOUS_CONFIG"}, - {name: "ATRINIK_PRODUCTION_BUILDS_READ_TOKEN"}, {name: "CLOUDFLARE_ACCOUNT_ID"}, {name: "CLOUDFLARE_API_TOKEN"} ] | map(. + {updated_at: "2026-10-11T00:00:00Z"})}' ;; "repos/atrinik/metaserver-worker/environments/development/variables?per_page=100&page=1") - jq -n '{total_count: 1, variables: [{name: "ATRINIK_PRODUCTION_WRITER_FENCE_POLICY"}]}' + jq -n '{total_count: 0, variables: []}' ;; repos/atrinik/classic/environments/discord-release) if [[ ${FAKE_GH_SCENARIO} == missing-environment ]]; then