Thanks for your interest in contributing. This doc covers how to get a change in front of maintainers and what we look for in a clean PR.
- Open an issue first for non-trivial work — saves you a wasted PR if the change conflicts with in-flight work or doesn't fit the project direction.
- Branch from
main, name itfeat/...,fix/...,docs/...,chore/..., orhotfix/.... - Sign your commits. SSH or GPG, your choice.
mainenforces signed commits; unsigned PRs are blocked at merge. - Write tests for behavioural changes. The bar is "is this change
regressable?" — if yes, add a test. Follow the conventions in
TESTING.md(gomega + table-driven + sub-tests). - Don't suppress findings. Lint, SAST, vuln-scan, and Scorecard
warnings are signals, not noise. If a finding is a real false
positive, document why in the PR description, not via
.trivyignore/# nosemgrep/// nolint:unless explicitly sanctioned by a maintainer. - One PR per concern. Keep diffs reviewable.
cmd/sc/— CLI entrypointpkg/api/— public consumer-facing typespkg/clouds/— provider implementations (AWS, GCP, K8s, etc.)pkg/security/— HMAC cache + signing helpersdocs/docs/— MkDocs site source, published to docs.simple-container.com.github/workflows/— CI pipelines (push.yaml is the prod path)welder.yaml— local build + tag-release tasksscripts/— helpers invoked from CI (kept small and testable)
# Clone + build
git clone https://github.com/simple-container-com/api.git
cd api
go build ./...
# Unit tests
go test ./...
# Unit + integration tests
go test -tags integration ./...
# Coverage (uses the welder task with entry-point + mock exclusions)
welder run coverage
# Fuzz tests (HMAC cache parse path)
go test -run='^$' -fuzz=FuzzVerifyAndExtract -fuzztime=30s ./pkg/security/
# Lint
golangci-lint run # if installed locallySee TESTING.md for the full set of test invocations,
fixture conventions, and the coverage policy.
CI runs golangci-lint, go vet, staticcheck, Semgrep, CodeQL, and
fuzz on every PR — set up local tooling to catch most of these before
push to save round-trips.
Conventional Commits. Subject line ≤ 72 chars:
fix(scorecard): pin pip via --require-hashes + Meteor version + CodeQL on all branches
feat(security): Go fuzz tests on HMAC cache parse path
chore: gofmt -s -w on 4 unfmt'd files
hotfix(sc.sh): drop invalid --yes flag on cosign verify-blob
docs(pinned-deps): inline Meteor Dockerfile into README, delete standalone
Allowed types: feat, fix, hotfix, chore, docs, refactor,
test, perf, ci, deps.
Body explains the why — what hidden constraint or threat-model consideration drove this choice. Don't restate the diff.
git commit -S -s -m "your message"-S GPG/SSH-signs the commit; -s adds a Signed-off-by trailer
(DCO). main enforces signed commits via branch protection; unsigned
commits are rejected at merge time.
To set up SSH signing:
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global gpg.format ssh
git config --global commit.gpgsign true- Title: same Conventional Commits form as the commit subject.
- Body: summary + rationale + test plan + projected impact (e.g., Scorecard score delta for security-leaning work, performance delta for perf work).
- Link any related issue with
Closes #N/Refs #N. - Pass all CI checks before requesting review.
- Code review is required — current branch protection enforces 1 reviewer minimum; this will increase to 2 once admin-UI gates land.
- Use
/codex-reviewor/gemini-reviewfor an LLM-driven pre-merge sanity pass on larger PRs. Reviewers hallucinate — verify any claim empirically before acting on it.
If your change touches:
pkg/security/(HMAC cache, signing helpers).github/workflows/push.yaml(the prod publish path)sc.sh(the install bootstrap consumers run viacurl | bash)docs/SECURITY.md(the threat model + identity-regex contract)- Anything in the SLSA / cosign / sigstore chain
… open the PR with a threat-model note: which entry in
SECURITY.md's STRIDE table + attack vectors V1–V5
does this change address or affect, and what's the reachability /
blast-radius story? Maintainers will pull in additional reviewers
(codex + gemini round + human security review) for changes here.
Never disable verification as a fix for a verification bug. If sc.sh's cosign-verify path rejects a release, the bug is in the signing pipeline or the verify call — not in the verification itself. See PR #268 for the canonical example of how to handle this.
Don't open a public issue for security bugs. See SECURITY.md for the responsible-disclosure channels (GitHub Security Advisory preferred, email fallback).
By contributing you agree your contributions are licensed under the
MIT License (see LICENSE). The DCO trailer added by
git commit -s is your record of this.
- Release tags are managed by
welder run tag-releaseinpush.yamldocker-finalize. The new stepCreate GitHub Release on production tagsattaches signed sidecars. - Don't push directly to
main— branch protection rejects it. - Don't force-push to shared branches.
- Hotfix flow:
hotfix/...branch, regular review cycle, merge, next prod release picks it up automatically. Production releases are NOT manually cut — every push tomainproduces one.
- Open a discussion
- Ping a maintainer on the issue you filed
Thanks for helping make this safer.