Skip to content

Using GitHub Apps with the GitHub Actions deployment

GitHub Apps are the recommended credential profile for production use of the GitHub Actions only deployment. The Activity collector, orchestrators, and workers create short-lived installation tokens from two private GitHub Apps that your organization or enterprise owns.

  • Read app. Used by GitHub tools, admission, control precompute, and the Activity collector (cao-activity.yml).
  • Write app. Used only by trusted safe-output processing.

The apps don’t affect how the dashboard is hosted. The dashboard build and deploy jobs never use the apps. They use the automatic github.token and OpenID Connect (OIDC).

Use this profile when any of the following is true:

  • Your targets are private or internal, span many repositories, or belong to several organizations in one GitHub Enterprise Cloud enterprise.
  • Your campaigns read Actions, security, issue, or pull request evidence across repositories.
  • Your review outputs go to a separate repository, or your campaigns write live safe outputs.
  • Your credentials must not depend on one person’s access.

You need everything in the prerequisites for the GitHub Actions only deployment, plus the following.

RequirementDetails
App ownershipAn approved private App pair and access to its keys, or permission to create Apps in the owning organization/enterprise. Public apps aren’t supported.
App installationAppropriate approval for each new organization installation. Repository administrators can extend an already-approved installation to their repositories where organization policy permits.
Read app permissionsRead-only access to Actions, Checks, Contents, Issues, Packages, Pull requests, Secret scanning alerts, Security events, Commit statuses, Vulnerability alerts, and Metadata. No write permissions.
Write app permissionsWrite access to Actions, Contents, Issues, and Pull requests. Read access to Administration and Metadata.
WebhooksTurned off for both apps.

Grant only the permissions that your installed campaigns need. For more information, see Permissions.

  1. Install the dashboard. For more information, see Deploying the dashboard. Don’t run any workflows yet.

  2. Create and install the apps.

    • For already-approved Apps, reuse their client IDs and securely enter their keys instead of registering replacements. This also supports private enterprise Apps installed separately in each enrolled organization.

      Terminal window
      gh cao setup auth github-app \
      --repo OWNER/CONTROL-REPOSITORY \
      --read-client-id READ-APP-CLIENT-ID \
      --write-client-id WRITE-APP-CLIENT-ID \
      --dry-run

      Remove --dry-run to apply. See Reuse approved Apps for approval boundaries and supported GitHub versions.

    • For one organization, use the manifest helper. Preview the changes with --dry-run first. If you use GitHub Enterprise Cloud with data residency, set GH_HOST to your host name. Otherwise, omit it.

      Terminal window
      export GH_HOST=HOSTNAME
      gh cao setup auth github-app --repo OWNER/CONTROL-REPOSITORY --dry-run
      gh cao setup auth github-app \
      --repo OWNER/CONTROL-REPOSITORY \
      --write-repository OWNER/OUTPUT-REPOSITORY
    • For several organizations in one enterprise, create both private apps in your enterprise settings. The manifest helper can’t create apps that an enterprise owns. Install each app on selected repositories in every enrolled organization, then run the following command.

      Terminal window
      gh cao setup auth enterprise-app \
      --repo OWNER/CONTROL-REPOSITORY \
      --read-client-id READ-APP-CLIENT-ID \
      --write-client-id WRITE-APP-CLIENT-ID \
      --policy .github/workflows/cao.json \
      --write-repository OWNER/CONTROL-REPOSITORY

    Replace the placeholders as follows:

    • OWNER/CONTROL-REPOSITORY with your control repository.
    • OWNER/OUTPUT-REPOSITORY with an approved safe-output repository.
    • READ-APP-CLIENT-ID and WRITE-APP-CLIENT-ID with the client IDs of your apps.
  3. For every installation, select Only select repositories.

    • Install the read app on the control repository and on every repository that .github/workflows/cao.json allows.
    • Install the write app only on approved safe-output repositories. By default, that’s only the control repository.
    • Setup uses a short-lived App installation token to verify the exact selected repository membership and fails closed rather than relying on manual verification.
  4. Add the write App’s APP-SLUG[bot] login to the on.bots allowlist of every worker workflow that the App may dispatch, then recompile those workflow sources. A run whose pre-activation job succeeds while activation is skipped has not executed the worker.

  5. Confirm that the helper stored the client IDs as variables and the private keys as secrets. For the names, see Configuration reference. The helper passes private keys to gh secret set through standard input, so they are never written to disk or passed as command arguments.

  6. Delete any GH_AW_GITHUB_READ_PAT, GH_AW_GITHUB_WRITE_PAT, or GH_AW_GITHUB_TOKEN secrets. If you leave them in place and an app is misconfigured, CAO silently uses the PAT instead.

  7. Run the CAO Activity workflow. Its first successful default-branch run triggers the CAO Dashboard workflow automatically. For the remaining steps, see Deploying the dashboard.

  8. Before you enable any campaign in live mode, validate the credentials. For more information, see Validating the credentials.

