← Back to blog
Feature note2026-09-02 17:31 UTC

A Protected Runner Is Not an Approval System

Four VPS runs show Ota binding exact-scope authority to one governed repository action while expired, revoked, and out-of-scope grants refuse before work.

A runner label chooses a machine. It does not authorize a repository action.

GitHub Actions can route a job to a self-hosted runner. A label answers one useful question:

Which machine is eligible to receive this job?

It does not answer the question that matters for a heavier repository action:

Is this exact publish, migration, deployment, or other non-routine task authorized to run now?

Those are different boundaries. A workflow must not be able to create its own approval merely by asking for a more privileged runner.

Ota's first signed crossing-authority carrier is designed around that separation. The repository declares the governed lane. The workflow asks GitHub to schedule a protected runner. A root-managed authority source outside the checkout and unprivileged job process decides whether the exact lane may cross its boundary. Ota verifies that decision immediately before effects start and emits a fresh transaction record after it does.

We pressure-tested that model on a pre-provisioned Linux/x64 VPS runner. One live authorization executed and produced a retained archive. Three invalid-authority cases refused before the task ran. The result is not a general approval system. It is concrete evidence for a deliberately bounded carrier.

The flow under pressure

COMMANDStext
Repository contract selects governed lane
        |
Workflow requests a protected runner label
        |
Protected runner invokes `ota run ... --grant <id>`
        |
Ota verifies fixed authority state and exact semantic scope
        |
One fresh crossing transaction begins
        |
Selected task runs, then receipt and archive bind admission to outcome

Each layer retains its own job:

LayerOwnsDoes not own
Repository contractWhich lane requires a crossingTrust keys, grants, bundle paths, or revocations
GitHub workflowScheduling, checkout, credentials, and provider policyAuthority issuance
Authority provisionerSigned bundle, revocation, sequence state, and signing keyRepository task selection
Ota on the runnerAdmission, scope verification, transaction, receipt, and archiveIssuing its own authority

That separation is the product value. GitHub still owns scheduling and platform controls. Ota adds repository-specific governance: a way to bind authority issued outside the selected repository task to the complete action the repository is about to execute.

What we tested

The pressure workflow verified the exact administrator-installed Ota binary against a root-owned full-commit and SHA-256 manifest before it checked authority. It then ran Ota's read-only hardening diagnostic as the unprivileged job user. Required observations had to pass: Linux/x64, non-root execution, no declared Docker host or common Docker socket, and valid fixed trust, bundle, and sequence records.

The four hosted scenarios ran against repository revision a1dba8ece7bf79448c3201a87ec7bad81bde2a8d and clean source-built Ota v1.6.26 at Core revision e9d8d8d6d206c65311f48d61648c8467b90f0c03:

ScenarioResultWhat the evidence proves
Live grantPassedExact-scope admission created a completed crossing transaction; the retained artifact contains the verified receipt archive and valid receipt history.
Expired grantPassedDry-run and real execution refused with crossing_grant_expired; before/after checkout manifests matched.
Revoked grantPassedDry-run and real execution refused with crossing_grant_revoked; before/after checkout manifests matched.
Out-of-scope grantPassedDry-run and real execution refused with crossing_grant_out_of_scope; before/after checkout manifests matched.

The refusal cases are important. A green validate result or a rejected command alone would not establish the boundary. The pressure workflow retained typed refusal JSON, the human refusal output, and complete checkout manifests before and after both dry-run and real refusal. The selected scaffold task never started.

What Ota verified before the live task ran

The repository contract names only an authority identifier:

CONTRACTyaml
governance:  crossing_authority:    authority_id: platform-release-authority

It does not contain the signing key, bundle location, trust-store path, or revocation state. Those live outside the checkout at fixed protected system paths:

COMMANDStext
/etc/ota/crossing-authorities.json
/var/lib/ota/crossing-authority.json
/var/lib/ota/crossing-authority-sequence.json

Before Ota admits the selected lane, the first carrier verifies the fixed authority binding and the signed bundle, then checks the grant's exact contract identity, complete selected execution scope, crossing family, classification, actor posture, expiry, revocation, and sequence/high-water state.

The scope is not a friendly task name. Changing dependencies, hooks, task effects, target platform, or execution selection changes the semantic scope identity. A standing grant for yesterday's publish lane cannot silently authorize a changed closure that happens to keep the same label.

Why the archive matters

A successful run needs more than an approval-looking log line.

For the live scenario, Ota created a fresh crossing transaction before the governed scaffold action, recorded its terminal success, and retained the receipt archive in the hosted artifact. Receipt history revalidated that archive rather than merely reporting that a file existed.

That gives a reviewer a linked chain:

COMMANDStext
declared governed lane
        -> verified signed authority and semantic scope
        -> fresh crossing transaction
        -> selected task outcome
        -> archive that re-derives the recorded scope

This is the gap Ota is intended to close. A provider can tell you that a workflow ran. Ota can preserve which repository action crossed a declared boundary, what authority admitted it, and what happened afterward.

What this does not prove

The correct claim is intentionally narrow:

The authority files were protected from the current Ota process under the observed filesystem boundary, and Ota verified the selected crossing against them before work started.

The carrier reports this as current_process_filesystem_guarded. It does not prove:

  • provider-attested isolation between the job and an administrator;
  • absence of every possible escalation path, mount alias, metadata credential, or host-control endpoint;
  • a verified human, CI, or platform identity;
  • one-use authority or atomic broker consumption;
  • repository-global safety, application correctness, or prevention of raw-shell bypasses.

The runner's local inspection is diagnostic evidence. It cannot independently attest the whole provider boundary. A signed image or a protected label alone would not change that fact.

What came next

The VPS matrix closed the pressure gate for Ota's first signed-file carrier. V11.7 later added the separate broker-backed carrier, available in Ota v1.6.26 and later, and completed its bounded OSS slice.

The completed broker path binds launcher attestation to the job, consumes a short-lived one-use work-unit lease atomically, retains terminal cleanup and archive evidence, and proves recovery on an independently administered hardened launcher. Provider attestation remains optional stronger hardening rather than a V11.7 completion requirement.

The useful and honest statement remains narrow: Ota can verify exact-scope authority issued outside the selected repository task on a protected runner and produce transaction-bound evidence of execution or refusal. It does not call that provider isolation, independently administered authority, verified human identity, raw-shell prevention, or a complete enterprise control plane.

For the operator layout and verification procedure, see the canonical Prebound Crossing Authority reference.