chore(canary-phase9): wire-up complete — API + middleware + Turnstile + token service
Phase 9 is the wire-up phase: brings together everything the generator phases produced into a working HTTP service plus the TCP mysql listener. All 7 generators are now reachable end-to-end through HTTP routes (or TCP for mysql), and POST /api/tokens creates tokens backed by a real registry. Implementation commits in this phase: -c988e4b9feat(canary): middleware additions (realip, fingerprint, recovery) -537b0e3ffeat(canary): turnstile verifier + middleware -019bfda4feat(canary): token service + contract relocation -0a456f97feat(canary): token HTTP handlers (API + trigger routes) -8d1016b8feat(canary): full wire-up + webbug refactor + mysql goroutine -6c370825fix(canary-phase9): plumb mysql public host/port through to generator Deliverables: middleware/ - realip.go: RealIP + OptionalHeader (promoted from webbug) - fingerprint.go: ExtractFingerprint (sha256[:16] of realIP|UA) + KeyByFingerprint (rate-limit key builder) - recovery.go: defer recover + 500 JSON envelope + request_id log - turnstile.go: header-or-body extraction + verifier delegation turnstile/ - verifier.go: cloudflare siteverify call + 5-min fingerprint cache + dev-mode bypass when SecretKey empty + 10s HTTP timeout token/ - contract.go (NEW LOCATION): Generator interface + Artifact + ArtifactKind + TriggerResponse moved from generators package (resolved long-standing import cycle); generators/generator.go now re-exports as type aliases for backwards compatibility - service.go: Create (validation + ID/manage_id gen + registry dispatch + repo persist), GetByID, GetByManageID, IncrementTriggerCount, TriggerURL/ManageURL builders - handler.go: RegisterAPIRoutes (GET /tokens/types, POST /tokens), RegisterTriggerRoutes (GET /c/{id}, POST /c/{id}/fingerprint, HandleFunc /k/{id}, /k/{id}/* for any method) - types.go: TypeDescriptors() — static 7-type catalog for the /tokens/types endpoint cmd/canary/main.go: full wire-up per spec §11.1 - global: RequestID → Logger → Recovery → SecurityHeaders - /healthz: no further mw - /c/{id}, /c/{id}/fingerprint, /k/{id}, /k/{id}/*: OUTSIDE /api (no CORS, no rate limit, no Turnstile — we want every attacker hit recorded per spec §11.1 line 1552) - /api: CORS → RateLimit (KeyByFingerprint, FailOpen=true) - /api/tokens/types: read-only public - /api/tokens POST: + TurnstileVerify middleware - mysql goroutine via spawnMySQLListener iff cfg.MySQL.Enabled (closes Phase 8 task 8.5 deferral); uses mysqlTokenLookup + mysqlEventRecorder adapters - directEventRecorder bridges token.EventRecorder to event.Repository + token.Repository (Phase 10 swaps in event.Service for dedup + async notify) webbug refactor (task 9.8): deletes local realIP/lastNonEmptyXFF/ optionalHeader copies, uses middleware helpers. Other 5 generators still have their copies (sanctioned per handoff). config additions: Canary {BaseURL, ManageURL}, Turnstile {SecretKey, SiteKey}, MySQL {Enabled, Addr, PublicHost, PublicPort}. Env keys + defaults wired. .golangci.yml: adds gosec G107 + G704 to global excludes (SSRF taint analysis on operator-configured outbound HTTP). Documented as policy-level (matches G104/G706 precedent). Phase 10's webhook sender will validate user-supplied URLs at the call site. ~50 new test cases across the 5 new packages: - middleware: 16 (IP precedence + fingerprint + recovery + Turnstile mw incl. header/body/preservation) - turnstile: 14 (bypass + cache hit/miss + failure + nil-Redis + network error) - service: 15 (ID generation + repo persistence + metadata validation per type + rollback on generator failure + URL builders + ~30-call distinctness) - handler: 10 (envelope + happy path + bad JSON + validation + trigger known/unknown/disabled token + ArtifactJSON discriminator) - registry: +1 (MySQLUsesConfiguredPublicAddress regression guard) Audit outcome (2 agents in parallel per standing pattern): - superpowers:code-reviewer: PASS-WITH-NITS (0 BLOCKER, 3 SHOULD-FIX, 5 NITs). One actionable SHOULD-FIX (mysql public address ignored) cleared in6c370825before rollup. Other two SHOULD-FIX items (rate-limit create-tier; scope G107 exclude) deferred to Phase 10 with explicit rationale. - general-purpose spec-adherence vs §6/§8/§11: PASS-WITH-NITS. All 12 tasks discharged or properly deferred (9.9 → Phase 10, 9.12 = this rollup). All §11.1 middleware order layers verified. All §8.2 request/response shapes verified. §11.2 fingerprint and §11.4 Turnstile both match spec. Pre-rollup gate clean at HEAD 6c370825: - go build / vet / test -race / test -tags=integration / golangci-lint run — all clean - zero //nolint pragmas - BACKLOG open section still _(none)_ - go.mod surface unchanged (no new direct deps in Phase 9) DEFERRED (sanctioned): - Task 9.9 testcontainers integration test → Phase 10 (alongside event.Service + notify.Service end-to-end coverage) - §11.3 create-tier rate-limit nesting → Phase 10 (Turnstile already provides spam protection at the create path) - Scope gosec G107/G704 to specific packages → Phase 10 (need webhook sender first to validate the call-site validation pattern) - mysql_username metadata write in token.Service → Phase 10 (mysql trigger lookup is by token ID, not username — current Phase 8 impl already handles this in the generator) - FingerprintRecorder real wiring (currently nil → 204) → Phase 10 - /api/m/{manage_id} GET/DELETE → Phase 11 (manage page API) Forward state for Phase 10 (event service + dedup + notify dispatcher): swaps directEventRecorder + mysqlEventRecorder for event.Service.Record, brings notify.Service with Telegram + webhook senders, Redis SetNX dedup gate, retention loop. Then Phase 11 wires /api/m/{manage_id} manage page endpoints, Phase 12 wires admin + OperatorBearer, Phase 13 wires GeoIP.
This commit is contained in:
parent
6c37082598
commit
1fe61345ef