Skip to content

feat: accept pull request uploads for opted-in namespaces, starting with void - #17

Merged
wan9chi merged 1 commit into
mainfrom
feat/void-pull-request-uploads
Oct 19, 2026
Merged

wan9chi merged 1 commit into
mainfrom
feat/void-pull-request-uploads

Conversation

@wan9chi

@wan9chi wan9chi commented Oct 19, 2026

Copy link
Copy Markdown
Member

Motivation

voidzero-dev/void#492 caches every task build:all runs, and its PR CI should show remote cache reuse before it merges. void#492 already makes same-repository PR runs read-write, but the production cache only accepts uploads from default-branch push jobs, so those PR uploads get 403 write_policy. The decision is to let void's same-repository PR CI upload to its namespace (https://voidzero-remote-cache.voidzero-docs.workers.dev/projects/void) for now. Every other namespace keeps the main-branch-push-only policy.

Design

  • Opt-in per namespace, off by default. Migration 0002_pull_requests.sql adds scopes.pull_requests (0/1, default 0) and recreates the changed_policy trigger with that column, so turning the opt-in on or off bumps policy_version and stops uploads admitted under the old policy.

  • Write policy (src/auth.ts). push tokens work as before. For an opted-in namespace, a pull_request token passes the same audience, repository_id, repository_owner_id, and repository_visibility: public checks, and also needs:

    • ref matching refs/pull/<number>/merge
    • ref_type branch
    • base_ref equal to the registered default branch's short name (main, not refs/heads/main)

    These are the values GitHub signs for a PR's merge-branch run: see the ref description ("for pull request events except pull_request_target that were not merged, it is refs/pull/<pr_number>/merge"), GITHUB_BASE_REF ("for example, main"), the OIDC claim reference, and the decoded pull_request token in github/actions-oidc-debugger ("ref": "refs/pull/27/merge", "ref_type": "branch", "base_ref": "main"). pull_request_target, workflow_run, tags, PRs to other branches, the closed run of a merged PR (whose ref is the base branch), and workflows in fork repositories are still denied.

  • Configuration. A .github/production-repositories.jsonc entry can now be { "repository": "owner/repo", "pullRequests": true } as well as "owner/repo", which means pullRequests: false. Only void uses the object form. pnpm ci:deploy production sets every namespace's opt-in from the list on each run, the same way it already handles enabled/writes_enabled. Self-hosters use pnpm operator policy --pull-requests on|off, or setup --pull-requests on when creating a namespace. status already prints the full row.

  • Staging keeps pull_requests = 0, so PR staging runs still check that PR tokens are rejected.

  • Logging. Verified writes now also log event_name and head_ref. base_ref always equals the default branch, and the PR number is already in workflow_ref.

Risk

PR entries share the namespace with default-branch entries. Code from any same-repository PR, including Renovate dependency branches, runs before review and can publish an entry that a later default-branch build restores. That's why the opt-in is off by default and only on for void. The README, self-hosting guide, e2e plan, and RFC describe it as interim and link the long-term fix, #16 (scope pull request uploads to the pull request).

The server can't tell fork PRs from same-repository PRs: fork PR runs carry the base repository's IDs. It relies on GitHub not granting id-token: write to fork PR workflows. GitHub only does that when "Send write tokens to workflows from pull requests" is enabled, a setting it offers for private repositories only. The docs state this dependency.

After merge

Production changes only after a maintainer runs Actions → Remote cache production on main. That run applies migration 0002 and enables PR uploads for void. Its log should print Enabled pull request uploads for namespace void.

🤖 Generated with Claude Code

…ith void

A namespace can now opt in to GitHub Actions OIDC tokens from same-repository
`pull_request` runs whose PR targets its default branch. The opt-in is off by
default and enabled in production only for `void`, whose PR CI wants to show
remote cache reuse before merging.

An opted-in namespace keeps the audience, repository/owner ID, and public
visibility checks, and requires `ref` `refs/pull/<number>/merge`, `ref_type`
`branch`, and `base_ref` equal to the registered default branch.
`pull_request_target`, `workflow_run`, tags, other base branches, and fork
repositories stay denied.

The opt-in is a `pull_requests` column on `scopes` (migration 0002), part of
the `changed_policy` trigger so withdrawing it stops admitted uploads. It is set
by `pnpm operator setup/policy --pull-requests`, by production deployments from
the object form of `.github/production-repositories.jsonc`, and kept off in
staging so PR runs still check that PR tokens are rejected.

This is interim: same-repository PR code, including Renovate branches, can
publish entries that default-branch builds restore. #16 tracks scoping PR
uploads to the PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Remote cache staging

Commit: bd970c6aaeb8ce737f555b0d58619438de08f127

Cloudflare deployment is disabled. Configure the repository secrets and variables described in the e2e plan, then set REMOTE_CACHE_DEPLOY_ENABLED=true and rerun this workflow. No live deployment passed verification.

Manual checks and complete e2e plan.

@wan9chi
wan9chi merged commit 4e92504 into main Oct 19, 2026
8 checks passed
@wan9chi
wan9chi deleted the feat/void-pull-request-uploads branch October 19, 2026 07:32
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