Problem
This repo already has a working "encrypt for a set of recipients, publish, decrypt on demand" mechanism (vaults/*.yml → encrypt-env action → .sops.env release assets → deploy/vault.py's decrypt_env) - but it's scoped narrowly to one use case: a deploy target's own age key decrypting an app's env during a deploy.
Meanwhile there's a real, recurring problem this same mechanism could solve but currently doesn't: sharing a credential (e.g. a Telegram bot token) across multiple repos/orgs. GitHub's own cross-repo secret sharing (organization secrets) only works within one org and needs a paid tier; there's no native GitHub mechanism at all for sharing a secret across orgs. The alternative is a third-party secrets manager SaaS, which is more infrastructure than this problem deserves. Today the actual workaround is manually re-entering the same credential into every consuming repo's own Secrets UI by hand, every time it's needed or rotated.
This isn't a deploy/Docker Compose problem - it's general env/secret reuse across repo and org boundaries, using nothing but git, GitHub Releases, SOPS, and age.
Proposal
Origin side (one canonical repo holding a secret's real value):
- Holds the actual secret as a plain GitHub Secret, same as today.
- Holds a registry of every consumer's public age key (same shape as this repo's existing
keys/<name>.pub files, just repurposed: each entry is a consuming repo/org, not a deploy target).
- On rotation (or on every release, mirroring the existing
encrypt-vaults/encrypt job pattern), re-encrypts the secret for every registered recipient's public key and publishes one .sops.env per consumer as a release asset - generalizing encrypt-env from "recipients = this org's own deploy targets" to "recipients = anyone who registered a public key."
Consumer side (any repo, any org):
- Registers its own public age key with the origin repo (e.g. via a PR adding a
.pub file - the direction of key exchange is public keys flowing into the origin; encrypted secrets flow out to consumers, private keys never leave their own repo).
- Pulls the release asset published for it specifically - already works today unmodified, since
deploy/resolve.py's download_ref already supports owner/repo@tag:asset refs against any accessible repo, not just this one.
- Decrypts it with its own private age key (a GitHub Secret local to the consumer repo) and merges the result into the current job's environment (
$GITHUB_ENV) for later steps to use.
Open questions
- Manifest shape for the recipient registry - one registry per secret, or one shared registry the origin applies to everything it holds?
- Tooling for a consumer to bootstrap its own age keypair (this session did it by hand with
age-keygen; worth scripting/documenting properly if this becomes a real pattern).
- Package the consumer side as a composite action (
.github/actions/) any repo can pull in directly, matching the existing encrypt-env/build-bundle pattern.
- Whether this repo should dogfood it immediately -
notify-telegram-pr.yml/notify-telegram-release.yml still read secrets.TELEGRAM_BOT_TOKEN/secrets.TELEGRAM_CHAT_ID directly today, which is exactly the pain this would remove first.
Related
Came up while setting up notify-telegram-release.yml (#152) and continually re-entering the same Telegram credentials by hand across different secret slots.
Problem
This repo already has a working "encrypt for a set of recipients, publish, decrypt on demand" mechanism (
vaults/*.yml→encrypt-envaction →.sops.envrelease assets →deploy/vault.py'sdecrypt_env) - but it's scoped narrowly to one use case: a deploy target's own age key decrypting an app's env during a deploy.Meanwhile there's a real, recurring problem this same mechanism could solve but currently doesn't: sharing a credential (e.g. a Telegram bot token) across multiple repos/orgs. GitHub's own cross-repo secret sharing (organization secrets) only works within one org and needs a paid tier; there's no native GitHub mechanism at all for sharing a secret across orgs. The alternative is a third-party secrets manager SaaS, which is more infrastructure than this problem deserves. Today the actual workaround is manually re-entering the same credential into every consuming repo's own Secrets UI by hand, every time it's needed or rotated.
This isn't a deploy/Docker Compose problem - it's general env/secret reuse across repo and org boundaries, using nothing but git, GitHub Releases, SOPS, and age.
Proposal
Origin side (one canonical repo holding a secret's real value):
keys/<name>.pubfiles, just repurposed: each entry is a consuming repo/org, not a deploy target).encrypt-vaults/encryptjob pattern), re-encrypts the secret for every registered recipient's public key and publishes one.sops.envper consumer as a release asset - generalizingencrypt-envfrom "recipients = this org's own deploy targets" to "recipients = anyone who registered a public key."Consumer side (any repo, any org):
.pubfile - the direction of key exchange is public keys flowing into the origin; encrypted secrets flow out to consumers, private keys never leave their own repo).deploy/resolve.py'sdownload_refalready supportsowner/repo@tag:assetrefs against any accessible repo, not just this one.$GITHUB_ENV) for later steps to use.Open questions
age-keygen; worth scripting/documenting properly if this becomes a real pattern)..github/actions/) any repo can pull in directly, matching the existingencrypt-env/build-bundlepattern.notify-telegram-pr.yml/notify-telegram-release.ymlstill readsecrets.TELEGRAM_BOT_TOKEN/secrets.TELEGRAM_CHAT_IDdirectly today, which is exactly the pain this would remove first.Related
Came up while setting up
notify-telegram-release.yml(#152) and continually re-entering the same Telegram credentials by hand across different secret slots.