Skip to content

Self-hosted CI is a bridge, not the target state — and it must not survive the public flip unmitigated #452

Description

@KenTaniguchi-R

State as of filing

macmini-agentcall is registered and online (labels self-hosted, macOS, ARM64), but no job targets it. Every workflow on main still declares a hosted runner, and ci.yml / invariants.yml are workflow_dispatch: only, so nothing fires on its own either.

Workflow runs-on Triggers
ci.yml:19 verify ubuntu-latest workflow_dispatch
ci.yml:75 packed-cli-consumer ${{ matrix.os }}[macos-latest, ubuntu-latest] workflow_dispatch
ci.yml:117 windows-compat windows-2025 workflow_dispatch
invariants.yml:23 ubuntu-latest workflow_dispatch
release.yml ×3 ubuntu-latest release: published

Registering a runner is half the change. A runner with no job targeting it never picks up work.

The decision

Superseded 2026-08-28 — see "Blocking precondition on going public" below.
The runner-topology decision below (verify stays self-hosted for regular
CI) still stands; what changed is the public-repo precondition, not this.

Self-hosted is a bridge while Actions billing is unavailable. Hosted runners are the target state for anything a pull request can trigger. Moving permanently to one Mac mini converts a three-platform gate into a one-platform gate, and — once this repository is public — turns every forked PR into arbitrary code execution on a personal machine.

What to change, and what deliberately not to

release.yml first (lines 17, 86, 163 → [self-hosted, macOS, ARM64]). This is the change that pays for itself: OIDC trusted publishing works on self-hosted runners, because the token is issued by GitHub rather than the runner host. 0.5.1 shipped with attestations: none after a manual publish; this restores provenance for 0.5.2.

Fold in the attach fix while in the file — it has failed on every release, 0.5.0 and 0.5.1 included, because gh release upload runs in a job with no checkout and cannot infer the repository:

run: gh release upload "$RELEASE_TAG" "$RUNNER_TEMP"/agentcall-release/* --clobber --repo ${{ github.repository }}

gh run rerun 32048093911 verifies both without cutting a version — it will refuse to republish 0.5.1, but build proves the runner and attach proves the fix.

ci.yml: move verify only. verify is the job scripts/ci-local.sh mirrors and the one that decides whether a change ships.

Leave the other two hosted. They cannot run on one macOS ARM64 machine:

  • packed-cli-consumer matrixes [macos-latest, ubuntu-latest] × node [20, 22, 24]. The Linux legs have nowhere to go, and that is the leg catching the missing-dist/ packaging trap CLAUDE.md documents. It also deliberately has no checkout, so it would inherit a dirty self-hosted workspace.
  • windows-compat is windows-2025. Support Windows as a native AgentCall callee #251 and Ship a signed, unattended Windows installer #252 are open against Windows behavior; silently dropping the job is worse than leaving it queued.

Restore the triggers. Retargeting alone changes nothing while both files are workflow_dispatch: only. Delete the "Automatic runs are temporarily paused while GitHub Actions billing is unavailable" comment blocks at the same time, or they become the next stale claim someone trusts — the same failure mode inv_gate_mirrors_ci exists to prevent.

Blocking precondition on going public

The repository is private today (visibility: private), which is the only reason a self-hosted runner is acceptable here.

Once it is public, pull_request + self-hosted means any stranger's forked PR runs arbitrary code on the Mac mini — ~/.ssh, ~/.npmrc, ~/.agentcall, and the npm credentials that publish this package. verify performs a full pnpm install and runs wrangler, so such a PR gets network access and a writable store. GitHub warns against self-hosted runners on public repositories specifically.

Do not flip visibility until one of these is in place:

  1. verify moved back to a hosted runner once billing is settled. Simplest, and the intended end state.
  2. pull_request dropped, running on push to in-repo branches only. Loses fork-PR coverage.
  3. Settings → Actions → "Require approval for all external contributors".

Option 1 is the recommendation; 2 and 3 are stopgaps if publication has to happen before billing clears.

Decided 2026-08-28: option 3, chosen deliberately, not as a stopgap.
verify stays on [self-hosted, macOS, ARM64] — moving it to a hosted
runner reintroduces the cost self-hosting was meant to avoid, and doesn't
remove the review burden anyway: a maintainer still has to read a first-time
contributor's diff before trusting a CI result, hosted or not. Required
approval makes that reading mandatory instead of optional — a forked PR's
workflow run queues until a maintainer approves it, so nothing executes on
the Mac mini before a human has looked at it. Read package.json, the
lockfile, and scripts/ especially closely before approving: verify runs
a full pnpm install and wrangler, so a malicious first PR has both
network access and a writable store once approved.

Apply before flipping visibility: Settings → Actions → General → "Fork pull
request workflows" → Require approval for all outside collaborators. No
workflow file changes needed for this — see #453 discussion.

release.yml is unaffected either way — it only fires on release: published, which is maintainer-triggered, not fork-PR-triggered.

Why this is filed rather than done

.github/workflows/ is a PreToolUse hard deny in .claude/settings.json (scripts/guard-verification-gate.sh), so an agent cannot make these edits — by design, see #335 and #374. A human applies them.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions