# TASK: mopac events — webhook receiver (build phase 2b, V1 scope) ## Objective Implement `mopac events` in /home/reachableceo/projects/meta/MOPAC/harness per DESIGN.md ("Events are V1 scope" section — READ FIRST). A Go HTTP webhook receiver that makes Redmine/Discourse/Gitea able to punch the harness: replies, ticket updates, PR approvals -> harness knows and acts. ## Constraints - ALL DEV IN DOCKER (hard rule): no host toolchains. Pull digest-pinned golang:1.26(-alpine) builder; bind-mount the repo; build/vet/test inside the container. If a Makefile/dev.sh wrapper helps, add it, but every compile/test path routes through docker run. - Go stdlib net/http only (no frameworks); no new deps without need. - Receivers: /hooks/redmine, /hooks/discourse, /hooks/gitea. - Verify secrets: Gitea HMAC-SHA256 header; Discourse secret header; Redmine shared-secret header; reject unsigned/unverified with 401. - Normalize each into one internal Event struct (source, kind, actor, subject/topic/PR id, payload digest, received-at). - Persist events (append-only JSONL + dedup by provider event id) under a state dir from config; NEVER log secret/header values. - Event -> action mapping per DESIGN (turn dispatch is a stub call into the conductor interface for now; wiring comes after skeleton lands). - Config: webhook secrets via env refs (os.environ/VAR pattern), port + state dir in harness.toml; sensible defaults. - Table-driven tests for signature verification + normalization + dedup. - Run it: brief docker run smoke on a high LAN port (e.g. 4100) with a curl-equivalent POST (python urllib — curl is banned on host) using a test secret; show 401 without secret, 200 + JSONL record with. - Commit incrementally to main; push. REPORT to inbox-pmo lists what works, what's stubbed, exact container commands used.