diff --git a/internal/transport/transport.go b/internal/transport/transport.go index 10f9ba3..4ba6a1f 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -35,6 +35,21 @@ const ( PathDevices = "/v1/agent/devices" ) +// DefaultMaxBytes bounds a relay response body. +// +// It has to admit a full long-poll batch, which is the largest thing the relay can legitimately send: +// one envelope per mailbox slot. The previous 1 MiB was chosen as "generous" without being derived +// from anything, and it is smaller than what the relay is now allowed to send — a mailbox of eight +// envelopes at the policy's ceiling is about 4 MiB, so a full batch would have been refused as +// "relay response exceeds 1048576 bytes", and the agent would have retried a poll the relay answered +// correctly. That is the same class of defect as the relay's own envelope ceiling: a bound picked by +// feel, on the other side of a seam, disagreeing with the party that produces the data. +// +// The derivation is stated here rather than computed from the policy because the agent reads the +// policy for its own limits; the two numbers are pinned against each other by +// test/limits.test.js, which is where a change on either side becomes visible. +const DefaultMaxBytes int64 = 8 << 20 + var ( // ErrSessionExpired means the relay no longer recognises this session. The caller re-runs the // handshake and polls again; it is not an error worth crashing over. @@ -142,7 +157,7 @@ func New(cfg Config) (*Client, error) { } maxBytes := cfg.MaxBytes if maxBytes <= 0 { - maxBytes = 1 << 20 + maxBytes = DefaultMaxBytes } client := &http.Client{Timeout: timeout} if cfg.RootCAs != nil {