SecretShift is a CLI tool for migrating and syncing secrets and environment variables between providers. Move secrets from GitHub, GitLab, HashiCorp Vault, etcd, Kubernetes, or local files to any of those destinations.
- 7 provider types: GitHub, GitLab, Vault, etcd, Kubernetes, local file (source + destination)
- Flexible processing: Filter by name regex or type, add prefixes/suffixes to secret names
- Multiple run modes: One-shot, periodic interval, cron-scheduled, or long-running server
- Server mode: HTTP health endpoints (
/healthz,/readyz,/status) for Kubernetes deployments - Dry-run support: Preview changes without writing to the destination
- Encrypted file I/O: Optional AES-256-GCM encryption for file sources and destinations
- Conflict handling: Replace, skip, or report existing secrets at the destination
- Per-step environment variables: Separate
SECRET_SHIFT_SRC_*andSECRET_SHIFT_DST_*for source and destination credentials - Helm chart: Deploy to Kubernetes as a Deployment (periodic/server) or CronJob (one-shot)
brew tap PapaDanielVi/tap
brew install secret-shiftgo install github.com/PapaDanielVi/secret-shift@latestgo build -o secret-shift .Download the binary for your platform from the releases page.
Create a secret-shift.json:
{
"source": {
"type": "github",
"repo": "owner/repo",
"token": "ghp_xxx"
},
"process": {
"add_prefix": "PROD_",
"include_regex": "^DB_"
},
"destination": {
"type": "vault",
"vault_address": "https://vault.example.com",
"vault_path": "myapp/config",
"token": "hvs_xxx"
}
}Run the sync:
secret-shift syncAll config keys can be set via environment variables. Source fields use the SECRET_SHIFT_SRC_ prefix and destination fields use SECRET_SHIFT_DST_:
SECRET_SHIFT_SRC_TYPE=github \
SECRET_SHIFT_SRC_GITHUB_TOKEN=ghp_xxx \
SECRET_SHIFT_DST_TYPE=file \
SECRET_SHIFT_DST_PATH=./secrets.json \
secret-shift syncThis per-step convention means you can use different credentials for source and destination, e.g. SECRET_SHIFT_SRC_GITHUB_TOKEN and SECRET_SHIFT_DST_GITHUB_TOKEN.
Execute a sync pipeline.
| Flag | Default | Description |
|---|---|---|
-c, --config |
./secret-shift.json |
Path to config file |
--periodically |
false |
Run in a loop |
--frequency |
5m |
Interval between syncs (e.g. 1m, 1h) |
--cron |
Cron expression (e.g. */5 * * * *) |
|
--dry-run |
false |
Simulate sync without writing to destination |
--server |
false |
Start HTTP server with health endpoints + periodic sync |
--health-port |
8080 |
Port for health HTTP server |
Print the current version.
| Source | What it reads |
|---|---|
| github | GitHub Actions secrets + environment variables |
| gitlab | GitLab project-level CI/CD variables |
| vault | HashiCorp Vault KV v2 secrets |
| etcd | etcd key-value pairs under a prefix |
| kubernetes | K8s Secrets + ConfigMaps (by name, label, or namespace) |
| file | Local JSON or YAML key-value file (optionally encrypted) |
| Destination | What it writes | Notes |
|---|---|---|
| file | Local JSON or YAML file | Optional AES-256-GCM encryption |
| github | GitHub Actions secrets + environment variables | RSA-OAEP encrypted |
| gitlab | GitLab project-level CI/CD variables | |
| vault | HashiCorp Vault KV v2 | Single KV entry |
| etcd | etcd key-value store | One key per secret |
| kubernetes | K8s Secrets + ConfigMaps | Routes by secret type |
When running with --server, secret-shift starts an HTTP server that exposes:
| Endpoint | Description |
|---|---|
GET /healthz |
Liveness probe — always returns {"status":"ok"} |
GET /readyz |
Readiness probe — returns ready after first successful sync, not_ready if no sync has completed or the last sync failed |
GET /status |
Detailed status including sync count, error count, last sync time |
In server mode, syncs run periodically in the background (default every 5 minutes).
Set the url field to your GitHub Enterprise API endpoint:
{
"source": {
"type": "github",
"repo": "owner/repo",
"url": "https://git.mycompany.com/api/v3",
"token": "ghp_xxx"
}
}Set the url field to your GitLab instance:
{
"source": {
"type": "gitlab",
"project_id": "123",
"url": "https://gitlab.mycompany.com",
"token": "glpat-xxx"
}
}The processor runs between source and destination:
- Type filtering:
include_types/exclude_typesfilter by"env"or"secret" - Name filtering:
include_regex/exclude_regexfilter by secret name - Name transformation:
add_prefixandadd_suffixmodify secret names
For destinations that support it (GitHub, GitLab):
| Strategy | Behavior |
|---|---|
replace |
Overwrite existing secrets (default) |
skip |
Silently skip existing secrets |
report |
Print conflict info and skip |
The config system supports separate environment variables for source and destination:
| Variable Pattern | Example |
|---|---|
SECRET_SHIFT_SRC_TYPE |
Source provider type |
SECRET_SHIFT_SRC_GITHUB_TOKEN |
GitHub token for source |
SECRET_SHIFT_SRC_GITLAB_TOKEN |
GitLab token for source |
SECRET_SHIFT_SRC_VAULT_TOKEN |
Vault token for source |
SECRET_SHIFT_SRC_PATH |
File path for file source |
SECRET_SHIFT_SRC_KUBE_NAMESPACE |
K8s namespace for source |
SECRET_SHIFT_DST_TYPE |
Destination provider type |
SECRET_SHIFT_DST_GITHUB_TOKEN |
GitHub token for destination |
SECRET_SHIFT_DST_GITLAB_TOKEN |
GitLab token for destination |
SECRET_SHIFT_DST_VAULT_TOKEN |
Vault token for destination |
SECRET_SHIFT_DST_PATH |
File path for file destination |
SECRET_SHIFT_DST_KUBE_NAMESPACE |
K8s namespace for destination |
SECRET_SHIFT_DRY_RUN |
Enable dry-run mode |
Token resolution order: direct config value → token_env field → SECRET_SHIFT_SRC/DST_<PROVIDER>_TOKEN env var.
See the examples/ directory for ready-to-use configurations for every source→destination combination (49 total), each with a config.json and README.md.
A Helm chart is available at charts/secret-shift/. It supports three modes:
| Mode | Description | Kubernetes Resource |
|---|---|---|
server |
HTTP health endpoints + periodic sync | Deployment |
periodic |
Periodic sync loop | Deployment |
one-shot |
Single sync run on a schedule | CronJob |
helm install secret-shift charts/secret-shift/ --set mode=serversecret-shift/
cmd/ # CLI commands (root, sync, version)
internal/
config/ # Config loading, validation, env resolution
pipeline/ # Sync pipeline (source → process → destination)
provider/ # Provider interfaces + registry
github/ # GitHub source + destination
gitlab/ # GitLab source + destination
vault/ # HashiCorp Vault source + destination
etcd/ # etcd source + destination
kubernetes/ # Kubernetes source + destination
file/ # File source + destination (JSON/YAML, encrypted)
server/ # HTTP health server (/healthz, /readyz, /status)
charts/secret-shift/ # Helm chart for Kubernetes deployment
examples/ # 49 src→dst example configurations
- CLI: Cobra + Viper
- APIs: go-github, GitLab client, Vault API, etcd client, Kubernetes client-go
- Scheduling: cron
- HTTP Server: Standard library
net/http
MIT