git / moonbite-network / deploy/BRANCH-PROTECTION.md · raw
# Branch protection for `main` (checklist)Why this matters here: `deploy.yml` auto-deploys `main` to the production hostthat serves the wallet and `/downloads`. The `workflow_run` gate makes deployrun only *after* CI succeeds — but that gate protects **deploy**, not **merge**.Without a branch-protection rule, a red-CI or force-pushed commit can still landon `main`; branch protection is what stops an unvalidated commit from becoming`main` in the first place. The two together mean: **`main` only ever advancesthrough a CI-passing commit, and prod only ever runs a CI-validated commit.**Apply this in the GitHub web UI (no local change; it's a repo setting). Repo:`moonbitecoin/moonbite-network`. You need admin on the repo, and a working`gh`/web session — note the invalid `GITHUB_TOKEN` env var breaks `gh`, so usethe browser or `unset GITHUB_TOKEN` first.---## Do this first: run CI once so the check names existRequired-status-check pickers only list checks GitHub has **seen recently**. PRs#2/#3 should have produced them. The CI job is `test` on a 3.11/3.12 matrix, sothe checks are named:- `test (3.11)`- `test (3.12)`If the dropdown doesn't show them yet, let one CI run finish on a PR, then comeback.---## Option A — Branch protection rule (classic)**Settings → Branches → Add branch protection rule.**- [ ] **Branch name pattern:** `main`- [ ] **Require a pull request before merging** — blocks direct pushes to `main`. - [ ] Required approvals: **0** (solo maintainer — GitHub won't let you approve your own PR; set 0 so you aren't locked out. Raise to 1 only once a second trusted maintainer exists.) - [ ] *(optional)* Require conversation resolution before merging- [ ] **Require status checks to pass before merging** - [ ] Require branches to be **up to date before merging** (forces a re-run against the latest `main`, so a check can't pass stale) - [ ] Select the required checks: **`test (3.11)`** and **`test (3.12)`** (these include the pytest suite *and* the release-checksum guard, both steps of the `test` job)- [ ] **Do not allow bypassing the above settings** (a.k.a. "Include administrators") — **on**. This is what actually guarantees the deploy safety for a solo admin; otherwise you can push past your own rule and the gate means nothing. (You can toggle it off for a genuine emergency, then back on.)- [ ] **Allow force pushes:** **off** (never force-push `main` — matches the project rule; a force-push rewrites what prod will deploy)- [ ] **Allow deletions:** **off**- [ ] *(optional, recommended given release signing)* **Require signed commits**- [ ] *(optional)* **Require linear history** (if you prefer rebase/squash merges and no merge commits on `main`)Leave everything else default. Save.---## Option B — Repository ruleset (newer equivalent)GitHub's **Settings → Rules → Rulesets → New branch ruleset** is the modernform and can target `main` by pattern with the same effect:- [ ] **Enforcement status:** Active- [ ] **Target branches:** Default branch (`main`)- [ ] **Bypass list:** empty (add nobody — the equivalent of "include administrators")- [ ] Rules: - [ ] Restrict deletions - [ ] Block force pushes - [ ] Require a pull request before merging (required approvals: 0 for solo) - [ ] Require status checks to pass → add `test (3.11)` and `test (3.12)`, and tick **Require branches to be up to date** - [ ] *(optional)* Require signed commits / linear historyRulesets and a classic rule can coexist; pick one to avoid confusing overlap.---## Fastest path: one `gh` command block (do this, then verify)- [ ] Run exactly this (requires `gh auth status` = `moonbitecoin`, admin on the repo; `unset GITHUB_TOKEN GH_TOKEN` first if a stale token is exported):```bashunset GITHUB_TOKEN GH_TOKENgh auth status# require CI (both matrix legs, up to date), require a PR, enforce for admins,# no force-push, no deletegh api -X PUT repos/moonbitecoin/moonbite-network/branches/main/protection \ -H "Accept: application/vnd.github+json" --input - <<'JSON'{"required_status_checks":{"strict":true,"contexts":["test (3.11)","test (3.12)"]}, "enforce_admins":true, "required_pull_request_reviews":{"required_approving_review_count":0,"dismiss_stale_reviews":true}, "restrictions":null,"allow_force_pushes":false,"allow_deletions":false,"required_linear_history":false}JSONgh api repos/moonbitecoin/moonbite-network/branches/main/protection \ --jq '{checks:.required_status_checks.contexts, strict:.required_status_checks.strict, admins:.enforce_admins.enabled, pr:.required_pull_request_reviews.required_approving_review_count, force:.allow_force_pushes.enabled, delete:.allow_deletions.enabled}'```- [ ] Expected: `checks` lists both legs, `strict:true`, `admins:true`, `pr:0`, `force:false`, `delete:false`.## Optional: apply via API instead of the UI (same call, longer form)After clearing the bad token (`unset GITHUB_TOKEN GH_TOKEN`) and confirming`gh auth status` shows `moonbitecoin` active:```bashgh api -X PUT repos/moonbitecoin/moonbite-network/branches/main/protection \ --input - <<'JSON'{ "required_status_checks": { "strict": true, "contexts": ["test (3.11)", "test (3.12)"] }, "enforce_admins": true, "required_pull_request_reviews": { "required_approving_review_count": 0 }, "restrictions": null, "allow_force_pushes": false, "allow_deletions": false}JSON```Verify:```bashgh api repos/moonbitecoin/moonbite-network/branches/main/protection \ --jq '{checks: .required_status_checks.contexts, admins: .enforce_admins.enabled, force: .allow_force_pushes.enabled}'```---## After enabling — sanity check- [ ] Open a throwaway PR that deliberately fails CI (e.g. a broken test) and confirm the **Merge** button is blocked until checks pass.- [ ] Confirm a direct `git push origin main` is rejected (PR required).- [ ] Confirm `deploy.yml` still fires only after a green CI run on `main` (it deploys `github.event.workflow_run.head_sha`).## Not covered here (separate hardening, see RELEASE-SIGNING.md and deploy.yml)- Non-root deploy user with a narrow sudoers entry.- SHA-pinned GitHub Actions and signed-tag-only deploys.- The minisign release-signing key ceremony — **OPEN, owner-only task**: no signing key exists yet and none should be generated by automation or an agent. Until the ceremony is done, `publish-node-binaries.sh` emits checksums without a signature and `deploy/moonbite-release.pub` is a placeholder. Track it as a launch blocker in LAUNCH_CHECKLIST.md.