Skip to content

BRC-118 multipart body transport is unimplemented, and header-carried payments fail at 32KB behind Cloudflare #473

Description

@imranterranode

BRC-118 specifies moving the payment out of HTTP headers and into a multipart/form-data body. It is the structural fix for a failure we hit on mainnet: past ~32 KB of total request headers, a Cloudflare-fronted origin refuses a paid request outright. Grep says it is implemented nowhere — not in this SDK, not in go-bsv-middleware.

Split out of #445, which now carries only the BRC-96 half. The two are complementary and this is the more important one.

The failure it fixes

x-bsv-payment carries the payment transaction plus its ancestry. Chained payments each spend the previous payment's unconfirmed change, so every payment re-ships the whole unconfirmed run, growing ~0.3 KB per payment until:

400 Request Header Or Cookie Too Large

Unsigned HTML from cdn-cgi, refused before the origin sees it. Reproduced deliberately on mainnet at 33,058 bytes.

Two properties make it worse than a slow leak:

  • It does not self-correct. A refused payment is still broadcast and paid for, so it becomes one more ancestor and the next attempt is larger. Observed 32,870 → 33,174 that way — a satoshi burnt per attempt, each attempt further from working.
  • The error points at the wrong thing. Because Cloudflare's refusal is unsigned, the client library reports missing headers: x-bsv-auth-version, x-bsv-auth-identity-key, x-bsv-auth-signature. It reads like an authentication bug, and there is nothing in the origin log to contradict that.

BRC-96 (#445) shrinks the payload but does not remove the ceiling, and cannot help on first contact with a new recipient — nothing is known yet. BRC-118 removes the ceiling.

What the spec says

BRC-118 — Multipart Body Transport is motivated by exactly this:

HTTP headers are subject to practical size limits that vary by infrastructure: Cloudflare Workers enforce approximately 128 KB total header size; Most reverse proxies and CDNs enforce 8–16 KB per header value

…empirical data showing individual BEEFs reaching 155.7 KB… These payloads cannot be transmitted as HTTP header values.

The payment moves into a multipart/form-data body — an x-bsv-payment part (application/json) and a body part carrying the original payload — with the same verification pipeline and all other BRC-105 semantics unchanged. Negotiated by the server advertising x-bsv-payment-transports: header,multipart on the 402, with a recommended 8 KB switch threshold.

Open questions

  1. Signing. The spec says the client signs the complete request as transmitted, including the multipart Content-Type and body, and that "Authentication verification MUST occur before multipart extraction". Does AuthFetch/BRC-104 currently sign the transmitted bytes, or a normalised form? If normalised, a naive implementation produces requests that fail signature verification and look like auth bugs — the same misdiagnosis trap as the Cloudflare refusal.
  2. Adoption order. The client only uses multipart when the server advertises it, so a client-only change is inert and a server-only change is unused. Which side moves first, and does the SDK need a way to force it for testing?
  3. Threshold. The spec recommends switching at 8 KB. Worth confirming that against the ~11 KB floor a single real payment already occupies, since that floor means many first payments would switch immediately.

@ty-everett mentioned in #445 wanting to "mess around with some implementations" of this — opening this so there is somewhere for that to land. Happy to help or to take a first pass at the client side if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions