Review: Stripe webhooks unreachable — basketball-api Tailscale Funnel not routing public traffic

review-836-2026-04-04-r2 Doc

review ready

Verdict: APPROVED

Re-review after refinement. Previous review review-836-2026-04-04 returned NEEDS_REFINEMENT with 6 recommendations. All 5 [BODY] recommendations addressed. The 1 [SCOPE] recommendation (arch-basketball-api note) was accepted as discovered scope, non-blocking.

Template Completeness

  • [x] Type — Bug
  • [x] Lineage — references #340, explains discovery path
  • [x] Repo — corrected: lists both ldraney/pal-e-services (funnel infra) and forgejo_admin/basketball-api (webhook endpoint)
  • [x] What Broke — rewritten: correctly states funnel exists but public traffic doesn't reach endpoint. Includes dollar impact ($780 jersey + $375+ tryout).
  • [x] Repro Steps — 4-step reproduction from off-tailnet perspective
  • [x] Expected Behavior — clear, references working westsidekingsandqueens comparison
  • [x] Environment — detailed: namespace, webhook ID, funnel ingress name, comparison hostname
  • [x] Investigation Targets — 4 investigation areas (operator logs, ACL nodeAttrs, operator pod status, working vs broken diff)
  • [x] Acceptance Criteria — 4 criteria, all testable
  • [x] Related — references project page, originating issue, key files

Traceability

  • [x] story:WS-S18 label — "As a parent, I want to receive a branded email with jersey ordering link so that I can order without bringing cash" — verified in project-westside-basketball user-stories section under Parent stories
  • [x] story note verified — found in project-westside-basketball user-stories section
  • [x] arch:basketball-api label — present on board item
  • [ ] arch note MISSING — no arch-basketball-api note found in pal-e-docs. [SCOPE] Create architecture note arch-basketball-api. Non-blocking per original review agreement.
  • [x] Forgejo issue — forgejo_admin/basketball-api#343, open

File Targets

  • [x] pal-e-services/terraform/services.tf (~line 177) — verified: kubernetes_ingress_v1.service_funnel resource at line 177 with for_each over var.services filtered by v.funnel. Annotation tailscale.com/funnel = "true" at line 184.
  • [x] pal-e-services/k3s.tfvars (~line 178) — verified: basketball-api service block at line 178 with funnel = true at line 182.
  • [x] basketball-api/src/basketball_api/routes/webhooks.py — verified: @router.post("/stripe") endpoint at line 263. Handler validates stripe-signature header, constructs events, routes checkout.session.completed through generic order, legacy jersey, and tryout registration handlers.

Repo Placement

CORRECTED from previous review. Issue now correctly identifies both repos: ldraney/pal-e-services for funnel infrastructure and forgejo_admin/basketball-api for the webhook endpoint. The Forgejo issue is filed on basketball-api which is acceptable — the webhook endpoint is the affected service, and the fix may land in either repo depending on root cause (ACL in pal-e-platform, operator config in pal-e-services, or something else). Investigation-type bugs are appropriately filed on the affected service.

Dependencies

  • Board item #823 — forgejo_admin/basketball-api#340 "Jersey checkout load failed" — originating symptom, independent fix.
  • Board item #719 — "Stripe webhook not syncing payment to order status" — directly related, QA blocked until funnel works.
  • Board item #720 — "Legacy jersey webhook handler missing payment_status guard" — downstream, matters once webhooks reachable.
  • Tailscale ACL nodeAttrs funnel permission — verified in pal-e-platform/terraform/modules/networking/main.tf line 82-87: grants funnel attr to ["autogroup:member", "tag:k8s"]. Investigation target correctly identified in issue.

Acceptance Criteria

  • AC1 "Root cause identified (ACL, operator, or funnel config issue)" — appropriate for investigation bug. Agent can document root cause.
  • AC2 "Public internet traffic reaches basketball-api.tail5b443a.ts.net" — REWRITTEN per previous review. Testable via curl from off-tailnet host.
  • AC3 "Stripe webhook test event delivered successfully to /webhooks/stripe" — testable via Stripe dashboard "Send test webhook."
  • AC4 "New jersey payment triggers checkout.session.completed with pending_webhooks=0" — testable via Stripe CLI or real payment. Good AC.
  • Yussuf Duro data remediation AC removed per previous review recommendation. Separate scope confirmed.

Blast Radius

If basketball-api funnel isn't routing public traffic, other Terraform-managed funnels with funnel = true may be affected. Verified via kubectl get ingress -A: 26 funnels exist across the cluster. Platform funnels (Grafana, Forgejo, Harbor, etc.) are confirmed working — they are managed by pal-e-platform, not pal-e-services. The pal-e-services service_funnel resource manages funnels for: platform-validation, basketball-api, pal-e-docs, and others. If the issue is specific to the pal-e-services funnel codepath, other services managed by the same resource could be affected. Low regression risk since current state is already broken.

Decomposition Assessment

  • 3 file targets across 2 repos — at the limit but acceptable for investigation
  • 4 acceptance criteria — under the 5 threshold
  • Sequential investigation (find root cause → fix) — cannot parallelize
  • No decomposition needed. Single agent pass.

Recommendation

No action needed. All previous [BODY] recommendations addressed. The [SCOPE] recommendation for arch-basketball-api note remains as discovered scope — non-blocking for ticket advancement.