diff --git a/CODEOWNERS b/CODEOWNERS index 6ff3d5c..d076881 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,5 +2,13 @@ # change protocol behavior or touch a mainnet deployment. /contracts/ @unixwhisperer /deployments/ @unixwhisperer -/.github/workflows/ @unixwhisperer /Clarinet.toml @unixwhisperer + +# Workflows are co-owned. NOTE ON SEMANTICS: GitHub satisfies a code-owner +# requirement when ANY ONE listed owner approves, not all of them. Listing both +# names here therefore means "either Hillary or Matt", not "both". It is here so +# workflow changes reach both of us for review; it is deliberately NOT the +# control that guarantees two sets of eyes. If we ever want that guaranteed, +# it has to come from branch protection (required approvals >= 2), which is +# admin-side and currently unverifiable from the repo (Flashstack-ajv.2.5). +/.github/workflows/ @unixwhisperer @mattglory diff --git a/docs/ops/SLACK_INTEGRATION.md b/docs/ops/SLACK_INTEGRATION.md new file mode 100644 index 0000000..0dc65ab --- /dev/null +++ b/docs/ops/SLACK_INTEGRATION.md @@ -0,0 +1,150 @@ +# Slack Integration — FlashStack + +**Status:** Stage 1 specified here; **not yet active — requires admin action in Slack (see §5).** +**Owner:** Matt (Slack workspace admin + repo admin). Specified by the Security & Contract Lead. +**Scope rule:** Slack is **notification / observability only**. It is never part of the +security gate, and a Slack outage must never turn a CI check red. See §6. + +--- + +## 1. Why the official integration, not a custom bot + +We use the **official GitHub app for Slack** (`github.com/integrations/slack`) rather than a +webhook bot, because: + +- **No credentials in the repo.** It authenticates by OAuth between GitHub and Slack. There is + **no webhook URL, no bot token, and no GitHub secret to store** — so there is nothing for us + to leak, rotate, or accidentally commit. A custom bot would require an incoming-webhook URL in + repository secrets, which is a credential we would then own. +- **It cannot affect CI.** Subscriptions are read-only consumers of GitHub events. There is no + workflow step to fail, so the security gate is structurally unable to depend on Slack. +- **Nothing deployment-related is exposed.** No deployer mnemonic, no contract admin key, and no + mainnet credential is involved at any point. Slack sees only what is already public — this is a + public repository. + +## 2. Channels + +| Channel | Purpose | +|---|---| +| `#flashstack-ci` | Build/test health: PR activity, review + merge activity, `FlashStack CI` outcomes on `main`. | +| `#flashstack-security` | Security signal only: `Security Scan` workflow outcomes, audit blockers, findings needing attention. | + +Keeping these separate is deliberate. A security channel that also carries routine build chatter +stops being read, and an unread security channel is worse than none. + +## 3. Commands — `#flashstack-ci` + +Run these **in `#flashstack-ci`**, after `/github signin`: + +``` +/github subscribe mattglory/Flashstack +/github unsubscribe mattglory/Flashstack commits issues public branches discussions +/github subscribe mattglory/Flashstack reviews +/github subscribe mattglory/Flashstack workflows:{name:"FlashStack CI" branch:"main"} +``` + +- The first line subscribes with GitHub's defaults (issues, pulls, statuses, commits, releases, + deployments); the second line **removes the noisy half** of that default immediately. +- `commits` is dropped: every merge to `main` is already visible as PR merge activity. +- `branches` and `public` are dropped: branch create/delete is pure noise for a two-person team. +- `releases` / `deployments` are **left subscribed but are currently dormant** — FlashStack has no + release or GitHub-deployment flow today. They cost nothing now and are the right default if one + is added. +- `workflows:{…}` is scoped to the CI workflow **on `main`**, so PR-run chatter does not land here; + PR-level pass/fail is already visible on the PR itself. + +## 4. Commands — `#flashstack-security` + +Run these **in `#flashstack-security`**: + +``` +/github subscribe mattglory/Flashstack workflows:{name:"Security Scan" branch:"main"} +/github unsubscribe mattglory/Flashstack commits issues pulls releases deployments statuses branches public discussions +/github subscribe mattglory/Flashstack issues:{label:"security"} +``` + +The `unsubscribe` line is important: `/github subscribe` on a repo applies defaults, so the +security channel must have them stripped or it becomes a second CI channel. + +`issues:{label:"security"}` gives us a deliberate escalation path — labelling an issue `security` +routes it to the channel. That label does not exist yet; create it, or drop this line. + +### 4.1 Known gap — security *alerts* do not reach Slack + +**The official GitHub Slack app has no subscription for Dependabot alerts or CodeQL / code-scanning +alerts.** Those are restricted to users with repository security access and are not exposed as a +Slack subscription feature. So `#flashstack-security` will carry: + +- ✅ `Security Scan` **workflow** outcomes (dependency audit + CodeQL job results) +- ❌ **not** the Dependabot alert feed, and **not** individual CodeQL findings + +This is a real limitation, not an oversight in the commands above. Until it is closed, the alert +feed remains email + the GitHub Security tab. Closing it properly needs either a GitHub Actions +step that reads the alerts API and posts a digest, or GitHub Enterprise-level tooling — that is +Stage 2 territory, and see §7 for why Stage 2 is blocked. + +## 5. Admin action required — cannot be done from this repository + +Everything in §3 and §4 is a **Slack slash command**, not a repository file. None of it can be +committed, and none of it is active as a result of this document being merged. Matt needs to: + +1. Install the GitHub app into the Slack workspace (**Slack workspace admin**) — + https://slack.github.com or the Slack App Directory. +2. Create `#flashstack-ci` and `#flashstack-security` if they do not exist. +3. Run `/github signin` once, to link the GitHub account. +4. Run the §3 commands in `#flashstack-ci` and the §4 commands in `#flashstack-security`. +5. Confirm back what was actually applied — `/github subscribe list features` in each channel + prints the live subscription set. **Please paste that output**, so the state in this document + can be replaced with verified reality rather than intent. + +**Secrets required: none.** If any step asks for a webhook URL or a token, stop — that means a +custom-bot path was taken instead of the official app, and that path needs a separate decision +about where the credential lives (repository/org secret, never a committed file). + +### 5.1 Verify, do not assume + +GitHub has changed the `workflows:{…}` filter syntax before. If a command in §3/§4 is rejected, +run `/github help` for the syntax that channel's app version accepts, and correct this document — +do not silently leave a subscription broader than specified. A subscription that fails open is +the noise problem; a filter believed to be applied but rejected is worse, because it looks solved. + +## 6. Constraints — non-negotiable + +- Slack is **never** a required status check, and no workflow gains a Slack step in Stage 1. +- A Slack failure **must not** fail the security gate. Stage 1 satisfies this structurally: there + is no workflow step to fail. Stage 2 must satisfy it explicitly (`continue-on-error: true` on + the notify step *only*, never on an audit step). +- No deployment or contract credential is ever sent to Slack. +- No webhook URL or token is ever committed. Org/repository secrets only, if ever needed. +- `.github/workflows/` is CODEOWNERS-protected; any Stage 2 workflow change goes through review. + +## 7. Stage 2 — blocked on PR #69 + +Stage 2 is a custom CI summary posting the **security gate result** to `#flashstack-security`. +It must not be built yet, because today the result it would report is not trustworthy: + +``` + TODAY (before #69) AFTER #69 MERGES + ------------------ ---------------- + npm audit finds high/critical npm audit finds high/critical + | | + continue-on-error: true step fails (flag removed) + | | + step "fails" but is swallowed job conclusion = failure + | | + job conclusion = SUCCESS workflow conclusion = failure + | | + Slack would report "all clear" Slack reports the real result + | + FALSE ASSURANCE +``` + +This is not hypothetical. PR #61's `Security Scan` run printed several `Severity: high` findings +and still concluded **success**, while GitHub showed one critical and six high alerts. A Stage 2 +summary built today would have posted a green "Security CI passed" into the security channel at +exactly that moment — automating false assurance, which is strictly worse than no notification. + +**Dependency:** Stage 2 summary → requires PR #69 merged → audit failures become real workflow +failures → Slack can accurately summarize the security gate. + +Tracked in Beads as an explicit blocker. Do not start Stage 2 until #69 is on `main`.