Skip to content

Connect GitHub to Team Server, place the Runtime Sensor on the runner, and instrument each protected GitHub Actions job with Endura commands.

Object Mapping

GitHubTeam Server
OrganizationDiscovery scope
RepositoryPipeline
Workflow runBuild
JobPart of the workflow-run build

Discover the repository before instrumenting it. All its workflows and jobs share one policy.

Prerequisites

  • Operator or Administrator access to Team Server
  • GitHub organization access that can create an integration token
  • Permission to edit protected workflows
  • For self-hosted runners, root access to every Linux x86-64 runner host
  • For GitHub-hosted runners, an Endura license and Runtime Sensor token stored as Actions secrets

Configure the Integration

Credentials and Permissions

Use a dedicated service account. Team Server supports both classic and fine-grained personal access tokens.

For a classic personal access token, use these scopes:

ScopeWhen it is required
read:orgAlways. Team Server uses it to list the organizations available to the token owner.
repoWhen Team Server must discover private repositories or read their GitHub Actions workflow-run history. Selecting the parent repo checkbox also selects its child scopes, which is expected.

The repo scope does not include read:org, so select both when private repositories are required. GitHub does not provide a read-only classic PAT scope for private repositories. The repo scope therefore grants more access than Team Server uses, and the token must be protected accordingly.

For organizations that contain only public repositories, read:org is sufficient. The token owner must be a member of every organization Team Server should discover and must have access to the required repositories. If an organization uses SAML single sign-on, authorize the classic token for that organization.

For a fine-grained personal access token, configure:

SettingValue
Resource ownerThe GitHub organization Team Server should discover. The token can discover only the organization selected as its resource owner, so use a separate token and integration for each organization.
Repository accessAll repositories, or Only select repositories containing the workflows Team Server should monitor.
Repository permissionsMetadata: Read-only and Actions: Read-only.

The organization may need to approve the fine-grained token before it can access organization resources. If repository access is limited to selected repositories, Team Server discovers only those repositories.

Set an expiration and rotation owner, then copy the token into your secrets manager.

Add GitHub to Team Server

  1. Open Integrations, then GitHub under Discover.
  2. Select Get Started or Create.
  3. Enter https://github.com or the base URL of GitHub Enterprise Server.
  4. Paste the token and select Test.
  5. Select the organizations to discover.
  6. Select Save.

Repositories from the selected organizations appear under Pipelines. Create a separate integration for each GitHub Enterprise Server or credential boundary.

For a private GitHub Enterprise Server address or internal CA, configure Team Server outbound HTTP policy.

Schedule Discovery

Add these jobs to Team Server’s scheduler.jobs:

github_pipelines:
  run: "github_sync mode:pipelines trigger:scheduled"
  schedule: "0 2 * * * * *"
github_builds:
  run: "github_sync mode:builds trigger:scheduled"
  schedule: "0 3,18,33,48 * * * * *"

Keep the required statistics_calculate and status_reconcile jobs enabled. Restart Team Server after changing its configuration; see Scheduler.

Manage Sync and Deletion

Use Start Sync for an immediate run. See sync controls and deletion.

Choose the Runner Pattern

Self-hosted Runners

Install the Runtime Sensor once on every host that can run a protected workflow. Include installation in images or provisioning for ephemeral self-hosted runners. See Runtime Sensor deployment.

Use runner labels to keep protected jobs on Sensor-enabled Linux x86-64 hosts. Instrument every protected job:

jobs:
  build:
    runs-on: [self-hosted, linux, x64, endura]
    steps:
      # Start protection before source checkout.
      - name: Start Endura Job
        run: |
          ENDURA_JOB_ID=$(endura job start)
          echo "started endura job with id ${ENDURA_JOB_ID}"

      # Check out and build protected sources.
      - name: Checkout Code
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Build
        run: make build

      # Stop protection after every job outcome.
      - name: Stop Endura Job
        if: always()
        run: endura job stop

if: always() schedules cleanup after failure or cancellation. Forced cancellation, deadlines, or runner loss can still prevent cleanup and artifact collection. Keep Sensor credentials in the root-only host configuration.

GitHub-hosted Runners

GitHub-hosted runners are ephemeral, so install and start the Runtime Sensor within the job. Store ENDURA_LICENSE_KEY and ENDURA_SENSOR_TOKEN as repository or organization secrets.

name: CI
on:
  push:
  workflow_dispatch:

env:
  ENDURA_TEAM_SERVER: https://team-server.example.com
  ENDURA_ENV_VARS: ENDURA_TEAM_SERVER,ENDURA_LICENSE_KEY,ENDURA_SENSOR_TOKEN,ENDURA_LOG_FILE

