agentleFS
po4ykapo4yka/rust-skills/skills/rust-networking/SKILL.md

rust-networking

Skill5 starsChanged 10 days agoBSD-3-ClauseView on GitHub

Use when building or reviewing a production Rust network client or server with reqwest, hyper, axum, tower-http, tonic, or rustls; setting an HTTP timeout or deadline budget; retrying safely with Retry-After and jitter; keeping TLS verification on; configuring a connection pool, proxy, or DNS; enforcing a response body limit or overload bound; or adding graceful shutdown. Not for select! or task cancel safety; use `rust-async-internals`.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

Show its rules and text

We split this skill into the rules it gives an agent

28 rules. About 0 are common, 16 are distinctive, and 12 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈16

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 12

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use the timeout, retry, pool, body-limit, and shutdown controls of the stack that the workspace already uses.

    line 10
  2. Very specific:

    Use these skills, when they are installed, for adjacent work: rust-async-internals for select!, task ownership, cancel safety, and runtime …

    line 14
  3. Distinctive:

    Do not copy timeout values from another service.

    line 36
  4. Distinctive:

    Read references/stack-defaults.md when the workspace uses one of these crates: it has the reqwest 0.13 upgrade changes, working client and …

    line 57
  5. Distinctive:

    Run these checks when you add or review network configuration.

    line 63
  6. Distinctive:

    Use the TLS backend that the workspace already selected.

    line 102
  7. Distinctive:

    Verify the certificate chain and the server name on every production connection.

    line 104
  8. Very specific:

    Keep certificate and hostname bypass APIs (the rg check above lists them) out of production code paths.

    line 112
  9. Very specific:

    Keep private keys out of logs and error chains.

    line 117
  10. Distinctive:

    Use certificate pinning only when the product has a rotation and recovery plan.

    line 122
  11. Distinctive:

    Do not force a protocol-version policy that conflicts with the platform or the service contract.

    line 125
  12. Distinctive:

    Create one monotonic deadline at the operation boundary.

    line 131
  13. Distinctive:

    Before each phase, calculate this duration:

    line 135
  14. Very specific:

    Use tokio::time::timeout_at or the runtime equivalent when the stack does not accept a deadline directly.

    line 158
  15. Distinctive:

    Use this default classification, then narrow it for the target service:

    line 179
  16. Very specific:

    Do not retry every 5xx. A retry can multiply overload and repeat an application failure.

    line 194
  17. Very specific:

    Use exponential backoff with full jitter.

    line 208
  18. Distinctive:

    Do not sleep past the operation deadline.

    line 214
  19. Distinctive:

    Keep delay calculation pure.

    line 216
  20. Distinctive:

    Do not trust the declaration as the only limit.

    line 224
  21. Distinctive:

    Never add an unbounded queue to hide backpressure.

    line 236
  22. Distinctive:

    Stop reading after cancellation, a body-limit error, or an expired deadline.

    line 243
  23. Very specific:

    Read references/cancellation.md when a caller can cancel a state-changing request, or the application must recover from an Outcome unknown …

    line 249
  24. Very specific:

    Read references/proxy-and-dns.md when you key or reuse a connection pool, or configure a proxy, NO_PROXY, a resolver, or a DNS cache.

    line 254
  25. Very specific:

    Read references/overload-and-shutdown.md when you set server admission, concurrency, queue, or handler-timeout limits.

    line 259
  26. Very specific:

    Read references/overload-and-shutdown.md when you add or review graceful shutdown for a server or a client.

    line 264
  27. Very specific:

    Read references/diagnostics.md when you add a log field, metric label, or trace attribute to network code.

    line 269
  28. Very specific:

    Test policy without a real public network.

    line 274

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Rust Networking

Apply this policy to HTTP, gRPC, WebSocket, and custom TCP clients and servers. Use the timeout, retry, pool, body-limit, and shutdown controls of the stack that the workspace already uses. Do not replace the framework to get one middleware type.

