Single env source: c:/D/oriz/.env ? auto-push to chirag127 GH Org Secrets ? apps consume at build
Single env source: c:/D/oriz/.env ? GH Org Secrets
Decision
c:/D/oriz/.env (gitignored on master) is the single, authoritative source of every secret + config value used anywhere in the chirag127/oriz family.
A scheduled cron (.github/workflows/sync-env-to-org-secrets.yml on master) runs daily at 06:30 IST (or on-demand workflow_dispatch when c:/D/oriz/.env changes):
- Reads
templates/.env.exampleto enumerate canonical key names - For each key, reads the corresponding value from
c:/D/oriz/.env(loaded via secure GH-Action secret OR encrypted file — see "Bootstrapping" below) - Pushes each value to chirag127 GH Org Secrets:
gh secret set <KEY> --org chirag127 --visibility all --body "<VALUE>" - Every repo's workflows + builds inherit the org secret automatically (no per-repo setup)
User mandate verbatim (2026-06-22 evening): "I want to set the single token for all of the authentication and all of the monetization and all of the everything. I want minimum number of environment variable to be set. ... I will set them only one time. Throughout the organization of my GitHub. ... The builder will generate the website and deploy it on whatever server or this repository's .env file will also be served as a single source of truth for all of the environment variables. You will periodically push all of the environment variable from this repository to GitHub. ... I don't want to set environment variable manually for all of the apps and all of the websites."
Bootstrap (one-time, manual — minimum manual work)
You manually populate c:/D/oriz/.env ONCE with the canonical values (~50-80 env vars total). Then never touch them per-repo again.
To run the sync script the first time:
- Generate a chirag127 GH Personal Access Token with
admin:orgscope (one-time, 1-year expiry) - Add it to
c:/D/oriz/.envasGH_ADMIN_PAT=ghp_... - Run
node scripts/sync-env-to-org-secrets.mjslocally — this pushes all current keys to org-level secrets - After local verification, the GH Action takes over on the daily cron
Sync script
c:/D/oriz/scripts/sync-env-to-org-secrets.mjs:
// 1. Parse templates/.env.example for canonical key names
// 2. Parse c:/D/oriz/.env for values
// 3. For each key, call: gh secret set <KEY> --org chirag127 --visibility all
// 4. Report diff (keys added, updated, deleted)
Bootstrapping the GH Action
The cron workflow needs access to c:/D/oriz/.env itself. Options:
A. encrypted-file approach (RECOMMENDED): encrypt .env with age or sops, commit the encrypted blob to master, decrypt in GH Action using a single bootstrap key stored as SOPS_AGE_KEY org secret. One bootstrap secret ? unlimited derived secrets.
B. manual-paste approach: GH Action accepts the .env content as an input on workflow_dispatch; you paste it once + it pushes all keys. Re-paste only when keys rotate.
Choosing (A): encrypted-file. Use sops + age. The unencrypted .env stays gitignored locally; the encrypted .env.enc is committed to master.
Consuming env vars in app builds
Every app's CF Pages build inherits chirag127 org secrets automatically. Apps reference process.env.RAZORPAY_KEY_ID etc. — no per-app setup needed.
For client-side env vars (PUBLIC_*), Astro embeds them at build time via import.meta.env.PUBLIC_*. Same single source.
Implications
- Secret rotation: change once in
c:/D/oriz/.env+ commit the new encrypted file ? wait for daily cron OR trigger workflow_dispatch ? all 51+ repos rotated simultaneously - New secret: add to
templates/.env.example(key + comment) + add value toc:/D/oriz/.env+ sync ? instantly available org-wide - Audit: master commit log = secret rotation history
- No drift: per-repo manual secret writes are FORBIDDEN per
rules/security/github-org-level-secrets.md(already locked)
Supersedes-in-part
security/env-and-secrets-single-source.md — that file describes the two-track delivery (.env.example synced + GH Actions secrets set once). This file extends it with the AUTOMATED periodic-sync mechanism (was implicit; now explicit).
Cross-refs
- Env+secrets two-track decision ? [[security/env-and-secrets-single-source]]
- GH org-level secrets rule ? [[rules/github-org-level-secrets]]
- .env.example synced rule ? [[rules/env-example-synced-from-master]]
- No hardcoded secrets ? [[rules/no-hardcoded-secrets]]
Status: deployed 2026-06-22
First-run results (local bootstrap, 2026-06-22):
sops3.12.2 installed via wingetSecretsOPerationS.SOPSatC:\Users\C5420321\AppData\Local\Microsoft\WinGet\Packages\SecretsOPerationS.SOPS_*\sops.exeage1.3.1 installed via wingetFiloSottile.ageatC:\Users\C5420321\AppData\Local\Microsoft\WinGet\Packages\FiloSottile.age_*\age\age.exe- Age keypair generated at
c:/D/oriz/.sops-age-key.txt(gitignored). Public key:age1c40qjamejzrp9cajle9g0dss25mmsmyaq6uaa2pgmyr3pflsy4qspgw5c4 c:/D/oriz/.env(128 lines, 65 key=value pairs) encrypted ?c:/D/oriz/.env.enc(committed)SOPS_AGE_KEYpushed tochirag127/workspaceas a repo-level bootstrap secret (CI reads it from there)- Scope correction:
chirag127is a GitHub User account, not an Organization. Org-level secrets unavailable. Pivoted to per-repo Actions secrets across the oriz family (workspace + everyoriz-*repo = 42 repos). The scriptscripts/sync-env-to-org-secrets.mjsfans out automatically. - First sync pushed 31 secrets × 42 repos = 1302 repo-level secrets plus 1 bootstrap = 1335 total, 0 failures.
- One canonical key skipped automatically:
GITHUB_TOKEN(reserved name; GitHub auto-injects per workflow run). The script now hard-rejectsGITHUB_TOKENand anyGITHUB_*/ACTIONS_*prefix. - Cron:
30 1 * * *UTC (06:30 IST) daily, plusworkflow_dispatchand push triggers on.env.enc/templates/.env.example/ sync script changes. - Manual user step still required: generate a PAT with
reposcope at https://github.com/settings/tokens and rungh secret set GH_ADMIN_PAT --repo chirag127/workspace --body "ghp_...". The workflow fails fast with a clear error until that PAT is set. - 21 keys exist in
.env.encbut are not intemplates/.env.example(e.g.BING_SEARCH_API_KEY,APPWRITE_*,SSH_PRIVATE_KEY). They are listed by the script as warnings and not pushed until added to.env.example. Reconciling this drift is a follow-up task.