Skip to content

waf: add waf.events read API + collector.setWAFEvents contract#131

Open
acoshift wants to merge 2 commits into
mainfrom
waf-events
Open

waf: add waf.events read API + collector.setWAFEvents contract#131
acoshift wants to merge 2 commits into
mainfrom
waf-events

Conversation

@acoshift

Copy link
Copy Markdown
Member

Types-only contract PR for the WAF events feature (workstream #2 in SPEC-waf-events.md §J): Collector.SetWAFEvents + WAF.Events interfaces, request/response types with Valid(), constants, and client methods. No server logic lives here.

What

  • Collector.SetWAFEvents (collector.setWAFEvents) — internal ingest contract for sampled WAF match events shipped from the controller's event ring (CollectorSetWAFEvents / CollectorWAFEventItem). Documented as synchronous server-side: events have no healing second source, so the collector advances its ring cursor only on success.
  • WAF.Events (waf.events, waf.get permission) — read API (WAFEvents / WAFEventsResult / WAFEvent) with rule/action filters, keyset Before cursor (id < before over controller-minted ULIDs), Table() for CLI output.
  • Constants in constraint.go: page limits (default 50 / max 200), batch cap (5000), per-string item caps, WAFEventIDLength, and exported ValidWAFEventID so the apiserver enforces the exact same cursor shape (26-char uppercase Crockford ULID) at ingest that the client enforces on Before — a stored off-alphabet id would otherwise come back as Next and wedge pagination when echoed as Before (spec §E.2 references this helper).
  • Client methods on collectorClient / wafClient following the existing invoke pattern.

Wire format matches the engine side exactly (moonrhythm/parapet-ingress-controller#182, wafevent.Event): shared JSON names id/at/ruleId/action/status/clientIp/country/asn/method/host/path; engine-only zone and collector-added projectId (string-encoded) are the documented deltas. Wire-pin tests lock both the collector item and the read-API event field names.

Spec: SPEC-waf-events.md (§D contract, §E.2 ingest validation, §J rollout).

Tests

gofmt clean; go build ./... && go vet ./... && go test ./... green — 65 tests in 2 packages. New coverage: request validation table (required/enum/case/limit/cursor-shape/trim), ValidWAFEventID alphabet cases, Table() incl. empty result, wire pins for CollectorWAFEventItem (12 fields, projectId string encoding) and WAFEvent/WAFEventsResult (11 + 2 fields). All example data uses documentation IPs (203.0.113.0/24) — no real event data.

Rollout (spec §J — every step inert alone)

  1. parapet-ingress-controller image + WAF_EVENTS_TOKEN Secret (PR 182; endpoint off until both envs set, inert until polled)
  2. this PR — tag after merge
  3. apiserver: waf_events DDL migration before binary (house rule), then collector.setWAFEvents + waf.events + mounts
  4. collector: syncWAFEvents, enabled via waf_events_target + waf_events_token envs (unset = off, binary can ship early)
  5. console events table, then mcp/CLI + docs

Gate: downstream repos re-pin this module only after the tag; new interface methods break implementers at compile time on upgrade, per the established pattern.

Expected merge conflicts with open sibling branches

Branched from origin/main; two other WAF branches are open and append to the same spots — whichever merges second gets trivial append-append conflicts:

  • waf-test: waf.go (WAF interface method + tail-of-file types after WAFLimitMetricsSeries), client/waf.go (tail after LimitMetrics), constraint.go (WAF constants block)
  • waf-ip-lists: constraint.go (same WAF constants block)

All are additive; resolution is keep-both.

acoshift added 2 commits July 11, 2026 10:20
Contract leg of SPEC-waf-events: WAF.Events (sampled match events,
newest-first, ULID keyset pagination via Before/Next, rule/action
filters) and Collector.SetWAFEvents (append-only, ULID-deduped,
synchronous — events have no healing second source). Shared caps and
per-field length limits live in constraint.go for the apiserver's
server-side validation. Wire field names are pinned by test against
the engine's cursor-endpoint Event.
Review fixes:
- Export ValidWAFEventID so the apiserver applies the same cursor-shape
  check to item IDs at ingest that the client applies to Before — a stored
  off-alphabet id would come back as Next and then wedge pagination when
  echoed as Before (spec §E.2 amended to reference it).
- Trim Action and Before in WAFEvents.Valid() alongside RuleID; §G blesses
  hand-editing the query params these map onto.
- Pin WAFEvent/WAFEventsResult JSON field names (console/MCP/CLI consume
  them by name), matching the existing collector-item wire pin.
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