The sandbox GitHub Environment
Business view
GitHub repos have a feature called "Environments" — named buckets of variables and secrets that a workflow job can opt into. This PR has the PR sandbox deploy opt into one called sandbox. That's the mechanism that makes "team-wide defaults" possible at all: it's simply where those defaults now live, editable by whoever administers the repo, without touching the workflow file or any individual PR. The one side effect worth knowing about: GitHub records every job that declares an environment as a "deployment" against it, so this PR's sandboxes will now show a sandbox deployment marker on the PR. That marker is informational only — see below for why it can't accidentally block anything.
Technical view
Source: .github/workflows/sandbox.yml:86-97.
deploy:
needs: gate
if: github.event.action != 'closed' && needs.gate.outputs.deploy == 'true'
runs-on: [self-hosted, homelab, dentolize]
timeout-minutes: 30
# `sandbox` environment: team-managed base config (variables + secrets) for
# sandboxes, layered UNDER each PR's own inline block. Declaring it here makes
# those vars/secrets visible to the compose step. The environment has no
# protection rules, so this does NOT gate PR deploys (it only records a
# cosmetic `sandbox` deployment marker on the PR).
environment: sandbox
.github/workflows/sandbox.yml:86-96
Why declaring it is necessary, not cosmetic
In GitHub Actions, an environment's variables and secrets are scoped to jobs that explicitly declare environment: <name> — a job that doesn't declare it cannot read them via vars/secrets, even if the environment exists and has values configured. Before this PR, the deploy job had no environment: key, so a SANDBOX_ALL_WHATSAPP variable or secret set on the sandbox environment would have been completely invisible to toJSON(vars)/toJSON(secrets) — the layering feature described in The precedence chain would have had nothing to read from that source. This one-line addition is what turns "team-wide defaults" from a wish into something the job can actually see.
Why it doesn't gate anything
Declaring environment: sandbox is also how GitHub environments implement protection rules — required reviewers, wait timers, branch restrictions — which would pause a job until satisfied. The sandbox environment has none configured, so the job proceeds immediately, same as before this PR; the existing gate job (sandbox.yml:50-84, unchanged by this PR) remains the only thing that decides whether a deploy runs at all, based on whether the PR-body ## 🧪 Sandbox block changed on an edited event. The tradeoff is explicit: if someone later adds a protection rule to the sandbox environment (e.g. "require a reviewer"), every PR sandbox deploy would start blocking on that approval — worth flagging to whoever administers environment settings, and worth a periodic check (see For Stakeholders).
The deployment marker
Because environment: sandbox is declared, GitHub Actions records each run of this job as a deployment to the sandbox environment, visible in the repo's Deployments and on the PR's timeline. This is purely a GitHub bookkeeping side effect of declaring the environment — the workflow doesn't create it explicitly, and nothing in sandbox.yml reads or reacts to it.
Unaffected by this change
The GITHUB_TOKEN used by the "Deploy sandbox" step (sandbox.yml:196) is the default, automatically-scoped workflow token — not something sourced from the sandbox environment's secrets. Declaring environment: sandbox doesn't change how that token is issued or scoped, and (per Secrets and safety guardrails) it's explicitly excluded from what a secret:<NAME> reference can resolve to.