Use these skills, when they are installed, for adjacent work: rust-async-internals for select!, task ownership, cancel safety, and runtime configuration; rust-observability for tracing, metrics, and exporters; rust-security for dependency advisories and untrusted-input parser hardening.

Start from one operation contract

Record these decisions next to the client or server configuration before you tune individual settings:

FieldRequired decision
Operation deadlineMaximum wall-clock time for all attempts and delays
Phase capsDNS, connect, write idle, first-byte, and read idle caps (see "Use one absolute deadline")
Retry attemptsTotal attempts, including the first attempt
Retry eligibilityOperation semantics, replayable body, and retryable result
Response limitMaximum decoded bytes accepted by the caller
Concurrency limitMaximum admitted work per process, route, or upstream
Server header limitMaximum header bytes, field count, and read time
Server idle limitsKeep-alive, request-body, and response-write idle caps
Shutdown graceMaximum time allowed for accepted work to drain

Do not copy timeout values from another service. Derive them from the caller's deadline and the service latency objective. Keep all values configurable. Give each value a finite production default when the caller does not supply one.

Set the limits the stack leaves open

Most Rust network stacks ship without the limits in the contract. An unset limit compiles and passes tests, then fails under a slow or hostile peer. Set each one explicitly:

StackDefaultConsequence
reqwest 0.13 async ClientNo timeout, connect_timeout, or read_timeout; pool_max_idle_per_host is usize::MAXA stalled peer holds a request forever; the idle pool has no bound
reqwest Response::bytes, text, jsonCollect the whole body with no size limitA large response exhausts memory
hyper 1.x HTTP/1 serverheader_read_timeout (30 s) acts only after Builder::timer; a configured value with no timer panicsSlow-header clients hold connections
axum::serve 0.8Builds its hyper builder with no timerNo header-read timeout at all
axum with_graceful_shutdownWaits for open connections with no deadlineOne open request stalls shutdown
tower-http TimeoutLayer::newDeprecated since 0.6.7; answers 408A client can repeat even a POST that the handler already ran
tonic 0.14 EndpointNo connect timeout; timeout does not send grpc-timeoutThe server never sees the deadline, so it cannot stop at it or pass it downstream
rustls 0.23Zero or two provider features (or custom-provider) and no installed providerClientConfig::builder() panics at run time

Read references/stack-defaults.md when the workspace uses one of these crates: it has the reqwest 0.13 upgrade changes, working client and server setups, and the rustls and Android TLS setup.

Verify the configuration

Run these checks when you add or review network configuration. Review each hit; a hit is not always a defect.

rg -n --type rust 'danger_accept_invalid|tls_danger_|\.dangerous\(\)|tls_sslkeylogfile\(true\)|KeyLogFile|DefaultBodyLimit::disable|\bTimeoutLayer::new|axum::serve\(|with_graceful_shutdown\(|\breqwest::get\(|\bClient(Builder)?::(new|builder)\(\)|\.(bytes|text|json)\(\)\.await|\.json::<'
cargo tree --locked -e features -i rustls --prefix none | grep -oE '^rustls feature "(ring|aws_lc_rs|custom-provider)"' | sort -u
cargo deny --config deny.toml --locked check advisories
CheckProvesDoes not prove
rgEach bypass, key log, unbounded body, 408 timeout, and unbounded serve or client construction is visibleThat the configured values fit the contract
cargo treeWhich rustls provider features are on. Any output other than a single ring or aws_lc_rs line means main must install a providerThat the provider is installed before first TLS use
cargo denyNo known vulnerability in rustls, h2, hyper, quinn, or the rest of the graph. An unsound advisory in a transitive crate fails only with unsound = "all" (the rust-security skill owns this deny.toml policy)Protocol correctness

A green grep does not prove the timing contract; the tests in "Test deterministic seams" do. The work is complete when each contract field and each stack-defaults row has a finite, configurable, tested value, and the TLS root source, the pool key, the outcome-unknown error state, and the diagnostics field list match their sections. In a review, each gap is a finding.

Failure triage

