SecretSync
Local-first CLI that pushes secrets from your vault-backed environment into deployments using a checked-in YAML file — without pasting values into Slack or dashboards.
⚠️ Disclaimer: This software is pre-alpha and under development. Use with caution.
Motivation
Vaults solve storage, rotation, access control, and audit. The gap is delivery: getting the same secret into GitHub Actions, Vercel, and SST without paste-into-Slack, clipboard history, or hand-copying the same key into three dashboards — and without half-rotated deploys when one destination is forgotten.
SecretSync is a thin CLI for that gap. You declare routing in a checked-in YAML file (reviewable in PRs; plaintext stays out of git), inject values from your vault into the process environment (op run, Doppler, etc.), and push. Destination quirks — GitHub repo/environment/org scopes, Vercel deployment targets, SST stages — stay behind connectors so the config stays simple.
Full secrets platforms (Infisical and similar) can do this and more, but they are overkill when you already trust a vault and only need to say which names land where. Plaintext should only move through process memory, authenticated provider APIs, or one-shot env injection — never config, plans, logs, or temp files.
Quickstart
## Install with uv
uv tool install secretsync-cli
# Scaffold config + 1Password-style env template
secretsync init
Setup
# .env.tpl — vault refs injected into the process env (e.g. via `op run`)
GITHUB_TOKEN="op://vault/github/token" # connector auth
API_KEY="op://vault/app/api-key" # secret value SecretSync reads
# secretsync.yaml
version: 1
changeDetection: always-write
# Logical secret ids → which process env var holds the plaintext value
secrets:
apiKey:
env: API_KEY # reads os.environ["API_KEY"]
# Non-secret configuration (same shape; never written to a secure store as a secret)
variables:
logLevel:
env: LOG_LEVEL
# Named bundles of secrets/variables to ship together
sets:
production:
include: [apiKey, logLevel]
# Where values can be written (connector + auth)
destinations:
github:
connector: github-actions
# Either repository: owner/name …
repository: owner/repo
# … or organization: + repository: name (not both with owner/name)
# organization: owner
# repository: repo
auth:
tokenEnv: GITHUB_TOKEN # reads os.environ["GITHUB_TOKEN"] for API auth
# Concrete pushes: which set → which destination, under what remote names
deployments:
- name: github-production
set: production
destination: github
scope:
kind: environment
environment: production
secrets:
# logical id → remote secret name in GitHub Actions
apiKey: API_KEY
variables:
# logical id → remote variable name (Actions Variables API)
logLevel: LOG_LEVEL
# Org secrets/variables: org is destination.organization, or the owner of
# repository (acme from acme/web). Org-only destinations work for this scope.
# Token needs admin:org (classic PAT). visibility is required.
- name: github-org
set: production
destination: github
scope:
kind: organization
visibility: private # required: all | private | selected (+ selectedRepositoryIds)
secrets:
apiKey: API_KEY
variables:
logLevel: LOG_LEVEL
Kind is declared once under secrets or variables. Connectors map kind to the provider primitive (GitHub secrets vs variables APIs; Vercel type: sensitive vs encrypted). SST supports secrets only — non-secret SST config belongs in code as Linkables.
Breaking: Vercel
scope.sensitiveis removed. Put sensitive values underdeployment.secretsand plaintext underdeployment.variables.Breaking: Vercel destinations require
teamId. Project env deployments needscope.kind: environment(and destinationproject). Team shared env usesscope.kind: shared-environmentwith optionalscope.projects.
Vercel destination modes (selected by scope.kind):
destinations:
vercel:
connector: vercel
teamId: team_xyz # required
project: prj_abc # optional; required for kind: environment
auth:
tokenEnv: VERCEL_TOKEN
deployments:
- name: vercel-production
set: production
destination: vercel
scope:
kind: environment
targets: [production]
secrets:
apiKey: API_KEY
- name: vercel-shared
set: production
destination: vercel
scope:
kind: shared-environment
targets: [production]
projects: [prj_abc, prj_def] # optional link set
secrets:
sharedSecret: SHARED_SECRET
Deploy secrets
# After editing secretsync.yaml (repo/project/stage names) and .env.tpl (secret references),
# then inject env and run command:
# Example with 1Password CLI:
# Run a health check to ensure secretsync is authenticated
op run --env-file=.env.tpl -- secretsync health
# Validate and plan a config
op run --env-file=.env.tpl -- secretsync validate ## makes sure the config is valid
op run --env-file=.env.tpl -- secretsync plan ## shows you what changes will be made
## Apply changes into your destination
op run --env-file=.env.tpl -- secretsync apply ## applies the changes
You can also target a single deployment or destination
op run --env-file=.env.tpl -- secretsync --deployment github-production plan
op run --env-file=.env.tpl -- secretsync --deployment github-production apply --yes
op run --env-file=.env.tpl -- secretsync --destination github plan
--deployment and --destination are repeatable; when both are set, only the intersection runs.
👉 While this example uses the 1Password CLI and vault,
secretsyncwith any system that can securely set secrets in your environment (Doppler, mise, Infisical etc.)
Commands
| Command | Purpose |
|---|---|
secretsync init |
Initialize secretsync.yaml + .env.tpl template |
secretsync validate |
Check config + required env presence |
secretsync plan |
Plan the necessary changes (--prune lists remotes and plans deletes) |
secretsync apply |
Resolve the plan and write secrets (--prune also deletes orphaned secrets) |
secretsync health |
Auth/reachability checks (skips unset tokens) |
secretsync ui |
Interactive Textual review/apply |
secretsync connectors |
List built-in connectors |
Useful flags: --config, --format json, --verbose, --quiet, --deployment, --destination, --prune.
With --prune, SecretSync lists remote names at plan time (secrets and variables separately) and treats YAML as the full desired inventory for each destination scope + kind — remote entries not listed in the config are planned for deletion. Without --prune, apply is put-only.
For Vercel, a remote env var belongs to a deployment only when its target set exactly matches scope.targets (and, for shared env, scope.projects). A multi-target remote such as [production, preview] is owned by a deployment that declares that same multi-target scope — not by a production-only or preview-only sibling.
Supported Destinations
We currently support these destinations.
Audit
secretsync writes an audit trail under .secretsync/audit.log so you can keep track of what changed when. The .secretsync folder can safely be checked into source control.
License
MIT — see LICENSE.
Metadata
Release files for secretsync-cli 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| secretsync_cli-0.1.2.tar.gz | 57.8 kB | Without provenance | Details for secretsync_cli-0.1.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| secretsync_cli-0.1.2-py3-none-any.whl | Python 3 | none | any | Details for secretsync_cli-0.1.2-py3-none-any.whl |
Total release size: 133.7 kB
Release files / secretsync_cli-0.1.2.tar.gz
| Download URL | secretsync_cli-0.1.2.tar.gz |
|---|---|
| Size | 57.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92cbe4071197a1f6e4f1ad0e526533a4208cef6e239aa58636c00a095cb7cef3
|
|
BLAKE2b-256 checksum How to use checksums |
4e7d9ef290d166ea3bbe2cc8d6348851874eaa26fad5eadec116e3f5cc88ee27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / secretsync_cli-0.1.2-py3-none-any.whl
| Download URL | secretsync_cli-0.1.2-py3-none-any.whl |
|---|---|
| Size | 75.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dfad0e0a789bf4952f0fea184fca02c829dbc36b4777c0bb470a25b14b14ca10
|
|
BLAKE2b-256 checksum How to use checksums |
f740a94d43948a6c2a0522cc167b273763df3ecd2cf52f00dfbc538908122414
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for secretsync-cli 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.