Shared Actions Runner Readiness
This document is the S41j-6 readiness packet for letting normal repositories use the shithub.sh shared Actions pool. S41j is the safety and operations track. S41k is the Actions UI parity track; S41k improves the product surface but is not a blocker for controlled arbitrary-repo execution.
Current Decision
Status: controlled dogfood, not broad public GA.
The platform can run ordinary repositories that meet the Actions policy, runner label, and syntax constraints. Broad public shared-runner enablement should wait until this checklist has a deployed/manual pass and no open Critical or High findings.
Eligibility Contract
A repository may use the shared pool when all of these are true:
- Site Actions policy allows runner dispatch. The site switch is a hard kill switch and overrides repo/org policy.
- The repo or its owner org has Actions enabled, or inherits an enabled site policy.
- The workflow lives under
.shithub/workflows/*.ymland parses under the supported v1 subset. - The triggering actor can run Actions on that repo. Untrusted pull requests queue in an approval-required state before runner dispatch.
- The job's
runs-onlabels match an online runner, normallyubuntu-latestfor the first shared Linux pool. - Repo queued-run, repo concurrency, owner concurrency, and actor hourly caps permit the run.
- The repo is not archived or deleted.
For public shithub.sh rollout, operators should keep site caps conservative and raise them only after real queue/claim/host-cost data exists.
Billing And Entitlements
Billing is present, but Actions minute metering is not enforcement-ready yet. The current entitlement boundary includes:
org.actions_minutes_quotaLimitOrgActionsMinutesQuota
LimitOrgActionsMinutesQuota intentionally reports no concrete number until
usage accounting lands. Do not gate public shared-runner execution by scattered
plan checks. When billing gates arrive, they must go through
internal/entitlements and keep authorization separate from entitlement
denials.
Recommended rollout posture:
- personal/public dogfood repos: allowed only under site policy and conservative caps;
- organization-level Actions secrets/variables: already Team-gated;
- paid shared-runner minutes: defer until metering can record usage and enforce limits consistently;
- unpaid or past-due orgs: keep paid-only Actions configuration read-only, but do not delete secrets, variables, or prior run history.
S41j-6 Findings
| ID | Severity | Status | Finding | Resolution |
|---|---|---|---|---|
| S41J6-H1 | High | Fixed in S41j-6 | Site Actions disable was not a hard kill switch; explicit repo/org enablement could still evaluate true and queued jobs could be claimed. | Effective policy and runner claim SQL now return false whenever actions_site_policy.actions_enabled=false. Tests cover enqueue-time policy and claim-time dispatch. |
| S41J6-M1 | Medium | Open with compensating control | Actions minutes billing has an entitlement key but no usage accounting or numeric limits. | Do not market or sell metered Actions minutes yet. Use site/org/repo policy caps and runner capacity as the public-runner control until billing SP08 defines usage accounting. |
| S41J6-M2 | Medium | Manual validation pending | The S41j-5 arbitrary-repo smoke must run on production after deploy. | Run the scratch plus second-repo checklist in runbooks/actions-runner.md before declaring broad availability. |
No Critical findings are open in this packet.
Required Evidence Before Broad Enablement
scripts/audit-actions-public-runners.shpasses on the deployed commit.- Focused Go tests pass for site kill switch, repo/owner concurrency caps, unsupported label diagnostics, token gates, and untrusted PR secret behavior.
- Live smoke passes on
mfwolffe/scratch. - Live smoke passes on at least one additional normal public repository with
runs-on: ubuntu-latest. - Unsupported-label workflow shows a queued diagnostic with zero matching runners.
- Untrusted pull request run receives no secrets or mask values before approval.
- Drained and revoked runners do not claim or complete new work.
- A job-container network bypass attempt cannot reach direct IP destinations or the DigitalOcean metadata service unless explicitly allowlisted.
Operator Controls
Emergency stop:
UPDATE actions_site_policy
SET actions_enabled = false,
updated_at = now()
WHERE id = true;
After this change, newly matched workflows should be skipped by policy and already queued jobs should not be claimed by runners. Keep this SQL in the incident runbook until a site-admin UI exists.
Capacity limits:
max_repo_queued_runsbounds backlog.max_repo_concurrent_jobsbounds active jobs for one repository.max_owner_concurrent_jobsbounds active jobs across one user or org owner.actor_trigger_limit_per_hourbounds trigger spam by a single actor.
These are policy controls, not billing meters. They protect the shared pool while Actions minute accounting is still future work.
Relationship To S41k
S41k should follow S41j because it is UI parity:
- Actions sidebar and management placeholders;
- workflow-specific run pages;
- run graph canvas;
- log viewer and annotations;
- caches, runners, and metrics pages.
None of those replace S41j's security gates. S41k can make unsupported labels, queue state, runner health, and usage easier to see, but it should not be the first line of defense for arbitrary code execution.
View source
| 1 | # Shared Actions Runner Readiness |
| 2 | |
| 3 | This document is the S41j-6 readiness packet for letting normal repositories |
| 4 | use the shithub.sh shared Actions pool. S41j is the safety and operations track. |
| 5 | S41k is the Actions UI parity track; S41k improves the product surface but is |
| 6 | not a blocker for controlled arbitrary-repo execution. |
| 7 | |
| 8 | ## Current Decision |
| 9 | |
| 10 | Status: **controlled dogfood, not broad public GA**. |
| 11 | |
| 12 | The platform can run ordinary repositories that meet the Actions policy, runner |
| 13 | label, and syntax constraints. Broad public shared-runner enablement should wait |
| 14 | until this checklist has a deployed/manual pass and no open Critical or High |
| 15 | findings. |
| 16 | |
| 17 | ## Eligibility Contract |
| 18 | |
| 19 | A repository may use the shared pool when all of these are true: |
| 20 | |
| 21 | 1. Site Actions policy allows runner dispatch. The site switch is a hard kill |
| 22 | switch and overrides repo/org policy. |
| 23 | 2. The repo or its owner org has Actions enabled, or inherits an enabled site |
| 24 | policy. |
| 25 | 3. The workflow lives under `.shithub/workflows/*.yml` and parses under the |
| 26 | supported v1 subset. |
| 27 | 4. The triggering actor can run Actions on that repo. Untrusted pull requests |
| 28 | queue in an approval-required state before runner dispatch. |
| 29 | 5. The job's `runs-on` labels match an online runner, normally |
| 30 | `ubuntu-latest` for the first shared Linux pool. |
| 31 | 6. Repo queued-run, repo concurrency, owner concurrency, and actor hourly caps |
| 32 | permit the run. |
| 33 | 7. The repo is not archived or deleted. |
| 34 | |
| 35 | For public shithub.sh rollout, operators should keep site caps conservative and |
| 36 | raise them only after real queue/claim/host-cost data exists. |
| 37 | |
| 38 | ## Billing And Entitlements |
| 39 | |
| 40 | Billing is present, but Actions minute metering is not enforcement-ready yet. |
| 41 | The current entitlement boundary includes: |
| 42 | |
| 43 | - `org.actions_minutes_quota` |
| 44 | - `LimitOrgActionsMinutesQuota` |
| 45 | |
| 46 | `LimitOrgActionsMinutesQuota` intentionally reports no concrete number until |
| 47 | usage accounting lands. Do not gate public shared-runner execution by scattered |
| 48 | plan checks. When billing gates arrive, they must go through |
| 49 | `internal/entitlements` and keep authorization separate from entitlement |
| 50 | denials. |
| 51 | |
| 52 | Recommended rollout posture: |
| 53 | |
| 54 | - personal/public dogfood repos: allowed only under site policy and conservative |
| 55 | caps; |
| 56 | - organization-level Actions secrets/variables: already Team-gated; |
| 57 | - paid shared-runner minutes: defer until metering can record usage and enforce |
| 58 | limits consistently; |
| 59 | - unpaid or past-due orgs: keep paid-only Actions configuration read-only, but |
| 60 | do not delete secrets, variables, or prior run history. |
| 61 | |
| 62 | ## S41j-6 Findings |
| 63 | |
| 64 | | ID | Severity | Status | Finding | Resolution | |
| 65 | | --- | --- | --- | --- | --- | |
| 66 | | S41J6-H1 | High | Fixed in S41j-6 | Site Actions disable was not a hard kill switch; explicit repo/org enablement could still evaluate true and queued jobs could be claimed. | Effective policy and runner claim SQL now return false whenever `actions_site_policy.actions_enabled=false`. Tests cover enqueue-time policy and claim-time dispatch. | |
| 67 | | S41J6-M1 | Medium | Open with compensating control | Actions minutes billing has an entitlement key but no usage accounting or numeric limits. | Do not market or sell metered Actions minutes yet. Use site/org/repo policy caps and runner capacity as the public-runner control until billing SP08 defines usage accounting. | |
| 68 | | S41J6-M2 | Medium | Manual validation pending | The S41j-5 arbitrary-repo smoke must run on production after deploy. | Run the scratch plus second-repo checklist in `runbooks/actions-runner.md` before declaring broad availability. | |
| 69 | |
| 70 | No Critical findings are open in this packet. |
| 71 | |
| 72 | ## Required Evidence Before Broad Enablement |
| 73 | |
| 74 | - `scripts/audit-actions-public-runners.sh` passes on the deployed commit. |
| 75 | - Focused Go tests pass for site kill switch, repo/owner concurrency caps, |
| 76 | unsupported label diagnostics, token gates, and untrusted PR secret behavior. |
| 77 | - Live smoke passes on `mfwolffe/scratch`. |
| 78 | - Live smoke passes on at least one additional normal public repository with |
| 79 | `runs-on: ubuntu-latest`. |
| 80 | - Unsupported-label workflow shows a queued diagnostic with zero matching |
| 81 | runners. |
| 82 | - Untrusted pull request run receives no secrets or mask values before approval. |
| 83 | - Drained and revoked runners do not claim or complete new work. |
| 84 | - A job-container network bypass attempt cannot reach direct IP destinations or |
| 85 | the DigitalOcean metadata service unless explicitly allowlisted. |
| 86 | |
| 87 | ## Operator Controls |
| 88 | |
| 89 | Emergency stop: |
| 90 | |
| 91 | ```sql |
| 92 | UPDATE actions_site_policy |
| 93 | SET actions_enabled = false, |
| 94 | updated_at = now() |
| 95 | WHERE id = true; |
| 96 | ``` |
| 97 | |
| 98 | After this change, newly matched workflows should be skipped by policy and |
| 99 | already queued jobs should not be claimed by runners. Keep this SQL in the |
| 100 | incident runbook until a site-admin UI exists. |
| 101 | |
| 102 | Capacity limits: |
| 103 | |
| 104 | - `max_repo_queued_runs` bounds backlog. |
| 105 | - `max_repo_concurrent_jobs` bounds active jobs for one repository. |
| 106 | - `max_owner_concurrent_jobs` bounds active jobs across one user or org owner. |
| 107 | - `actor_trigger_limit_per_hour` bounds trigger spam by a single actor. |
| 108 | |
| 109 | These are policy controls, not billing meters. They protect the shared pool |
| 110 | while Actions minute accounting is still future work. |
| 111 | |
| 112 | ## Relationship To S41k |
| 113 | |
| 114 | S41k should follow S41j because it is UI parity: |
| 115 | |
| 116 | - Actions sidebar and management placeholders; |
| 117 | - workflow-specific run pages; |
| 118 | - run graph canvas; |
| 119 | - log viewer and annotations; |
| 120 | - caches, runners, and metrics pages. |
| 121 | |
| 122 | None of those replace S41j's security gates. S41k can make unsupported labels, |
| 123 | queue state, runner health, and usage easier to see, but it should not be the |
| 124 | first line of defense for arbitrary code execution. |