Home / Alt manpages / gh-secret-list(1)

  • gh-secret-list(1)
  • User command
  • linux

Audit GitHub Secret Scopes with gh secret list

Before you rotate anything or hand a repository to someone else, you want to know which secrets it holds and at what level. gh secret list gives you a read-only view of secret names visible at repository, environment, organisation or user level. It never prints secret values, but names and metadata can still disclose how a project is built.

  • Time: about ten minutes.
  • You need: GitHub CLI, an authenticated account with permission to see the target scope, and a repository or organisation to inspect.
  • Safe: none of the commands needs sudo, and nothing here creates, changes or deletes a secret.

1. Confirm the local command

Check the executable and its version before copying examples. The installed help is the contract for the machine you are using:

$ command -v gh
/home/linuxbrew/.linuxbrew/bin/gh
$ gh --version
gh version 2.87.3 (2026-02-23)
$ gh secret list --help

On this host, the executable is GitHub CLI 2.87.3 from Linuxbrew. The Debian package database reports a separate gh package version, so do not use that record as the version of the command selected by PATH. The local help lists the applications actions, codespaces and dependabot. Read gh secret list --help again if your version differs.

Checkpoint

Continue only when command -v gh points to the executable you meant to run and the help output looks familiar.

2. Select the repository scope

With no scope flag, gh secret list lists secrets for the current repository. Make the target explicit with --repo when you are outside a checkout or want to avoid an accidental repository context:

$ gh secret list --repo OWNER/REPO
NAME                 UPDATED
DEPLOY_TOKEN         2026-09-20
PACKAGE_SIGNING_KEY  2026-08-11

The names and dates above are illustrative, and your output depends on the repository and your permissions. Replace OWNER/REPO with an actual value such as acme/inventory.

The repository level is the default. It covers secrets made available to GitHub Actions runs or Dependabot in that repository.

Warning

Do not paste a real token or secret value into the command line. If the command says you are not logged in, use your normal approved GitHub authentication process, not a token in shell history.

3. Inspect an environment without confusing it with the repository

An environment is a separate secret level within a repository. Pass its exact name with --env, and keep --repo explicit:

$ gh secret list --repo OWNER/REPO --env production
NAME                 UPDATED
DEPLOY_TOKEN         2026-09-18
PACKAGE_SIGNING_KEY  2026-08-11

An environment listing is not a second view of every repository secret. It covers only secrets associated with that deployment environment. Names can be similar at both levels, so record the scope alongside any audit result.

Checkpoint

Confirm that the repository and environment names in the command match the deployment you meant to inspect. An empty list can be a correct result, not proof that the repository has no secrets anywhere.

4. Choose organisation or user scope

Use --org for organisation secrets and --user for secrets available to your user. These are alternative scopes, not options to combine with the repository scope:

$ gh secret list --org ORG
NAME             VISIBILITY
CI_READ_TOKEN    all

$ gh secret list --user
NAME             UPDATED
CODESPACE_KEY    2026-09-01

Replace ORG with the organisation login. The local command describes organisation secrets as available to Actions runs, Dependabot or Codespaces within that organisation, while user secrets are available to your Codespaces. GitHub permissions and policy decide which rows you can see.

Tip

A successful organisation listing is not permission to use those secrets. Listing is an audit operation. It does not reveal values, and it does not test whether a workflow can consume a secret.

5. Narrow the application and output

When a scope supports more than one GitHub application, use --app to narrow the listing. The local help accepts actions, codespaces and dependabot:

$ gh secret list --repo OWNER/REPO --app actions
NAME          UPDATED
DEPLOY_TOKEN  2026-09-20

For scripts, request stable JSON fields rather than parsing the aligned table:

$ gh secret list --repo OWNER/REPO --json name,updatedAt
[{"name":"DEPLOY_TOKEN","updatedAt":"2026-09-20T14:32:00Z"}]

The JSON fields in this installation are name, numSelectedRepos, selectedReposURL, updatedAt and visibility. Filter the JSON with --jq when you need a small report:

$ gh secret list --repo OWNER/REPO --json name,visibility --jq '.[] | [.name, .visibility] | @tsv'
DEPLOY_TOKEN	all

Templates are another option, using --template and the Go template syntax documented by gh help formatting. Keep output restricted to metadata.

Warning

Avoid writing a full listing to a shared log if the names themselves reveal sensitive project structure.

6. Handle failures without escalating blindly

Rerun the exact command with --help and check the target scope. A missing repository context, misspelled environment, insufficient permission or expired authentication can all prevent a listing. sudo cannot fix any of those GitHub-side conditions.

If a script consumes the result, make failure visible instead of treating empty output as success:

if ! output=$(gh secret list --repo OWNER/REPO --json name,updatedAt); then
    printf '%s\n' 'secret listing failed' >&2
    exit 1
fi
printf '%s\n' "$output"

This captures metadata only and leaves GitHub unchanged.

Recovery

If you accidentally expose secret names in a log, remove or restrict that log according to your organisation's incident process. There is no undo for a listing because it makes no remote state change.

Done means

  • Right binary. You checked the executable and version actually selected by PATH.
  • One scope. You chose repository, environment, organisation or user, explicitly.
  • Explicit target. You used --repo, --env or --org where the target was not obvious.
  • JSON over tables. You used --json and, where useful, --jq instead of brittle table parsing.
  • Names are sensitive. You treated secret names and metadata as sensitive and never expected values from this command.
  • Failures visible. You checked the command status so permission or authentication failures cannot look like an empty inventory.