SymptomLikely causeFirst check
Request exceeds its advertised timeoutEach retry received a fresh timeoutTrace one absolute deadline across attempts
POST executes twiceMethod-only retry policyCheck idempotency and body replayability
Retry storm during outageNo jitter, no attempt cap, or ignored Retry-AfterInspect delay and total attempt metrics
TLS works only in developmentProduction root source differs or verifier bypass leaks into testsPrint root source and certificate error class
Requests use the old endpointLive pooled connection survives DNS changeCompare pool reuse and connect events
Memory grows under slow clientsBody collection or unbounded queueInspect decoded-byte and queue limits
HTTP/2 overloads the serviceConnection limit is used as request limitInspect in-flight streams and route permits
Shutdown never finishesNew work is still admitted, each step resets grace, or axum with_graceful_shutdown has no outer deadlineTrace admission close and one deadline
Slow clients exhaust server connectionsaxum::serve or a hyper builder without a timer has no header-read timeoutSend a partial header and time the close
Panic "Could not automatically determine the process-level CryptoProvider"Zero or two provider features (or custom-provider) and no installed providerRun the cargo tree check; install one provider in main
Metrics leak user dataRaw URI, header, or error text is a labelApply the closed field vocabulary

Keep TLS verification on

Use the TLS backend that the workspace already selected. Apply these rules:

  • Verify the certificate chain and the server name on every production connection.
  • Load trust roots from one explicit source. Choose the platform store when the product must honor managed enterprise roots. Choose a bundled store when the product requires the same roots on every target. A reqwest 0.12 rustls-tls build (bundled webpki roots) silently moves to the platform verifier on 0.13; call tls_certs_only(roots) to keep a bundled or private set.
  • Fail startup or client construction when the required root store is empty.
  • Keep certificate and hostname bypass APIs (the rg check above lists them) out of production code paths. A test verifier must not be reachable from runtime configuration.
  • Send SNI for a DNS name. Verify the original service name, not the resolved IP address.
  • Keep private keys out of logs and error chains. Never enable key logging in production: reqwest tls_sslkeylogfile(true) and rustls KeyLogFile write session secrets to the file that SSLKEYLOGFILE names.
  • Rebuild the client and drain old pooled connections after a trust-root, client-certificate, or private-key rotation.
  • Use certificate pinning only when the product has a rotation and recovery plan. Ship at least one backup identity before the active identity changes.

Do not force a protocol-version policy that conflicts with the platform or the service contract. Use the secure defaults of the maintained TLS backend. Raise the minimum only when the deployment matrix proves that every peer supports it.

Use one absolute deadline

Create one monotonic deadline at the operation boundary. Carry it through DNS, connect, proxy negotiation, TLS, every request attempt, response streaming, and retry delay. Do not restart the full timeout after a retry.

Before each phase, calculate this duration:

phase allowance = min(configured phase cap, operation deadline - monotonic now)

Fail before the phase starts when no time remains. Report which phase consumed the budget. Do not report every deadline failure as connect timeout. Read references/deadlines-and-retries.md when you set the phase caps: it gives the start, the end, and the failure mode of each cap.

On a server, start the header-read deadline when the connection is accepted. A slow client must not hold an accepted-connection slot without completing a bounded header.

An idle timeout is not a total transfer timeout. Reset it only after useful progress. A peer that sends one byte before every idle timeout can still consume the operation deadline, so enforce both. With tower-http, pair RequestBodyTimeoutLayer (idle) with RequestBodyDeadlineLayer (total, tower-http 0.7+). With reqwest, pass the remaining time to RequestBuilder::timeout on every attempt.

Use tokio::time::timeout_at or the runtime equivalent when the stack does not accept a deadline directly. Dropping the timed future cancels it, so check its cancel safety.

Retry only a safe operation

Retry only when all three conditions hold:

  1. The operation is idempotent by protocol semantics or by an application idempotency key that the server stores atomically.
  2. The complete request can be replayed byte for byte. A consumed stream is not replayable unless the caller can open a fresh stream.
  3. The failure is transient and the operation deadline can hold another attempt plus its delay.

