Skip to main content



GitHub is a Git-based source code repository. You can use GitHub to share code with team members, track and manage changes to code over time, and collaborate on projects across your organization.

In this guide, we'll look at a couple ways to integrate GitHub with Cortex, and how this integration can streamline operations and improve code quality.

Setup and configuration

Getting started

There are two ways you can connect Cortex to your GitHub instance: through a GitHub app or with a personal access token. By using Cortex's official GitHub app or a custom GitHub app, users can tag entities with Git details and enable GitOps-style configuration of data in Cortex.

If your GitHub setup involves multiple organizations, you can add multiple GitHub apps, use a personal access token that has access to all orgs, or create multiple configurations with corresponding aliases.


If you do not see the settings page you're looking for, you likely don't have the proper permissions and need to contact your admin.


If you're using a self-hosted instance of GitHub, you'll need to verify that your Cortex instance is able to reach the GitHub instance.

We route our requests through a static IP address. Reach out to support at to receive details about our static IP. If you're unable to directly allowlist our static IP, you can route requests through a secondary proxy in your network that has this IP allowlisted and have that proxy route traffic to your GitHub instance.

Cortex's official GitHub app

Cortex's official GitHub app is the easiest way to connect your GitHub instance. To set up the official app, navigate to GitHub settings and click Install next to the "cortex" configuration. Click Configure on the next page, choose your account, and click Install & Authorize.

Cortex's official GitHub app is preconfigured with:

Cortex's official GitHub app also comes with a built-in linter, which validates a given cortex.yaml file and checks Cortex-specific items. However, the linter DOES NOT validate data correctness.

For example, the linter will confirm that the format of a group block is correct, but will not check that the group exists.


The linter in Cortex's official GitHub app only validates the format of data, not content.

Custom GitHub app

If you're using Cortex Server, or if you don't want to use the official Cortex app, you can connect a custom GitHub app.

First, you'll need to register the app in advance. When you're creating the app, make sure to follow these steps:

  • Disable "Expire user authorization tokens." (Cortex does not support this OAuth workflow yet.)
  • Select "Request user authorization (OAuth) during installation."
  • Callback URL:{alias}
  • Webhook URL:
  • Add a webhook secret identical to the secret in Cortex settings.
  • Set the repository and organization permissions outlined in the configuration modal.
  • Check Push and Check suite under Subscribe to events.
  • Generate a client secret and private key. This will only be possible once the app has been created.

Once the app is registered, select Add GitHub configuration from GitHub settings in Cortex and enter the following information in the modal.

  • Alias: Name Cortex will associate with a given configuration.
  • Application ID: ID for your custom GitHub app
  • Client ID: Unique client ID assigned to your app during registration.
  • Client secret: Unique client secret assigned to your app during registration.
  • Private key: Private key to authenticate with your GitHub app.
  • Public link: Public URL of your GitHub app.
  • API endpoint (for GitHub enterprise): Endpoint root URL for self-managed GitHub setups.

Once you've saved the configuration, you must authenticate the app via the Install button next to the configuration.


Cortex Server users need to submit a custom GitHub app, as the offical one will not be available. You can add new apps under settings → GitHub.

Personal access token

You can also use a personal access token to connect to GitHub. The personal access token needs repo and read:org permissions at the minimum.


Beyond the minimum permissions indicated above, many scenarios will require that the user who generated the personal access token have organization ownership permissions within GitHub.

Select Add GitHub configuration in settings and choose Personal access token from the dropdown menu in the top right corner of the modal and enter the following information:

  • Alias: Name Cortex will associate with a given configuration.
  • Token: Personal access token generated in GitHub.
  • API endpoint (for GitHub enterprise): Endpoint root URL for self-managed GitHub setups.

Multi-account support

The GitHub integration has multi-account support so you can add a configuration for each additional organization, instance, or account by repeating the above process.

Each configuration requires an alias, which Cortex uses to correlate the designated organization, instance, or account with registrations for various entities. Registrations can also use a default configuration without a listed alias.

You can edit aliases and default configurations from the GitHub page in settings. Select the edit icon next to a given configuration and toggle "Set as default" on. If you only have one configuration, it will automatically be set as the default.

API permissions

Cortex's official GitHub app is pre-configured with the permissions needed to use this integration, listed below.

Custom GitHub apps should be configured with the following minimum permissions:

