feat(api): GitHub Marketplace purchase webhook#62
Merged
Conversation
Add a signature-verified webhook endpoint for the bpfcompat GitHub Marketplace listing at POST /github/marketplace/webhook (api.kernelguard.net/github/marketplace/webhook). - internal/marketplace: transport-agnostic domain package — HMAC-SHA256 signature verification (constant-time), marketplace_purchase parsing, and a durable append-only JSONL ledger (0600; raw payload can carry a billing email). Close errors surfaced so a failed flush isn't dropped. - internal/api/handlers_marketplace.go: thin handler with an explicit status contract — 503 if the secret is unset (never runs open), 401 on bad/missing signature (coarse message, no oracle), 400 malformed, 200 ping/recorded/ ignored, 500 on persistence failure so GitHub redelivers. - Auth is the delivery signature, not API key/JWT (GitHub can't present those); route registered at top-level, outside /api. - New env knob BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRET cataloged in internal/envref (+ regenerated docs/env-reference.md). - docs/github-marketplace.md: operator setup + response contract. - Tests: signature (valid/missing/tampered/wrong-secret), parse (known/unknown/ missing action), ledger append/perms, and full handler matrix. Scope is ingestion + durable recording; turning a purchase into an entitlement is a deliberate follow-up that consumes the ledger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- errorlint: wrap the json error with %w (multi-error) in ParseEvent - gocritic httpNoBody: use http.NoBody in the method-not-allowed test - gosec G101 false positive: annotate the webhook-secret env-var NAME const (it's a key name, not a credential); G101 stays active elsewhere - drop an unused path/filepath import + dead line in the ledger test Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a signature-verified webhook endpoint for the bpfcompat GitHub Marketplace listing:
What it does
Receives
marketplace_purchaseevents (purchase / change / cancel / free-trial), verifies the delivery, and appends each one to a durable ledger for reconciliation.internal/marketplace— transport-agnostic domain package:VerifySignature— HMAC-SHA256 over the raw body vsX-Hub-Signature-256, constant-time (subtle.ConstantTimeCompare), coarse error so it can't be used as an oracle.ParseEvent— lenient decode (GitHub adds fields over time); requires anaction.AppendLedger— append-only JSONL at<workdir>/marketplace/events.jsonl,0600(raw payload can carry an org billing email),Close()errors surfaced.internal/api/handlers_marketplace.go— thin handler, auth is the signature (GitHub can't present the API key/JWT used elsewhere), route mounted top-level (outside/api).BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRETcataloged ininternal/envref+ regenerateddocs/env-reference.md.docs/github-marketplace.md— operator setup + response contract.Status contract
X-GitHub-EventScope
Ingestion + durable recording only. Turning a purchase into an entitlement (grant access, email buyer, update plan) is a deliberate follow-up that consumes the ledger — kept decoupled so ingestion stays simple and independently testable.
Verification
go build,go vet,go test ./...green; gofmt clean;make env-docsregenerated and committed. New unit + handler tests cover signature, parsing, ledger perms, and the full status matrix.🤖 Generated with Claude Code