Runner commands required an instance admin, so the key a CI host holds was an admin key: a build step could read it and run admin commands. On gitbay.org the runner polls as the admin account with no repository scope while registration is open.
keys add --scope runnerconfines a key torunner next/log/doneand read-only git;Dispatchrefuses every other command for that scope,whoamiincluded.requireRunneraccepts a runner-scoped key or, still, an admin, so the deployed runner keeps working until its key is swapped.deploy/gitbay-runner.override.confaddsNoNewPrivileges,ProtectSystem=full,ProtectKernelTunables,ProtectControlGroups,RestrictSUIDSGID.TestRunnerScopedKey: claim works, every other command is exit 4, the account's full key is refused as a runner, clone works, push is refused.
Not in this MR: env scrubbing in the runner (a step runs as the same uid and can read the same files, so the scope is what limits the blast radius), a per-repository CI opt-in, and the Threat-Model wiki page. Left as Ref #92; the operational follow-up on bay1 is to create a non-admin ci account, add its key with --scope runner, and restart the runner with -repos krz/gitbay.
Ref #92