Skip to content
GantryCD

Runner Authentication

Runners authenticate with ES256 JWTs signed by the runner group’s private key. The backend stores only the public key.

Key Model

Each runner group owns one ECDSA P-256 keypair:

  • private key: delivered once at group creation or key rotation
  • public key: stored on the runner-group record in PostgreSQL

The backend does not keep the private key.

JWT Types

Runner Group JWT

Used by:

  • GET /api/v1/runner-groups/poll
  • POST /api/v1/runner-groups/heartbeat

Purpose:

  • authenticate the long-lived coordinator process

Key claims include:

  • token type
  • runner group ID

The group signs fresh JWTs during normal operation rather than relying on a long-lived static token.

Ephemeral Runner JWT

Used by:

  • POST /api/v1/runners/{runner_id}/ready
  • POST /api/v1/runs/{run_id}/accept
  • GET /api/v1/runs/{run_id}/status
  • POST /api/v1/runs/{run_id}/done
  • POST /api/v1/runs/{run_id}/acknowledging-cancel
  • DELETE /api/v1/runners/{runner_id}/deregister

Purpose:

  • bind one ephemeral runner to one run within one runner group

Key claims include:

  • token type
  • runner ID
  • run ID
  • group ID

Handlers still verify that JWT path parameters match the signed runner/run IDs.

Post-Completion Grace Period

Ephemeral JWTs remain valid for EphemeralRunnerJWTGracePeriod = 15 minutes after the run terminates (constant in internal/crypto/jwt.go). This window lets the runner flush logs, deregister, and complete cleanup calls without racing the run-completion transaction. The primary validity gate is run.completed_at + grace; the JWT exp claim is a backstop only.

Backend Verification Flow

Middleware verifies runner JWTs by:

  1. reading kid from the JWT header
  2. resolving the runner group by ID
  3. loading its public key
  4. verifying the ES256 signature
  5. validating standard and type-specific claims
  6. placing authenticated identity into request context

This is stateless verification. There is no token database.

O(1) Lookup

kid is the runner group ID, so verification can jump directly to the correct public key.

Key Rotation

Key rotation replaces the stored public key immediately and returns a new private key once.

Operational impact:

  • JWTs signed by the old key stop verifying immediately
  • the runner group must pick up the new private key (typically a restart) before its next signed call

Labels Are Not Trusted From JWTs

Runner labels are not embedded in ephemeral JWTs as an authority source. The backend reloads effective labels from database state during ready/assignment flows instead of trusting caller-supplied label claims.