PermissionRequirementPurpose(s) in Cortex
  • Read workflow run information for Git-based CQL rules
  • Artifact information for actions
AdministrationRead & write
  • Create repositories
ChecksRead & write
  • Used by Cortex's GitHub app linter on pull requests
Code scanning alertsRead-only
  • Get vulnerability information for Git-based CQL rules
Commit statusesRead & write
  • Read commits for an entity's Git metadata
  • Read commits for Git-based CQL rules
  • Show pending status messages on the OpenAPI incompatibility check
ContentsRead & write
  • Read cortex.yaml, cortex-properties.yaml, and package/OpenAPI files
  • Read Git rules
  • Create file contents
Dependabot alertsRead-only
  • Read vulnerability information for Git CQL rules (only relevant if using Dependabot)
  • Read associated data with repositories for populating entity Git integration and for Git CQL rules
Pull requestsRead & write
  • Read pull request data for Git CQL rules and developer homepage My PRs tab
  • Comment if there are breaking OpenAPI changes on a PR
SecretsRead & write
  • Optionally write repo secrets after creating new repo
Single fileRead & write (Path to cortex.yaml)
  • Read cortex.yaml files
  • Create cortex.yaml files
WorkflowsRead & write
  • Write in GitHub actions files
Members (org-level permission)Read-onlyRead membership information for ownership and team composition

To write rules related to Dependabot alerts, you must verify the necessary permissions are set for repositories you'd like to see vulnerabilities reported on.

To verify, navigate to a repository on GitHub and click "Settings" → "Code security and analysis". Make sure you are a member of a team under "Access to alerts."


You can use GitHub to define metadata about the entity's repo and ownership.


Entity descriptor


You can define a GitHub repository for a given entity by adding the x-cortex-git block to the entity's descriptor.

repository: cortex/docs
basepath: myService
alias: myApp
repositoryGitHub repository in the form {organization}/{repo}
basepathSubdirectory for the entity if it is in a monorepo
aliasAlias is optional and only relevant if you have opted into multi-account support

Only one repository can be defined for in a given entity's YAML in the x-cortex-git block. Users looking to list additional repositories without the full functionality of GitOps can define the repos as custom data.

- `{organization}/{repo-two}`
- `{organization}/{repo-three}`


You can define the following block in your Cortex entity descriptor to add your GitHub teams.

- type: group
name: cortex/engineering
provider: GITHUB
description: This is a description for this GitHub team that owns this entity.
typeOwnership type; must be defined as group for GitHub teams
nameGitHub team name in the form {organization}/{team}

Team names are generally converted to lowercase with - separators (Team Name would be cortex/team-name), but you can verify your exact name from the GitHub permalink
providerName of integration (in this case, GITHUB)
descriptionDescription for the GitHub team

Multiple GitHub organizations are not supported yet for ownership, and Cortex will use the default configuration when fetching teams.

Identity mappings

Cortex maps users' email addresses to discovered GitHub accounts, so you never need to define email ownership in an entity descriptor.

You can confirm users' GitHub accounts are connected from GitHub identity mappings in settings.

Expected results

Entity pages

Cortex uses the Github integration for a significant amount of data that appears on entities' detail pages.

The GitHub integration will populate the Repo and Language detail blocks on an entity's details page. If a GitHub team has been defined as the owner for an entity, it will also appear in the Owners block.

Vulnerabilities pulled from the GitHub Dependabot will also be tracked in the Code and Security block in the Overview tab and in the Issues and Vulnerabilities block in the Code and Security tab. This feature does not support secret scanning.

In the Recent activity preview, you'll find the recent commits and releases. These will also appear in the event timeline.

This data will appear for entities imported from a Git source or those that have a Git repo defined in their YAMLs.


On an entity's Events page, you can find all of the commits and releases associated with that entity. Each is hyperlinked to the commit or release page in GitHub and includes a timestamp.


If a Workflow applies to a given entity, any actions you can perform are available under the Actions tab in the side panel.

Integrations - Git

You can access more detailed information pulled from GitHub in the Git tab under Integrations in the sidebar. At the top of the Git page, you'll find the repo(s) associated with that entity and the most-used language in files for that entity. In the Top contributors block, you'll find the three users who have contributed the most code and the number of their contributions.