NameTypeDescription
GH_AW_GITHUB_READ_APP_IDActions variableClient ID of the read app
GH_AW_GITHUB_READ_APP_PRIVATE_KEYActions secretPrivate key of the read app, in PEM format
GH_AW_GITHUB_WRITE_APP_IDActions variableClient ID of the write app
GH_AW_GITHUB_WRITE_APP_PRIVATE_KEYActions secretPrivate key of the write app, in PEM format

CAO chooses a credential for each step in this order.

Step typeOrder
Read stepsRead app token, then GH_AW_GITHUB_READ_PAT, then GH_AW_GITHUB_TOKEN, then github.token
Safe outputsWrite app token, then GH_AW_GITHUB_WRITE_PAT, then GH_AW_GITHUB_TOKEN, then github.token

An app token is used only when both its variable and its secret are set. CAO looks up installation IDs at runtime from the target owner and repository. It never stores them in policy or dispatch inputs.

The apps control which repositories CAO can reach, not which ones it may act on. The .github/workflows/cao.json file still decides scope and mode.

This profile uses the same signals as the GitHub Actions only deployment, plus the following.

SignalWhat to check
Token selectionThe actions/create-github-app-token step in each job shows whether CAO created an app token. If the step was skipped, the run used a PAT or github.token instead.
API capacityBefore discovery, admission checks GET /rate_limit for the selected credential. Each app installation has its own rate limit, which grows with the size of the installation. A failed capacity gate points to one installation, and the dashboard shows it as a GitHub API capacity admission gate.
Audit logsInstallation token activity appears in organization and enterprise audit logs under the app’s identity, not under a user.
  • Short-lived tokens. Tokens expire after about one hour and are scoped to one installation. Each safe-output token is limited to the permissions of the selected handler.
  • Separate read and write identities. The read app can’t write.
  • No dependency on individuals. The credentials keep working if a user loses access or leaves.
  • Multi-organization support. One enterprise can span several organizations through separate installations in each organization.
  • No inferred access. If an app isn’t configured, CAO skips it. It never infers access from another credential.
  • Protection from silent fallback. If an app variable or secret is missing, the run uses the next available credential. CAO can’t tell whether that was intended, so delete PAT secrets that you don’t use.
  • Access outside the enterprise. A private app can’t be installed outside the organization or enterprise that owns it. For unrelated owners, use separate control planes.
  • Access from an enterprise installation. Installing an app on the enterprise doesn’t grant access to any repositories. Every organization needs its own installation on selected repositories.
  • Automatic key rotation. Private keys don’t expire. You must rotate them.
  • Policy changes. Installation scope doesn’t widen or narrow CAO policy. CAO still refuses a repository that the app can reach but policy doesn’t allow.
  1. For every enrolled organization, create a read token and confirm that it can read the expected repositories but can’t write.
  2. In an approved output repository, use only the write app to make a reversible change, then undo it.
  3. Confirm that neither app is installed on unrelated repositories.
  1. Generate a new private key for each app, and replace the matching secret.
  2. Run a bounded review run for each installed campaign.
  3. Delete the old private keys, then review the installations and permissions again.

If you suspect that a key was exposed:

  1. Set the kill switch for each affected campaign to false.
  2. Cancel active runs.
  3. Delete the private key, or suspend the installation.
  4. Investigate the exposure.

For more information, see Rotation and revocation.