GitHub Actions runners
GitHub Actions execution uses the same runner claim, hosting and storage contracts as other placements. The controller verifies OIDC exchange through the shared capability policy. Native runner startup and workflow execution require separate integration evidence.
Repository consent and identity
A team binds a repository through its GitHub Actions registration. The workflow selects that team. Repository and owner IDs come from verified GitHub evidence, because display names and clone URLs can change or be supplied by a caller.
The workflow requests an ID token for the controller's audience with
permissions: id-token: write. POST /api/v1/runners/github/exchange verifies the
OIDC evidence and current registration before issuing a root capability. The
controller derives its team, repository, ref, SHA and workflow-run scope.
User-supplied labels grant no repository or pool authority.
OIDC evidence establishes the workflow's push scope. Verified default-branch trust comes from a signed App delivery. See GitHub App and credentials.
Credential issuance
The exchange issues a controller capability with nodes.claim and
triggers.claim, a one-hour lifetime, and the verified repository, ref, commit
workflow run, run attempt and workflow reference binding. Each repository/run/attempt
can exchange once, including after the first credential is revoked. Repository identity uses GitHub's numeric ID, so a
rename preserves that identity. An owner transfer requires a matching new
binding.
The request takes id_token and team. The controller derives grants, push
facts, expiry and runner identity. Its response carries token, team,
repository, expires_at, labels and the verified binding. The token is returned once. The exchange
has no automatic retry because a repeated request cannot recover a lost bearer.
POST /api/v1/runners/claim accepts the requested trigger and job kinds. Each
kind requires its own claim grant, and the store checks repository and push
authority before awarding work. Source, secrets, data and execution reporting
use the separate capability awarded for that unit. A runner root cannot read
them or a pipeline's sizing records by itself. Discovery routes remain
available to live roots.
The cache-grant protocol and its issuance route are removed. Byte-storage access uses the authenticated data adapter and the unit's live authority.
Claims and execution
POST /api/v1/runners/claim scans one ordered queue for the requested trigger
and job kinds. A mixed request needs both claim grants. Named owner and job
claims use the corresponding run-scoped claim routes. Every selection checks
repository authority and push scope.
Each award carries a fresh capability, a binding, granted runner identity and immutable execution inputs. The reusable Actions credential stays in the runner. SDK processes receive delegated process authority through protocol hello. Claims and exchanges never retry automatically after a lost one-time response. Lease expiry resolves an uncertain award.
The acquired checkout uses its sealed location and exact SHA. Its SDK pin selects the verified matching engine before installation, planning and execution. A workflow checkout alone cannot replace those accepted inputs.
Data and source
Data reads and writes use signed /data transfers and authenticated record
operations. A live claim needs its own authorized read edges and write scope.
Repository membership alone does not make every object readable. Pipeline
binaries remain local to the executing machine.
Source acquisition uses the live run claim at
POST /api/v1/runs/{run}/source-credential. The controller checks sealed
repository authority. Actions source acquisition uses the workflow's own
GITHUB_TOKEN, scoped to the registered repository. It receives no team
credential or App token.
See team git credentials.
Revoking a registration or credential stops later controller operations. It
cannot recall GitHub tokens, provider sessions or signed URLs already issued.
Set GITHUB_TOKEN to ${{ github.token }} on the runner step. The generated
workflow includes this setting. The token stays in source acquisition and does
not enter the pipeline environment.
Workflow
Commit this workflow as .github/workflows/sparkwing.yml, replacing the team
and controller URL with your registration values. The runner uses the
github-actions label.
# Runs Sparkwing work for this repository on this repository's GitHub
# Actions minutes. The controller hands the job only triggers and nodes for this
# repository, and the job exits once the queue has been empty for --idle-exit.
name: sparkwing
on:
push:
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
sparkwing:
runs-on: ubuntu-latest
# The runner credential lives one hour, so the job does too.
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Install sparkwing-runner
run: |
base=https://github.com/sparkwing-dev/sparkwing/releases/latest/download
curl -fsSLO "$base/sparkwing-runner-linux-amd64"
curl -fsSLO "$base/SHA256SUMS"
grep ' sparkwing-runner-linux-amd64$' SHA256SUMS | sha256sum -c -
install -m 0755 sparkwing-runner-linux-amd64 "$RUNNER_TEMP/sparkwing-runner"
- name: Run this repository's Sparkwing work
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
"$RUNNER_TEMP/sparkwing-runner" warm --github-actions \
--team acme \
--controller https://sparkwing.example.com \
--idle-exit 2m
App-triggered runs
On Sparkwing Cloud an App push or pull request run goes to the Cloud launcher
unless its automation or subscription selects runner: github-actions or
runner: pool=<name>, or a repository pool route rule requires a pool. Each of
those seals host dispatch. With github-actions, only an Actions runner whose
push binding matches the delivery's repository, ref and commit may claim the
run's trigger; other team machines are refused. A create-run request cannot
select github-actions itself. See GitHub App run owner.
The runner requests a GitHub ID token with the controller URL as its audience,
then exchanges it for a short-lived controller capability. The exchange returns
the verified push binding; the runner uses that repository and expiry for its
claims. The workflow needs id-token: write and GITHUB_TOKEN for source access.
warm uses controller-awarded leases and job settings. It rejects the removed
--heartbeat, --lease, --holder-prefix, --trigger-source,
--accept-working-tree, --cpu-ceiling, --memory-ceiling, --deadline and
--warm-module flags. Configure resource ceilings on the Kubernetes launcher;
configure job timeouts in the pipeline. Pipeline lint always analyzes source
without running pipelines; its removed --static flag is rejected.
--max-concurrent defaults to one job. Trigger owners have separate capacity
with the same bound, because an owner waits for its jobs to finish. The default
runner can hold one owner and execute one job at a time.