In the Commits section, you'll find the 10 most recent commits and metadata about each. Below Commits is the Recent releases section, which includes the 5 most recent releases.

Integrations - GitHub workflows

From the GitHub workflows tab, you can find a history of GitHub workflow runs for the past week. Each run is tagged with its status: IN_PROGRESS, COMPLETED, SUCCESS, CANCELLED, FAILURE, PAUSED.


The GitHub workflows page displays data about workflows in GitHub, not workflows initiated via Cortex's Workflows tool.

Team pages

When a GitHub team is registered with a team entity, Cortex will pull GitHub users in to the Members tab. When available, Cortex will pull in the profile picture and email address for each user.

Scorecards and CQL

With the GitHub integration, you can create Scorecard rules and write CQL queries based on GitHub data.

Approvals required to merge

Number of approvals required to merge a pull/merge request into a repository.

Defaults to 0 if no approvals are defined.

Definition: git.numOfRequiredApprovals()


For a security or development maturity Scorecard, you can write a rule to make sure at least one approval is required to merge a pull/merge request:

git.numOfRequiredApprovals() > 0

By having a rigorous PR process in place for a repo, you can make sure changes aren't made that create vulnerabilities. This kind of rule could also be used in a best practices or project standards Scorecard.

You can also use a similar expression in the Query Builder to find entities lacking approval:

git.numOfRequiredApprovals() < 1

List all live branches with some basic metadata.

  • Head
  • Is protected
  • Name

Definition: git.branches()


For a best practices Scorecard, you can make sure that branches associated with an entity match a standard naming convention:

git.branches().all((branch) =>"(main|master|feat-.*|bug-.*|task-.*)"))
Branch protection details

Find details for specified branch, or default branch if none is specified.

  • Branch name
  • Code owner reviews required
  • Dismiss stale reviews
  • Required status checks
  • Restrictions apply to admin
  • Review required

Definition: git.branchProtection()


For a security Scorecard, you can write a rule to make sure the default branch is protected:

git.branchProtection() != null

Because vulnerabilities in the default branch are critical, this rule should be in one of the first couple levels.

You can also use the Query Builder to find entities with unprotected default branches:

git.branchProtection() = null

Get the latest commits (to a maximum of 100) for a defined lookback period (defaulting to 7 days).

  • Date
  • Message
  • SHA
  • URL
  • Username

These results can be filtered based on branch name, using the default branch if no other branch is provided.

Definition: git.commits()


You can use the git.commits() expression in a security Scorecard to make sure entities have fewer than three commits to a "security-fixes" branch in the last week:

git.commits(branch="security-fixes", lookback=duration("P7D")).length < 3

Entities passing this rule will include those that haven't needed three or more security fixes. This can indicate that there aren't vulnerabilities in a given entity's code, but could also suggest that fixes aren't being implemented. Using this rule in conjunction with one focused on vulnerabilities could provide the extra context needed to gain a better understanding of what's happening.

Default branch

Default branch for the repository, or main when null.

Definition: git.defaultBranch()


If default branches should always be named "main," you can write a rule to make sure entities follow this practice:

File contents

Load the contents of a file from the entity's associated repository.

The contents can be validated by using string comparison operations or parsed by the built-in jq function. The jq function will automatically coerce file contents of JSON or YAML formats.

Definition: git.fileContents()


For a Scorecard focused on development maturity, you could use the git.fileContents() rule to enforce that a CI pipeline exists, and that there is a testing step defined in the pipeline.

git.fileContents("circleci/config.yml").matches(".*npm test.*")

A best practices Scorecard, meanwhile, could use this expression for a number of rules:

  • To make sure node engine version in specified in the package.json file:
    jq(git.fileContents("package.json"), ".engines.node") != null
  • To make sure TypeScript projects have a tsconfig.json file checked in:
    jq(git.fileContents("package.json"), ".devDependencies | with_entries(select(.key == \"typescript\")) | length") == 0 or git.fileExists("tsconfig.json")
  • To make sure projects using yarn do not allow NPM:
    jq(git.fileContents("package.json"), ".engines.yarn") == null or jq(git.fileContents("package.json"), ".engine.npm") = "please-use-yarn"
  • And to ensure the yarn version being used is not deprecated:
    jq(git.fileContents("package.json"), ".engines.yarn") == null or !(semver("1.2.0") ~= semverRange(jq(git.fileContents("package.json"), ".engines.yarn")))
