Adds resource bounds to the tonic Server::builder() chain in crates/nunciod/src/grpc.rs::serve_on_listener_with_overrides_and_shutdown: concurrency_limit_per_connection, a per-call timeout, max_concurrent_streams, TCP keepalive, and HTTP/2 keepalive interval/timeout.
Adds a per-service max_decoding_message_size (16 MiB, above tonic's unconfigured 4 MiB default so a real email with an attachment still fits) to every one of the 8 mounted services, applied before each service is wrapped in its existing BearerAuthInterceptor (auth wiring untouched; shutdown wiring from the prior WS-C1 story untouched).
Why the timeout doesn't break Subscribe or long Sync calls
tonic's Server::timeout wraps the handler's request future. For a server-streaming RPC (System.Subscribe) that future resolves as soon as the response stream is constructed, not when the stream finishes emitting items -- so a long-lived Subscribe connection is unaffected. For unary RPCs (Mail.Sync/Calendar.Sync/Contacts.Sync) the 300s deadline is a generous bound chosen to cover a slow real-world sync against a large mailbox while still failing a genuinely stuck backend call instead of hanging forever. This is a documented tradeoff, not a per-RPC exemption, since tonic's server-level timeout applies uniformly to the whole router.
Test plan
send_message_rejects_body_larger_than_max_decoding_message_size -- a body one byte over the configured 16 MiB limit is rejected with Code::OutOfRange at decode time, before the handler runs.
send_message_accepts_body_above_default_but_within_configured_limit -- a 6 MiB body (above tonic's unconfigured 4 MiB default, within the configured 16 MiB) decodes successfully and reaches the handler, proving the raised limit is real and not a no-op.
All existing E2E suites unchanged and green, including Subscribe streaming (subscribe_streams_mapped_core_events_from_the_live_event_bus, subscribe_rejects_missing_bearer_token) and Sync (spine_e2e_test, calendar_e2e_test, contacts_e2e_test, sync::tests::*).
## Summary
- Adds resource bounds to the tonic `Server::builder()` chain in `crates/nunciod/src/grpc.rs::serve_on_listener_with_overrides_and_shutdown`: `concurrency_limit_per_connection`, a per-call `timeout`, `max_concurrent_streams`, TCP keepalive, and HTTP/2 keepalive interval/timeout.
- Adds a per-service `max_decoding_message_size` (16 MiB, above tonic's unconfigured 4 MiB default so a real email with an attachment still fits) to every one of the 8 mounted services, applied before each service is wrapped in its existing `BearerAuthInterceptor` (auth wiring untouched; shutdown wiring from the prior WS-C1 story untouched).
- Named, documented constants: `GRPC_CONCURRENCY_LIMIT_PER_CONNECTION` (32), `GRPC_REQUEST_TIMEOUT` (300s), `GRPC_MAX_CONCURRENT_STREAMS` (64), `GRPC_MAX_DECODING_MESSAGE_SIZE_BYTES` (16 MiB), `GRPC_TCP_KEEPALIVE` (60s), `GRPC_HTTP2_KEEPALIVE_INTERVAL` (30s), `GRPC_HTTP2_KEEPALIVE_TIMEOUT` (20s).
### Why the timeout doesn't break `Subscribe` or long `Sync` calls
tonic's `Server::timeout` wraps the handler's request future. For a server-streaming RPC (`System.Subscribe`) that future resolves as soon as the response stream is constructed, not when the stream finishes emitting items -- so a long-lived `Subscribe` connection is unaffected. For unary RPCs (`Mail.Sync`/`Calendar.Sync`/`Contacts.Sync`) the 300s deadline is a generous bound chosen to cover a slow real-world sync against a large mailbox while still failing a genuinely stuck backend call instead of hanging forever. This is a documented tradeoff, not a per-RPC exemption, since tonic's server-level `timeout` applies uniformly to the whole router.
## Test plan
- [x] `send_message_rejects_body_larger_than_max_decoding_message_size` -- a body one byte over the configured 16 MiB limit is rejected with `Code::OutOfRange` at decode time, before the handler runs.
- [x] `send_message_accepts_body_above_default_but_within_configured_limit` -- a 6 MiB body (above tonic's unconfigured 4 MiB default, within the configured 16 MiB) decodes successfully and reaches the handler, proving the raised limit is real and not a no-op.
- [x] All existing E2E suites unchanged and green, including `Subscribe` streaming (`subscribe_streams_mapped_core_events_from_the_live_event_bus`, `subscribe_rejects_missing_bearer_token`) and `Sync` (`spine_e2e_test`, `calendar_e2e_test`, `contacts_e2e_test`, `sync::tests::*`).
### Gate output
```
cargo fmt --all -- --check -> clean
cargo check-all -> clean, 0 warnings (clippy --all-targets --workspace -D warnings)
cargo test-all -> 536 passed; 0 failed; 0 ignored
```
No proto changes in this PR.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Server::builder()chain incrates/nunciod/src/grpc.rs::serve_on_listener_with_overrides_and_shutdown:concurrency_limit_per_connection, a per-calltimeout,max_concurrent_streams, TCP keepalive, and HTTP/2 keepalive interval/timeout.max_decoding_message_size(16 MiB, above tonic's unconfigured 4 MiB default so a real email with an attachment still fits) to every one of the 8 mounted services, applied before each service is wrapped in its existingBearerAuthInterceptor(auth wiring untouched; shutdown wiring from the prior WS-C1 story untouched).GRPC_CONCURRENCY_LIMIT_PER_CONNECTION(32),GRPC_REQUEST_TIMEOUT(300s),GRPC_MAX_CONCURRENT_STREAMS(64),GRPC_MAX_DECODING_MESSAGE_SIZE_BYTES(16 MiB),GRPC_TCP_KEEPALIVE(60s),GRPC_HTTP2_KEEPALIVE_INTERVAL(30s),GRPC_HTTP2_KEEPALIVE_TIMEOUT(20s).Why the timeout doesn't break
Subscribeor longSynccallstonic's
Server::timeoutwraps the handler's request future. For a server-streaming RPC (System.Subscribe) that future resolves as soon as the response stream is constructed, not when the stream finishes emitting items -- so a long-livedSubscribeconnection is unaffected. For unary RPCs (Mail.Sync/Calendar.Sync/Contacts.Sync) the 300s deadline is a generous bound chosen to cover a slow real-world sync against a large mailbox while still failing a genuinely stuck backend call instead of hanging forever. This is a documented tradeoff, not a per-RPC exemption, since tonic's server-leveltimeoutapplies uniformly to the whole router.Test plan
send_message_rejects_body_larger_than_max_decoding_message_size-- a body one byte over the configured 16 MiB limit is rejected withCode::OutOfRangeat decode time, before the handler runs.send_message_accepts_body_above_default_but_within_configured_limit-- a 6 MiB body (above tonic's unconfigured 4 MiB default, within the configured 16 MiB) decodes successfully and reaches the handler, proving the raised limit is real and not a no-op.Subscribestreaming (subscribe_streams_mapped_core_events_from_the_live_event_bus,subscribe_rejects_missing_bearer_token) andSync(spine_e2e_test,calendar_e2e_test,contacts_e2e_test,sync::tests::*).Gate output
No proto changes in this PR.