Skip to content

fix(ingest): keep three request costs bounded per request - #2566

Merged
Makisuo merged 3 commits into
mainfrom
sec/ingest-request-bounds
Nov 13, 2026
Merged

Makisuo merged 3 commits into
mainfrom
sec/ingest-request-bounds

Conversation

@Makisuo

@Makisuo Makisuo commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

What

Part of a pass over every API surface. Three places where one request to the ingest gateway could cost more than its size suggests.

  • Session replay chunks. The chunk is inflated on the blocking pool, as OTLP bodies already are. The decoded ceiling is 8x the body limit, and on a one-core host the async workers are a single thread, so this work no longer sits on it. It reserves what it inflates from the shared decode memory first, and a chunk that does not fit is shed with the retryable busy answer.
  • Vercel audit deliveries. A delivery is refused with 413 past 10,000 events. Each event becomes a stored row of fixed columns however short it is, so the 4 MiB byte ceiling alone did not bound the rows or the memory a delivery takes. Real deliveries are a handful of events.
  • Oversized credentials. A bearer or x-maple-ingest-key value longer than 256 bytes is treated as no credential. It is no longer hashed, looked up in Postgres or held in the unknown-key cache.

No change to what an accepted request stores.

Verification

cargo test --locked --bin maple-ingest -- extract_ingest_key vercel_audit_drain replay_: all passing, including new tests (a delivery of 10,001 short events is a 413 and nothing reaches the audit trail; an oversized key is ignored in both headers while a 256-byte one is still read). The existing replay tests cover the moved decode (blob store path, inline path, gzip bomb refusal), and the bomb test now also sheds a chunk while the decode memory is full.

Not run locally: the load tests. The change moves existing work to another thread and adds two comparisons, so no throughput change is expected.

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

- A session replay chunk is inflated on the blocking pool, like an OTLP body, so a chunk that inflates to the decoded ceiling does not hold the async workers.
- A Vercel audit delivery is refused past 10,000 events. Every event becomes a row of fixed columns, so the byte ceiling alone did not bound what a delivery stores.
- A credential longer than any key Maple issues is treated as no credential, before it is hashed, looked up or cached as unknown.
@maple-review-bot

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 37 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a09c1e02-b291-4b57-8687-93cf2b52fc99
Commits

Reviewing files that changed from the base of the PR and between b4fd88f and 58bcbb1.

Files selected for processing: 1

  • apps/ingest/src/main.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

Comment thread apps/ingest/src/main.rs Resolved
On the blocking pool many chunks can inflate at once, so each reserves its body and declared size first and is shed with the retryable busy answer when that does not fit. An inline chunk keeps its text reserved until it is stored.
@maple-review-bot

This comment has been minimized.

…rved

The reservation is sized from the gzip trailer, which the sender writes. The inflate now stops at that declared size, so a trailer that understates the stream is refused and cannot get more decoded than was reserved.
@Makisuo

Makisuo commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining decode-memory point in the latest commit: the inflate now stops at the declared size that was reserved, so a trailer that understates the stream is a 413 and nothing past the reservation is decoded. The bomb test has a case for it.

@maple-review-bot

maple-review-bot Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Maple review

🟢 Confidence 10/10 · safe to merge. The replay decode now reserves from decode_memory before inflating and the shed/bounds paths are each pinned by a new test.
no findings · tests covered · risk medium · 2/2 new units observable

Replay chunk inflate moves to the blocking pool inside the shared decode-memory budget, Vercel audit deliveries are capped at 10,000 events, and ingest credentials over 256 bytes are ignored. The earlier budget-escape finding is fixed; nothing else in the diff is unsafe.

  • Replay inflate runs on the blocking pool inside state.decode_memory
  • gzip_declared_len stops the inflate at the size reserved
  • Audit deliveries past AUDIT_TRAIL_MAX_EVENTS answer 413
  • extract_ingest_key ignores values longer than 256 bytes

Fixed since the last review

  • ✅ F1 · Replay decode on the blocking pool escapes state.decode_memory

58bcbb1 · What was checked, coverage and telemetry details are on the Maple / review check. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@Makisuo
Makisuo merged commit 6f66550 into main Nov 13, 2026
26 checks passed
@Makisuo
Makisuo deleted the sec/ingest-request-bounds branch November 13, 2026 16:25
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