GitHub Secrets Manager
github-secrets-manager.py manages local plaintext copies of GitHub Actions
repository secrets and shared organization secret values, then syncs them to
GitHub by using the authenticated GitHub CLI (gh).
GitHub does not allow reading secret values after they are created. This script solves that by keeping local copies of the values, plus local creation and update timestamps, so repository secrets can be recreated or updated from your machine. For organizations, one local shared secret can be applied to every repository that already has a remote secret with the same name.
Requirements
- Python 3
- GitHub CLI (
gh) - An authenticated GitHub CLI session:
gh auth login
The script manages GitHub Actions repository secrets. It does not manage environment, organization-level, Dependabot, Codespaces, or Agents secrets.
Local Storage
By default, local secrets are stored under:
~/.github-local-secrets
You can change that location with --secrets-dir:
./github-secrets-manager.py --secrets-dir ~/secure/github-secrets local list octocat/hello-world
Repository-specific local secrets are stored using this layout:
SECRETS_DIR/
OWNER/
REPO/
metadata.json
secrets/
SECRET_NAME.secret
Organization shared local secrets are stored using this layout:
SECRETS_DIR/
organizations/
ORG/
metadata.json
secrets/
SECRET_NAME.secret
Each local secret value is stored as a plaintext UTF-8 file. Secret files are
written with 0600 permissions. Protect the configured secrets directory as
sensitive material and do not commit it to Git.
metadata.json stores local metadata for each secret:
created_atupdated_at- relative local file path
Usage
./github-secrets-manager.py [--secrets-dir DIR] {local,repo,org} ...
Use --help at any level to see the available commands:
./github-secrets-manager.py --help
./github-secrets-manager.py local --help
./github-secrets-manager.py repo --help
./github-secrets-manager.py org --help
./github-secrets-manager.py org local --help
Local Commands
Create a local secret from standard input:
printf 'my-secret-value\n' | ./github-secrets-manager.py local create octocat/hello-world API_KEY --stdin
Create a local multiline secret:
./github-secrets-manager.py local create octocat/hello-world PRIVATE_KEY --stdin <<'EOF'
-----BEGIN PRIVATE KEY-----
line 1
line 2
-----END PRIVATE KEY-----
EOF
Create a local secret from a file:
./github-secrets-manager.py local create octocat/hello-world NPM_TOKEN --value-file ./npm-token.txt
Update or create a local secret:
printf 'new-value\n' | ./github-secrets-manager.py local update octocat/hello-world API_KEY --stdin
List local secrets and their metadata:
./github-secrets-manager.py local list octocat/hello-world
Show a local secret value:
./github-secrets-manager.py local show octocat/hello-world API_KEY
Delete a local secret:
./github-secrets-manager.py local delete octocat/hello-world API_KEY
Repository Commands
List GitHub Actions secrets configured in a repository:
./github-secrets-manager.py repo list octocat/hello-world
GitHub only returns secret names and update metadata. It never returns secret values.
Create or update one GitHub secret from its local value:
./github-secrets-manager.py repo create octocat/hello-world API_KEY
Preview that operation without writing to GitHub:
./github-secrets-manager.py repo create octocat/hello-world API_KEY --dry-run
Update selected GitHub secrets from local values:
./github-secrets-manager.py repo update octocat/hello-world --secrets API_KEY,NPM_TOKEN
Update all remote secrets that have matching local values:
./github-secrets-manager.py repo update octocat/hello-world --all
Preview repository updates without writing to GitHub:
./github-secrets-manager.py repo update octocat/hello-world --all --dry-run
Compare local and remote secrets:
./github-secrets-manager.py repo sync octocat/hello-world
The sync report uses these statuses:
remote-and-local: the secret exists in GitHub and locally.missing-local: the secret exists in GitHub but no local value is available.local-only: the secret exists locally but not in GitHub.
Remote secrets that are missing locally are reported only. They are not deleted or overwritten.
Organization Commands
Organization local secrets are stored once per organization and can be synced to repositories in that organization. The sync still writes repository-level GitHub Actions secrets; it does not create GitHub organization-level secrets.
Create an organization local secret from standard input:
printf 'shared-token-value\n' | ./github-secrets-manager.py org local create my-organization API_TOKEN --stdin
Create an organization local multiline secret:
./github-secrets-manager.py org local create my-organization PRIVATE_KEY --stdin <<'EOF'
-----BEGIN PRIVATE KEY-----
line 1
line 2
-----END PRIVATE KEY-----
EOF
Create an organization local secret from a file:
./github-secrets-manager.py org local create my-organization NPM_TOKEN --value-file ./npm-token.txt
Update or create an organization local secret:
printf 'new-shared-value\n' | ./github-secrets-manager.py org local update my-organization API_TOKEN --stdin
List organization local secrets:
./github-secrets-manager.py org local list my-organization
Show an organization local secret value:
./github-secrets-manager.py org local show my-organization API_TOKEN
Delete an organization local secret:
./github-secrets-manager.py org local delete my-organization API_TOKEN
Update matching repository secrets across an organization:
./github-secrets-manager.py org sync my-organization
The command lists repositories in the organization and updates only remote repository secrets that have matching organization local values. If a repository does not already have a remote secret with the same name, the script skips it.
Sync only selected organization local secrets:
./github-secrets-manager.py org sync my-organization --secrets API_TOKEN,NPM_TOKEN
Preview organization-wide updates without writing to GitHub:
./github-secrets-manager.py org sync my-organization --dry-run
Include archived repositories:
./github-secrets-manager.py org sync my-organization --include-archived
Use a custom local secrets directory for an organization sync:
./github-secrets-manager.py \
--secrets-dir ~/secure/github-secrets \
org sync my-organization \
--dry-run
Example Workflow
Create shared local secrets for an organization:
printf 'token-value\n' | ./github-secrets-manager.py org local create my-org API_TOKEN --stdin
./github-secrets-manager.py org local create my-org PRIVATE_KEY --value-file ./private-key.pem
Review what is stored locally:
./github-secrets-manager.py org local list my-org
Preview the organization-wide GitHub update:
./github-secrets-manager.py org sync my-org --dry-run
Apply the shared values to matching repository secrets:
./github-secrets-manager.py org sync my-org
Apply only one shared value:
./github-secrets-manager.py org sync my-org --secrets API_TOKEN
Use repository-local commands for repository-specific exceptions that should not come from the shared organization store.
Safety Notes
- Local secret values are plaintext. Keep the secrets directory private.
- Do not store the secrets directory inside a Git repository.
- Secret values are sent to
gh secret setthrough standard input, not through command-line arguments. - Use
--dry-runbefore repository or organization updates when you want to inspect what would change. - The script validates repository names as
OWNER/REPOand secret names as uppercase/underscore-compatible identifiers, though lowercase letters are also accepted.
Metadata
Release files for github-secrets-manager 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| github_secrets_manager-0.0.1.tar.gz | 10.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| github_secrets_manager-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 19.3 kB
Release files / github_secrets_manager-0.0.1.tar.gz
| Download URL | github_secrets_manager-0.0.1.tar.gz |
|---|---|
| Size | 10.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f635df6920a3a231e9529ddd62df31ee6e10e1443375b1ffb39b822b512e4d3
|
|
BLAKE2b-256 checksum How to use checksums |
6053ef95334316ff75092f71c84c95830deaf4489522077114e9023548614405
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / github_secrets_manager-0.0.1-py3-none-any.whl
| Download URL | github_secrets_manager-0.0.1-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5a9753d8f8de93d9ac2518394b51247b77326bb83f0e66901d7a94b050e2ee8
|
|
BLAKE2b-256 checksum How to use checksums |
0e3ebe9f8713891e795b6fd00c196c00c744f10026f45583fcf08d3a5db3d4fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|