HTTP GET, HEAD, PUT, DELETE, OPTIONS, and TRACE have idempotent method semantics. Application behavior can still make an operation unsafe to repeat. Do not infer replay safety from the method name alone. Do not retry POST unless the application contract provides idempotency or proves that the server did not apply the request.

Use this default classification, then narrow it for the target service:

ResultAutomatic retry
DNS temporary failureYes, if another attempt fits
Connect refused, reset, or timeoutYes, for an eligible operation
Connection closes before response headersYes, for an eligible operation
HTTP 408, 429, 502, 503, or 504Yes, when service policy permits it
HTTP Retry-AfterWait at least the valid server delay, within the deadline
Other 4xxNo
TLS certificate or hostname failureNo
Request encode or validation failureNo
Body stream failed after response headersNo by default
Cancellation or exhausted deadlineNo

Do not retry every 5xx. A retry can multiply overload and repeat an application failure. Keep the retry status set explicit.

Count the first request as attempt one. Use a small finite attempt limit. Cap retries again with the operation deadline. Never retry forever in a background task.

Also bound retries per upstream across the process. Require a retry permit or token before a second attempt. Limit retry concurrency, and suppress retries when the local service is overloaded or the upstream failure rate crosses the configured threshold. If no retry budget remains, return the original failure instead of adding outage load. tower::retry::budget::TpsBudget implements such a budget for a tower stack.

Use exponential backoff with full jitter. Let base be the first cap and maximum be the largest cap. For retry number n, starting at zero, select a uniform delay in 0 ..= min(maximum, base * 2^n).

Parse both Retry-After forms: delay seconds and HTTP date. Reject invalid or negative values. Use the larger of the server delay and the local jitter delay. Do not sleep past the operation deadline. Clamp arithmetic to prevent overflow.

Keep delay calculation pure. Pass the random sample and current time into it. This seam makes retry tests deterministic without a mock HTTP framework. Read references/deadlines-and-retries.md when you write the delay function: it has a tested implementation.

Stream with hard limits and backpressure

Reject a declared body length that exceeds the route limit before allocation. Do not trust the declaration as the only limit. Count actual decoded bytes as chunks arrive, and stop when the count exceeds the limit.

Apply limits to the representation that the application consumes. A small compressed body can expand into a large decoded body. If the stack exposes both wire and decoded sizes, limit and record both.

Process large bodies as streams. Do not call a collect-to-bytes helper unless the route limit is small enough to allocate safely. On a server, use http_body_util::Limited, axum::body::to_bytes(body, limit), or a per-route DefaultBodyLimit::max(n). Put a bounded channel between network reads and a slower consumer. Await capacity or cancel the request when the channel is full. Never add an unbounded queue to hide backpressure.

For uploads, make replayability explicit. A byte buffer is replayable. A file is replayable only when each attempt opens a new handle and starts at the same offset. A live channel, socket, decoder, or one-shot generator is not replayable.

Stop reading after cancellation, a body-limit error, or an expired deadline. Release or reset the protocol stream as the library requires. Do not return a connection to the pool when its protocol state is uncertain.

Define cancellation at the network boundary

Read references/cancellation.md when a caller can cancel a state-changing request, or the application must recover from an Outcome unknown result.

Treat proxy and DNS as connection identity

Read references/proxy-and-dns.md when you key or reuse a connection pool, or configure a proxy, NO_PROXY, a resolver, or a DNS cache.

Bound server overload

Read references/overload-and-shutdown.md when you set server admission, concurrency, queue, or handler-timeout limits.

Shut down in a fixed order

Read references/overload-and-shutdown.md when you add or review graceful shutdown for a server or a client.

Emit safe network diagnostics

Read references/diagnostics.md when you add a log field, metric label, or trace attribute to network code.

Test deterministic seams

Test policy without a real public network. Read references/test-seams.md when you write a network policy test: it maps each control to the smallest seam that exposes it, and gives the paused-clock and test-run rules.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.