Compare the secrets and env vars your code declares against what is actually set on GitHub Actions and Netlify. Catches the green-workflow, green-deploy, variable-simply-absent defect that no status page shows.
/wiring - is the plumbing actually connected?
Purpose
A site could not send email for months. The mail API key was set and valid, so every surface-level check passed. But three other variables the send function needed were never added, so three scheduled digests never went out and the unsubscribe check failed open. Ten form submissions got silence.
Nothing in a workflow-failure audit, a deploy-status check or an uptime monitor can see that class of defect: the workflow runs, the deploy is green, the variable is simply absent. This skill is the check for it. Run it after any launch, and before saying a flow is live.
When to use
- After adding any integration that reads a secret or env var (email, payments, analytics, an API key).
- Before telling anyone a scheduled job or serverless function is live.
- When a function "works locally" and silently does nothing in production.
Steps
- From the repo checkout, run the helper. It needs the
ghCLI (authed) for the GitHub side and thenetlifyCLI (authed) for the Netlify side; either side degrades to UNKNOWN rather than guessing.
python3 scripts/wiring.py . # repo from `gh repo view`, site from .netlify/state.json
python3 scripts/wiring.py . --repo owner/name --netlify-site <site_id>
It derives the required set from the code every run. There is deliberately no hand-maintained registry of required vars, because those drift:
| Side | Declared | Actually set |
|---|---|---|
| GitHub Actions | secrets.X in .github/workflows/* | repo secrets plus org secrets |
| Netlify runtime | process.env.X / import.meta.env.X in netlify/, src/, functions/, edge-functions/ | getEnvVars, production context only |
- Report. Lead with GH SECRET MISSING and NETLIFY ENV MISSING; those are live defects. Then NETLIFY ENV NOT ON PRODUCTION, then UNKNOWN. Give one clickable line per side:
- GitHub secrets:
https://github.com/{repo}/settings/secrets/actions - Netlify env:
https://app.netlify.com/sites/{site name}/settings/env
- For every missing var, say what breaks. Trace it to the workflow or function that references it. "NETLIFY_ACCESS_TOKEN missing" is not actionable; "the three scheduled digests in
send-digest.ymlcannot authenticate, so they have never sent" is.
- Anything not verified against the API gets an
UNVERIFIED:prefix.
Failure modes
These are encoded in the script. Do not work around them.
- Never read
netlify env:list --plainas truth. It prints secret-scoped vars asKEY=with an empty value. That is redaction, not absence, and reading it as absence is how a valid key got wrongly called empty. The script uses thegetEnvVarsAPI, which reports real value presence. - A var set only on deploy-preview or branch-deploy is not wired. It reads as present in the dashboard and fails in production. Only
allandproductioncontexts count; anything else lands in NOT ON PRODUCTION. gh secret list --orgreturns 403 without org-admin. Org secrets are still visible per repo viarepos/{repo}/actions/organization-secrets, which needs only repo read. Skipping that reports every org-inherited secret as missing.- If either lookup fails, no diff is run for that side. A partial "available" set makes every unseen secret look missing. UNKNOWN is the honest answer.
- Some absences are correct. Add them to
INTENTIONALin the script with a written reason. Example: when a platform AI gateway is in use, a user-set provider key switches the gateway off, so its absence is the wired state. An unexplained suppression is worse than a false positive. scripts/is excluded from the Netlify scan. Those run in GitHub Actions; including them reported every cron key as a missing Netlify var.- A guarded reference is not a defect.
process.env.X || 'default',?? y, a ternary, or=== 'true'all work when unset. Those land in the "probably fine" line, not in MISSING.