Skip to content

feat: raw-request passthrough for webhook HMAC verification (COD-402) - #1

Merged
shivros merged 2 commits into
mainfrom
runner/cod-402-raw-request-passthrough
Aug 19, 2026
Merged

feat: raw-request passthrough for webhook HMAC verification (COD-402)#1
shivros merged 2 commits into
mainfrom
runner/cod-402-raw-request-passthrough

Conversation

@shivros

@shivros shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Opt-in raw_request: true operation flag for hydra's HTTP surface (COD-402): the generated handler receives the exact raw body bytes + a header map instead of typed extraction, so consumers can verify signatures (webhook HMAC) over the wire representation while keeping handlers generated. This removes the last sanctioned handwritten-surface pattern — rite's GitHub webhook handler is the reference use case.

Closes COD-402.

Design

  • hydra-core: raw_request: bool field (#[serde(default)], opt-in — no name-based inference). Validation: raw ops must list exactly surfaces: [http], stay unary (no SSE), and declare no body-location parameters.
  • hydra-codegen: when any raw op exists, emits GeneratedRawOperationInput { path, query, headers, raw_body: Vec<u8> } and handlers shaped State → Path?/Query? → HeaderMap → Bytes (Bytes last — sole body-consuming extractor). New config knob http_raw_dispatch_fn (default super::execute_generated_raw_operation) keeps the raw lane separate from the existing dispatch.
  • Header contract (documented in generated struct docs + README): names lowercased, non-UTF-8 values dropped, repeated headers last-wins. Signature headers (X-Hub-Signature-256) are ASCII single-valued — unaffected.

Verification

  • cargo build/clippy -D warnings/test/fmt — all green; 19 codegen + 7 surfaces tests (5 new codegen tests, 2 new live surface tests).
  • Byte-exactness proven live: raw_request_delivers_exact_wire_bytes_and_headers POSTs a payload with non-UTF-8 trailing bytes and asserts the echo matches byte-for-byte.
  • Default output byte-identity: pinned pre-feature fixture (tests/fixtures/notes-pre-raw-*, hash-verified against v0.1.0/f6ef2e6 artifacts); new generator + old definition = byte-identical.
  • Consumer round-trip: branch generator reproduces iris and rite committed generated/ artifacts byte-identically (verified via temp-dir diff — empty for all 6 files).

Consumer impact (deliberate, minor-version bump)

GenerateConfig gains http_raw_dispatch_fn. iris and rite construct exhaustive struct literals, so their next hydra upgrade adds one field. They are tag-pinned to v0.1.0 — unaffected until they opt in. Rite's migration to a generated webhook handler is a separate follow-up in the rite project.

Review panel

Dual-model review completed pre-PR; both reviewers returned CLEAN (no blockers/majors). Addressed findings: header-contract docs (field + generated struct + README), fixed a misleading test comment, README knob documentation. Full reviews posted as a PR comment.

Commits

  • ff6774d feat: raw-request passthrough for HTTP webhook operations (COD-402)
  • e4c3ed1 docs: address review panel findings for raw-request (COD-402)

Opt-in `raw_request: true` operation flag: the generated HTTP handler
receives exact raw body bytes + a headers map instead of typed
extraction, so consumers can verify signatures (webhook HMAC) over the
wire representation while keeping handlers generated.

- hydra-core: field + validation (http-only surface, unary, no body params)
- hydra-codegen: GeneratedRawOperationInput, HeaderMap/Bytes handlers,
  http_raw_dispatch_fn config knob, conditional imports
- examples/notes: echo_raw dogfood op + live byte-exactness test
- tests: pinned pre-feature fixture proves default output byte-identical;
  iris + rite round-trips verified byte-identical
- Document header-map contract (lowercased names, non-UTF-8 values
  dropped, repeated headers last-wins) on the field, the generated
  struct, and README
- Fix misleading header-casing comment in surfaces test
- Add raw-request section + http_raw_dispatch_fn knob to README
- Drop unused AppState/tokio from the structural absence test
@shivros

shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Automated Review Panel

Dual-model review ran before the PR was opened. Both reviewers independently ran the test suite and verified the byte-identity fixture provenance against v0.1.0. Verdicts: CLEAN / CLEAN — no blockers or majors. All minor/nit findings addressed in e4c3ed1.

Reviewer A

Verdict: CLEAN

Goal alignment: Yes. Rite's webhook can now be generated: raw_request: true (explicit opt-in, #[serde(default)] — no inference) makes the HTTP handler receive HeaderMap + Bytes and forward GeneratedRawOperationInput to a configurable http_raw_dispatch_fn. Live test proves byte-exactness including a non-UTF-8 payload. Default-path byte-identity genuinely pinned: fixtures verified byte-identical to f6ef2e6's committed artifacts; the HttpImportPlan.body_json refactor is equivalence-preserving when no raw ops exist. CLI/MCP untouched (surfaces must be exactly [http]; tests assert exclusion). Validation runs on both write and check. Consumer-agnostic; hydra-core gained only an accessor + validation.

Correctness findings:

  1. minor — non-UTF-8 header values silently dropped (value.to_str().ok()). Fails closed for signature checks, but undocumented → addressed: documented in generated struct + field + README.
  2. minor — multi-valued headers collapse last-wins; documented only in a test comment → addressed: documented in generated struct doc.
  3. nit — GENERATED_HTTP_RESERVED_NAMES not extended with raw-era names; verified ops named headers/raw_body still compile (shadowing). Convention-consistency only.
  4. nit — README knob docs missing http_raw_dispatch_fnaddressed.
  5. nit — misleading "deliberately unset" config comment in byte-identity test → addressed.
  6. nit — unused AppState + needless #[tokio::test] in structural absence test → addressed.
  7. note — axum DefaultBodyLimit (2 MiB) applies to Bytes; same as the prior Json path, not a regression.

Non-issues verified: extractor ordering correct (Bytes last, sole body-consuming extractor); BTreeMap header collection deterministic; header casing lowercase canonical.

Convention violations: None.

Reviewer B

Verdict: CLEAN

Goal alignment: push_raw_handler emits exactly rite's handwritten shape (State + optional Path/Query + HeaderMap + Bytes), converts headers to a map, passes raw_body.to_vec() to a configurable dispatch fn. Opt-in explicit; validation enforces surfaces == [http], unary-only, no body params. Default path provably unchanged — fixture git-hash verified identical to main's committed artifact; the regression proof is genuine, not synthesized. cargo test --workspace --all-targets and clippy -D warnings green.

Correctness findings:

  1. minor — non-UTF-8 header values silently dropped; inherent to the BTreeMap<String, String> contract, HMAC headers are ASCII, reference use case unaffected → addressed: documented.
  2. minor — multi-valued headers last-wins, only stated in test comment → addressed: documented on GeneratedRawOperationInput doc.
  3. nit — test comment claims "header casing preserved as received" — false; HeaderName::as_str() lowercases → addressed: comment fixed.
  4. nit — no validation that raw_request + GET is sensible; Bytes on GET yields empty body harmlessly. Benign.
  5. nit — README missing the new knob → addressed.
  6. Verified non-issues: extractor order correct; plan.body_json correctly excludes raw ops so Json import logic unchanged for legacy defs; HttpMethod is Get/Post-only; determinism intact (pure string-building, BTreeMap throughout).

Convention violations: None. The http_raw_dispatch_fn field breaking iris/rite exhaustive struct literals is the known, deliberate cost (minor-version bump; both consumers tag-pinned).


Runner: archon · Issue: COD-402 · Branch: runner/cod-402-raw-request-passthrough

@shivros

shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Auto-Merge Gate — SKIP (policy) 2026-08-19

Skipped by the gate, not on quality:

  • Repo policy: AGENTS.md"No auto-merge; human review required." Standing manual-review marker overrides green CI and confidence.
  • Confidence this PR fulfills COD-402: ~0.90. Review panel CLEAN/CLEAN (byte-identity fixtures pinned against v0.1.0, consumer round-trip verified), 26 tests green, Test check SUCCESS, mergeable_state: clean, 12 files +730/−22 purely additive (opt-in raw_request, consumers tag-pinned to v0.1.0 and unaffected).

Ready for your review/merge. COD-402 is In Review and assigned to you. Rite's migration to a generated webhook handler remains follow-up work in the rite project.

@shivros
shivros marked this pull request as ready for review August 19, 2026 15:42
@shivros

shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Auto-Merge Gate — Approval (2026-08-19)

Confidence: 0.92 — MERGE (squash)

PR goal (COD-402): opt-in raw_request: true operation flag so generated HTTP handlers receive exact raw body bytes + header map (webhook HMAC verification), default output unchanged.

Checks observed: CI Test SUCCESS (run 32223281362). Local verification on this PR head (e4c3ed1) by the gate: cargo build --all-targets ✅, cargo test --all-targets — 19 codegen + 7 surfaces, all pass ✅, cargo clippy --all-targets -- -D warnings clean ✅, cargo fmt --all -- --check clean ✅, examples/notes regen check — "generated artifacts are current" ✅.

Why it satisfies COD-402:

  • Opt-in explicit flag (#[serde(default)]) — no name-based inference (design law honored)
  • Validation enforces http-only surface, unary delivery, no body-location params
  • Byte-exactness proven by live test including non-UTF-8 payload; default-path byte-identity pinned via fixtures hash-verified against v0.1.0
  • Acceptance criteria (tests green, byte-exactness proof, default byte-identity) all covered in-PR; "Remaining: none in hydra" per runner completion log
  • Consumers iris/rite are tag-pinned to v0.1.0 — unaffected until they opt in

Review panel: dual-model, both CLEAN; all minors addressed in e4c3ed1.

Policy check: repo AGENTS.md on main (updated via #2, 2026-08-19): "Auto-merge is permitted per the standing gate policy: CI green and gate confidence >= 0.80." ✅ No manual-review markers in COD-402 Linear description/comments. ✅

Scope limits: 12 files +730/−22, purely additive feature flag in hydra-core/hydra-codegen + example + README. No secrets, no deployment, no destructive behavior, same-repo branch (owner TechGodHQ, author shivros).

Self-authored PR — formal self-approval rejected by GitHub; this comment is the approval rationale and audit artifact.

@shivros
shivros merged commit 7a13a51 into main Aug 19, 2026
1 check passed
@shivros
shivros deleted the runner/cod-402-raw-request-passthrough branch August 19, 2026 15:43
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