jobs:
  build:
    runs-on: ubuntu-26.04
    permissions:
      contents: read
    steps:
      - name: Start Endura Sensor
        run: |
          # Install the Runtime Sensor package.
          curl -sSf https://repo.endurasecurity.com/install/endura-sensor/testing.sh | sudo sh

          # Start and verify the ephemeral Sensor.
          export ENDURA_LOG_FILE="${RUNNER_TEMP}/endura.log"
          sudo --preserve-env="${ENDURA_ENV_VARS}" endura sensor start -d
          echo "started endura sensor: $(endura sensor status)"
          sudo endura sensor hooks

          # Start protection for subsequent workflow steps.
          ENDURA_JOB_ID=$(endura job start)
          echo "started endura job with id ${ENDURA_JOB_ID}"

        # Expose credentials only during Sensor startup.
        env:
          ENDURA_LICENSE_KEY: "${{ secrets.ENDURA_LICENSE_KEY }}"
          ENDURA_SENSOR_TOKEN: "${{ secrets.ENDURA_SENSOR_TOKEN }}"

      # Check out and build protected sources.
      - name: Checkout Code
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Build
        run: make build

      # Stop and flush the ephemeral Sensor.
      - name: Stop Endura Sensor
        if: always()
        run: sudo endura sensor stop

      # Make the root-owned log uploadable.
      - name: Prepare Endura Sensor Log
        if: always()
        run: |
          if test -f "${RUNNER_TEMP}/endura.log"; then
            sudo chown "$(id -u):$(id -g)" "${RUNNER_TEMP}/endura.log"
          fi

      # Retain Sensor logs after every outcome.
      - name: Upload Endura Sensor Log
        if: always()
        uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: endura-sensor-log-${{ github.job }}-${{ github.run_attempt }}
          path: ${{ runner.temp }}/endura.log
          if-no-files-found: warn

Keep ENDURA_TEAM_SERVER and the named sudo allowlist at workflow scope. Expose license and token only to Sensor startup; add any optional startup variable to that allowlist. Build and cleanup steps need no Sensor credentials.

The example checks status unprivileged and uses root for installation, startup, hooks, shutdown, and log ownership. After shutdown, it transfers the RUNNER_TEMP log to the runner for upload. Both cleanup and upload use if: always().

Sensor shutdown stops active jobs, prints derived policies, and attempts telemetry delivery. No preceding job stop or autoflush setting is needed. Keep cleanup failures visible and restrict GITHUB_TOKEN to build requirements.

Container Jobs on Self-hosted Runners

The Runtime Sensor remains on the runner host. Mount its socket and install the CLI inside the job container:

jobs:
  build:
    runs-on: [self-hosted, linux, x64, endura]
    container:
      image: node:24.21.0-bookworm
      options: --volume /run/endura/sensor.sock:/run/endura/sensor.sock
    steps:
      - name: Start Endura Job
        run: |
          # Install the CLI inside the container.
          curl -sSf https://repo.endurasecurity.com/install/endura-sensor/testing.sh | sh

          # Start protection through the host Sensor.
          ENDURA_JOB_ID=$(endura job start)
          echo "started endura job with id ${ENDURA_JOB_ID}"

      # Check out and test protected sources.
      - name: Checkout Code
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - run: npm test

      # Stop protection after every job outcome.
      - name: Stop Endura Job
        if: always()
        run: endura job stop

Without the socket mount, the CLI cannot reach the Runtime Sensor on the host.

Multi-job Workflows

Instrument each job that performs protected work. Jobs run in separate runner contexts, but all instrumented jobs in one workflow run report to the same Team Server build. Every job and workflow in the repository shares the repository policy.

Run all jobs and workflow variants while deriving the policy.

Standardize Instrumentation

Pin reused actions to a full commit SHA. These mechanisms reduce duplication but still require repositories or workflows to adopt the Endura wrapper. GitHub ruleset workflows gate pull-request merges; they do not transparently wrap every job in a repository and are not presented as universal Runtime Sensor instrumentation.

Verify

  1. Run an instrumented workflow.
  2. Open the repository under Pipelines in Team Server.
  3. Open Builds and confirm the workflow run appears.
  4. Confirm endura sensor status reports the expected Team Server connection and root-only endura sensor hooks reports the expected hook coverage.
  5. If the run does not appear, confirm repository discovery, runner placement, and the start-step log.

Troubleshooting

SymptomCheck
No organizations or repositories appearFor a fine-grained PAT, confirm the target organization is the resource owner and the organization approved the token. For a classic PAT, confirm read:org. Then verify repository access and both GitHub discovery jobs.
A workflow run does not appearConfirm the repository was discovered first, the Runtime Sensor was online on the selected runner, and the start step ran.
A GitHub-hosted runner cannot start the Runtime SensorReview the installer output and confirm the runner provides the required root access, BTF, and eBPF capabilities. Use endura sensor hooks to distinguish hook attachment from Team Server connectivity.
A container job cannot reach the Runtime SensorConfirm the host socket is mounted at /run/endura/sensor.sock inside the job container.
A job remains activeOn a self-hosted runner, confirm the job-stop step uses if: always(). On a GitHub-hosted runner, confirm the Sensor-stop step uses if: always(). Review the cleanup log and retained Sensor artifact for errors.

Create and Assign a Policy

Derive across representative workflows and jobs and capture the policy printed by endura job stop (persistent runners) or endura sensor stop (ephemeral runners). Refine and test it in observe mode, then assign it to the repository pipeline before enforcement.

Configure notifications after violations reach Team Server.