File exists

Check if file exists from within the entity's associated repository.

Definition: git.fileExists()


For a Scorecard focused on best practices, you can make sure that repositories contain a file:


This rule would make sense in the first level because it's so essential.

A higher-level rule in a best practices Scorecard might confirm that developers are checking in lockfiles to ensure consistency in package installs:

git.fileExists("yarn.lock") OR git.fileExists("package-lock.json")

And/or a rule that makes sure there are unit tests enabled:


Finally, you could write a rule to make sure projects have a standard linter:

git.fileExists(".prettierrc.json") OR git.fileExists(".eslintrc.js")
Git vulnerabilities

Check the number of vulnerabilities for an entity's associated repository.

You can filter by severity (by default searches by all severities) or source (by default only searches GitHub security advisories).

When using the GitHub Advanced Security source, severities displayed in the UI may not match severities returned by the API.

Definition: git.numOfVulnerabilities()


A security-focused Scorecard will likely include a rule making sure there are no Git vulnerabilities:

git.numOfVulnerabilities() == 0

You can use Scorecard levels to stratify vulnerabilities by risk. An initial level might make sure there are no critical vulnerabilities:

git.numOfVulnerabilities(severity=["CRITICAL"]) == 0

While a higher level might make sure there are no vulnerability warnings:

git.numOfVulnerabilities(severity=["WARNING"]) == 0
Has Cortex YAML

Check if a repository has a valid cortex.yaml file checked in at the root directory (when GitOps is enabled).

Definition: git.hasCortexYaml()


If you're using a Scorecard to track a migration from Cortex UI to GitOps, you can use this rule to make sure entities are set up for GitOps management of entity descriptors:

git.hasCortexYaml() == true
Has Git repository set

Checks if an entity has a registered Git repository.

Definition: git (==/!=) null


A Scorecard focused on best practices or standards will likely include a rule in its first level making sure a Git repository is set up:

git != null

If an entity is failing this rule, it can indicate broader issues with the integration or explain why an entity isn't functioning as expected.

Last commit details

Provides last commit details.

  • Date
  • Message
  • SHA
  • URL
  • Username

Definition: git.lastCommit()


One of the first rules you might write for a Scorecard focused on development maturity or security is one validating that the last commit was within the last month:

git.lastCommit().freshness < duration("P1M")

As counterintuitive as it may seem, services that are committed too infrequently are actually at more risk. People who are familiar with the service may leave a team, institutional knowledge accumulates, and from a technical standpoint, the service may be running outdated versions of your platform tooling.

Depending on best practices at your organization, you may want to confirm entities are updated within a week:

git.lastCommit().freshness < duration("P7D")

Confirming whether a service was updated within the last week can help team members catch outdated code sooner. Plus, if there is a security issue, you can quickly determine which services have or have not been updated to patch the vulnerability.

Pull requests

Lists pull requests opened during a defined lookback period.

  • Approval date
  • Author
  • Date closed
  • Date opened
  • First review date
  • Last updated
  • Number of commits
  • Number of lines added
  • Number of lines deleted
  • Organization
  • Repository
  • Source
  • Status
  • URL

Definition: git.pullRequests()


You can use the git.pullRequests() query to find entities that have a small number of pull requests opened in the last two weeks:

git.pullRequests(lookback=duration("P14D")).length < 3

This can highlight entities that haven't been updated recently, which may be especially useful when entities have to be updated to address a vulnerability.

Recency of last commit

Calculates the duration (with granularity of days) between Scorecard evaluation and the date of the last commit from an entity's Git repository.

Definition: git.lastCommit().freshness


For a project standards Scorecard, you can write a rule to make sure that the last commit was within the last two weeks:

git.lastCommit().freshness < duration("P2W")

List reviews left during a defined lookback period.

  • Organization
  • Repository
  • Review date
  • Reviewer



A development maturity Scorecard might use the expression to make sure that there is a rigorous review process in place before changes are implemented:"P7D")).length > 25

This rule makes sure that there are more than 25 reviews left in the last week.

Search repository files

Find all instances of code search query from within a repository.

  • Name
  • Path
  • SHA
  • URL

Definition: git.codeSearch()


You can use the git.codeSearch() expression to query for entities that have certain components, like automated and embedded unit/functional tests in CI pipelines via standard tooling:

