Security model
Treat every CI job as hostile.
The controller is trusted. Workers are temporary and untrusted. The design is meant to keep a compromised job away from permanent credentials, other installations, and production networks.
GitHub App key
Provider credential
Durable budget
No permanent secret
No inbound access
Hard deadline, idle release
Normal completion
Timeout
Reconciliation
Recommended
Your own dedicated GitHub App
RunnerYard creates a private App owned by your user or organization and installs it only on the selected repositories. GitHub places repository runner scale-set management behind Administration write; organization fleets use the narrower Self-hosted runners write permission. No webhook or OAuth permission is requested.
The manifest flow is protected by random state and a local callback. GitHub returns a one-time private key to the CLI; it is verified and written directly to your controller secret sink without being printed.
Alternative
Bring your own App
Use an existing private App when your organization already manages key issuance and rotation. RunnerYard verifies the app identity, installation owner, repository access, and required permission before storing anything.
chmod 600 ./existing-app.pem
npx runneryard auth github import \
--github https://github.com/acme/widgets \
--client-id Iv1.example \
--installation-id 123456 \
--private-key-file ./existing-app.pem \
--controller-app acme-ci-controllerNot offered
A shared hosted RunnerYard App
One central App is safe only when a hosted broker retains its private key, authenticates every controller, authorizes each installation, and mints short-lived tokens. Shipping the central private key to customer controllers would let any one of them sign as the App.
RunnerYard does not operate that broker today. The default stays fully self-hosted and operator-owned. A hosted control plane can be added later as a separate, explicit trust model, not hidden inside the CLI.
Worker isolation
No credential inheritance
Controller and workers must live in separate secret scopes. Every worker receives only a one-job JIT configuration, runs with no restart policy, has a maximum lifetime, and releases itself when no job arrives. Do not expose self-hosted runners to untrusted public fork pull requests.
Provider credentials should be limited to a dedicated worker app or project. The worker network must have no route to production, controller internals, or cloud metadata services.
Cost containment
Admission fails closed
Before launch, the controller durably reserves the job's worst-case runtime. New work stays queued when concurrency or rolling usage ceilings are reached. Missing or unreadable budget state stops the controller.
Provider-side spending alerts remain useful because network, image storage, the controller, and provider price changes sit outside the runtime ceiling.
Outboarding
Leave without RunnerYard
- Disable the fleet route and confirm jobs target
ubuntu-latest. - Stop the controller after active jobs finish; verify zero workers.
- Revoke the provider token.
- Dedicated App: revoke its keys, uninstall it, and delete it.
- Shared BYO App: remove this controller's local credential first. Change repository access only after confirming no remaining consumer needs it. For an organization fleet, retire its scale set and runner-group access as applicable without treating the shared installation as controller-owned.
- Keep shared keys, installations, and the App until every remaining consumer rotates away.
- Delete provider resources, generated config, local credential files, and the repository variable.