Skip to content

feat(parser_app): verify gateway-signed payment markers - #448

Draft
pepe-anchor wants to merge 1 commit into
mainfrom
pepefigueira/prs-581-04a-payment-verify
Draft

feat(parser_app): verify gateway-signed payment markers#448
pepe-anchor wants to merge 1 commit into
mainfrom
pepefigueira/prs-581-04a-payment-verify

Conversation

@pepe-anchor

Copy link
Copy Markdown
Contributor

Why

Enclave-side half of the x402 trust pair. parser_app has to check that a VerifiedPaymentMarker was signed by the pinned gateway key and is bound to this exact request, otherwise a paid parse could be replayed against a different transaction.

This is also where a wire collision gets fixed. ParseRequest field 4 shipped on main as include_intermediate_output (#414) while payment_marker was still sitting on the unmerged x402 branch claiming the same number. Renumbering now is free. Renumbering after the field is deployed is not.

What

  • ParseRequest.payment_marker takes field 5, leaving include_intermediate_output = 4 untouched.
  • host_primitives::payment_marker: the VerifiedPaymentMarker / SignedVerifiedPaymentMarker types, VPM_VERSION, and request_hash. Borsh-encoded, because the encoding has to match wire-for-wire between the gateway that signs and the enclave that verifies.
  • parser_app::payment_verify: PaymentPolicy (Disabled or Required) and the signature plus request-binding check, run as the first thing parse() does.
  • parse() gains a &PaymentPolicy parameter. Every existing caller passes Disabled, so nothing changes behavior in this PR.

PaymentPolicy::from_env from the branch was dropped on purpose: this repo cannot set env vars in tests (edition 2024 plus forbid(unsafe) bans std::env::set_var), and the real binaries take config from CLI args. from_hex is the only constructor.

Test evidence

cargo test -p host_primitives -p parser_app -p parser_grpc_server  -> all pass
make -C src test   -> full workspace green, every "test result" line ok, 0 failed
cargo fmt -- --check -> clean
make -C src lint   -> exit 0, no warnings
make -C src generated (re-run) -> no further diff, codegen is deterministic

Callers updated: parser_app::service, parser_grpc_server::main (both now pass &PaymentPolicy::Disabled), and parser_gateway::main picks up the new struct-literal field.

Rollback

Revert the commit and re-run make -C src generated. The proto field is additive and unused by any deployed caller, so nothing on the wire depends on it yet.

Linear

PRS-581

Stack position: based on main, independent of the pivot PRs. #414.

🤖 Generated with Claude Code

Enclave-side half of the x402 trust pair: parser_app checks that a
VerifiedPaymentMarker was signed by the pinned gateway key and is bound
to this exact request, so a paid parse cannot be replayed against a
different transaction.

payment_marker takes proto field 5. Field 4 shipped as
include_intermediate_output (#414) while this work sat on an unmerged
branch; renumbering here is free, renumbering after deploy is not.

Policy defaults to Disabled everywhere in this PR, so no caller changes
behavior yet.

Co-Authored-By: Claude <noreply@anthropic.com>
@pepe-anchor
pepe-anchor force-pushed the pepefigueira/prs-581-04a-payment-verify branch from 8bb360c to 320b5ea Compare August 6, 2026 17:08
@pepe-anchor pepe-anchor closed this Aug 6, 2026
@pepe-anchor pepe-anchor reopened this Aug 6, 2026
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