git.codeSearch(path = ".github/workflows", fileExtension = "yaml").length >= 1 and git.codeSearch(query = "pytest", inFile = true, path = ".github/workflows", fileExtension = "yaml").length = git.codeSearch(path = ".github/workflows", fileExtension = "yaml").length == 1
Team details

List of teams for each entity.

Definition: ownership.teams()


For an operational maturity Scorecard, you might want to make sure entities are associated with a GitHub team. Let's say our GitHub team is called "Example team."

ownership.teams().any(team => team.tag.matchesIn("[Ee]xample")) == true

Entities passing this rule would be linked to a team that has a tag containing "example" or "Example."

Top repository language

Find top used language for a repository, if available.

Definition: git.topLanguage()


Let's say the primary language developers should be using is Kotlin. You can write a rule to make sure that the top language associated with entities is Kotlin:

git.topLanguage() == "kotlin"

You can also use this expression to query for entities that don't have Kotlin as the top language to identify those that need to be updated:

git.topLanguage() != "kotlin"
Workflow runs

Get workflow runs meeting given filter criteria, including conclusions, statuses, and a lookback period.

  • Conclusion
  • Name
  • Run started at
  • Run time
  • Run updated at
  • Status



The lookback period specifies a duration for which returned runs should be created within, defaulting to a period of 3 days.

  • The runTime of the WorkflowRun object represents the difference between runStartedAt and runUpdatedAt times in seconds.

Definition: git.workflowRuns()


To make sure an entity has had a successful workflow run within the last two weeks, you can write a rule like:

git.workflowRuns(conclusions=["SUCCESS"], statuses=["COMPLETED"], lookback=duration("P14D")).length > 0

This rule is checking for GitHub workflow runs with a SUCCESS conclusion and COMPLETED status during a 14-day lookback window.

External repositories

By default, each GitHub rule is evaluated on the repository defined in a given entity descriptor. If the base path parameter has been set, CQL rules will automatically scope to the base path subdirectory.

To evaluate the rule for a service for an external repository, pass the repo identifier in the git(repoIdentifier: Text) command (e.g. git("github:org/repositoryName")).

This can be combined with other CQL rules. For example, a rule based on a dynamic external repository with custom data would be git("github:" + custom("my-custom-repo")).fileExists("").

Dev homepage

The GitHub integration enables Cortex to pull information about pull requests into the Developer homepage. You can find your open pull requests under the My open PRs tab and any pull requests assigned to you for review under the Assigned reviews tab.

Eng Intelligence

The Eng Intelligence tool also uses pull request data from GitHub to generate metrics:

  • Average PR open to close time
  • Avg time to first review
  • Avg time to approval
  • Avg deploys/week
  • PRs opened
  • Weekly PRs merged
  • Avg PRs reviewed/week
  • Avg commits per PR

You can read more about how Eng Intelligence tracks metrics for teams and users in the Eng Intelligence walkthrough.

Background sync

Cortex conducts a background sync of GitHub identities every day at 10 a.m. UTC.

FAQs and troubleshooting

I received "{"message":"Not Found", "documentation_url":""}".

If you've set up multiple GitHub accounts/organizations, Cortex will not be able to identify the correct one unless the alias variable is defined.

What if I have multiple email addresses set in my GitHub account?

Cortex will only detect the primary email address associated with your GitHub account if it is public.

If Cortex is not correctly pulling in user emails, ensure the given user(s) have allowed their email address to be public. Make sure the "Keep my email address private" setting is unchecked in the user's personal GitHub settings.

My ownership isn't being automatically mapped through GitHub.

If the email address associated with your Cortex account is not the same as your GitHub email address, you need to add your Cortex email address to the Public email dropdown in GitHub settings.

Github OAuth, which you can configure in Cortex user settings, allows you to link your GitHub username with your Cortex account, even if you don't have a public email set up on GitHub.

Still need help?

The following are all the ways to get assistance from our customer engineering team. Please use the option that is best for your users:

  • Email:, or open a support ticket in the in app Resource Center
  • Chat: Available in the Resource Center
  • Slack: Users with a connected Slack channel will have a workflow added to their account. From here, you can either @CortexTechnicalSupport or add a :ticket: reaction to a question in Slack, and the team will respond directly.

Don’t have a Slack channel? Talk with your customer success manager.