Skip to content

feat(api): GitHub Marketplace purchase webhook#62

Merged
ErenAri merged 2 commits into
mainfrom
feat/github-marketplace-webhook
Jun 28, 2026
Merged

feat(api): GitHub Marketplace purchase webhook#62
ErenAri merged 2 commits into
mainfrom
feat/github-marketplace-webhook

Conversation

@ErenAri

@ErenAri ErenAri commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Adds a signature-verified webhook endpoint for the bpfcompat GitHub Marketplace listing:

POST https://api.kernelguard.net/github/marketplace/webhook

What it does

Receives marketplace_purchase events (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 vs X-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 an action.
    • 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).
  • Env knob BPFCOMPAT_GITHUB_MARKETPLACE_WEBHOOK_SECRET cataloged in internal/envref + regenerated docs/env-reference.md.
  • docs/github-marketplace.md — operator setup + response contract.

Status contract

Status When
200 ping / recorded purchase / acknowledged non-purchase event
400 malformed payload or missing X-GitHub-Event
401 missing/invalid signature
405 non-POST
413 body > 1 MiB
500 persistence failed → GitHub redelivers
503 secret not configured (never runs open)

Scope

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-docs regenerated and committed. New unit + handler tests cover signature, parsing, ledger perms, and the full status matrix.

🤖 Generated with Claude Code

ErenAri and others added 2 commits June 28, 2026 18:58
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>
@ErenAri ErenAri merged commit 6c3b12c into main Jun 28, 2026
8 of 9 checks passed
@ErenAri ErenAri deleted the feat/github-marketplace-webhook branch June 28, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant