secrets-source-of-truth
Why It Exists
A portfolio of services each needs the same kinds of secrets (database URLs, signing keys, API tokens) in several
places: a hosting platform's env, a parameter store, CI, and a local .env file. Without a rule, the "real" value
ends up wherever someone last edited it, and the copies drift silently.
The rule this skill encodes: 1Password is the single human-authored source of truth; every platform is a runtime
mirror, filled from 1Password by an env-sync CLI (diff, push, validate) driven by a per-repo
secrets.manifest.yml. The skill answers one question: where does secret X live, and how do I get it into the
platform that needs it? How an app then reads the variable is a different skill (runtime-env-config).
It was hardened after a real credential-exfiltration finding in its CapRover adapter (below).
What It Does
- A naming convention for 1Password items,
<repo>/<service>/<env>, split into anapp-secretsvault for runtime values and aninfra-accessvault for credentials that reach a store. - The
env-syncCLI and one adapter per platform (SSM, Vercel, Render, CapRover, GitHub Actions, dotenv), all implementing the samereadCurrent/pushValues/describeTargetinterface. - A decision tree for "where does this secret live", a rotation procedure, and a capture procedure for secrets that only exist on a platform today.
- Invariants that keep it honest, for example: never hand-edit a platform value; never pass
--yesto override a confirmation refusal; never print a secret, only a fingerprint.
How To Use It
Run from the consuming repo's root, where secrets.manifest.yml lives:
env-sync validate --service <name> --env <env> # item exists, every target reachable
env-sync diff --service <name> --env <env> # compare 1Password to the mirrors
env-sync push --service <name> --env <env> # preview first; add --apply to write
If a manifest has a caprover target, export the allowlist first:
export CAPROVER_ADMIN_PASSWORD=... # from the infra-access vault
export CAPROVER_ALLOWED_DOMAINS=captain.example.com # comma-separate multiple
Gotchas & Invariants
A manifest must not choose where the admin password goes
The CapRover adapter logs in by POSTing CAPROVER_ADMIN_PASSWORD to https://<captainDomain>/api/v2/login, and
captainDomain comes from the manifest, which is repo content a reviewer may never read. A hostile or compromised
manifest on a default branch could therefore redirect the full admin password to a host the attacker controls.
Two changes close it, and both are breaking for existing local setups:
- Fail closed on an allowlist. Before the password is even read,
captainDomainmust be a bare hostname (letters, digits,.and-; no scheme, port, path or@) that exactly matches, case-insensitively, an entry ofCAPROVER_ALLOWED_DOMAINS. Unset, empty or blank refuses. No wildcards, no suffix matching. The error names the variable and shows the export line, so a sudden failure explains itself. - Refuse redirects.
fetchfollows redirects by default, and a 307 or 308 re-sends the POST body and custom headers to the new location. The allowlist only vets the first host, so every CapRover request is made withredirect: "error". A captain that legitimately redirects (http to https, apex to www) fails withcaprover login failed: fetch failed (unexpected redirect): list its final hostname.
The standalone Go and TypeScript ports of env-sync (0.4.0 and later) enforce the same rules.
Every secret-shaped log line is a bug
Error messages are built only from an HTTP status and the response's description, never from a request body or a
response data field, which is where env var values live. This is why a failed push can be pasted into a ticket.
How it ships
This skill is not an npm package. It reaches you through the repository's git-based channels (the skills CLI with
--skill skills/secrets-source-of-truth, or the plugin marketplace entry), so update your installed copy to pick up a
fix.
Related Skills
- runtime-env-config: how an app loads the variables once they exist.
- local-dev-stack: the local process and database side.
- prod-deploy: the prod half of an ENV-selected stack, including CapRover.
- docker-ecr-caprover-migration: moving a service onto CapRover.
- secret-scan-onboarding: the CI gate that scans build output for leaked secrets.