This document records the nginx adapter contract. It does not redefine the
public C-client API or policy: those belong to the version-matched
rl-c-client documentation linked below.
The module uses the standalone rl-c-client repository as its protocol and
client-policy engine. The supported tag and full commit SHA are recorded in
../dependencies/rl-c-client.env. Fetch the
locked public release with:
./tools/fetch-rl-c-client.sh
The default checkout is ./_deps/rl-c-client. Set
RCLIENT_DIR=/path/to/rl-c-client to test an intentional development or
packaging checkout; doing so does not change the supported release revision.
Without an override, build and test entrypoints fetch or verify the locked
checkout before use and never fall back to an adjacent development checkout.
The default checkout must have both the locked HEAD and a clean working tree.
Generated files ignored by the C-client repository are permitted; tracked or
untracked local source changes are rejected. An explicit RCLIENT_DIR is the
only supported way to opt into a dirty development tree.
| Client-owned topic | Authoritative v1.0.0 documentation | nginx-specific consequence |
|---|---|---|
| Logical operations | Operation Model | Each protected main request creates one resource request; an explicitly configured post-response latency report is separate and may also be used without admission. |
| Integration layer | Choosing an integration layer | nginx uses the core borrowed API because it already owns UDP, DNS, timers, logging, and request pools. |
| Credentials and quotas | Credentials | nginx accepts format-1 keys only, uses the latency-buffer quota when buffer_size is omitted, and relies on the client to reject resource windows above rate_window_size_ms_max before transmission. |
| State identity | Content-defined IDs | nginx renders names and passes the defining settings to the canonical ID helpers. |
| Delivery and selection | Resource-Request HA Policy | ratelimitly_policy selects the complete policy passed to the C client. |
| Discovery | DNS Refresh and I/O abstraction | nginx supplies the asynchronous resolver and worker-local UDP adapter. |
| Failure surface | Error Codes | Client failures are mapped through ratelimitly_fail; valid denials are never fail-opened. |
The HA policy sends each resource request to every currently usable member. When a request contains resources, it is mutating. rl-nginx neither selects one deterministic commit target nor deduplicates writes across targets. Supported deployments therefore require a RateLimitly topology whose server/protocol semantics make that fan-out one logical consumption. This external commit-safety property is not replaced by an nginx-module fallback.
r_client_check_rate_limit_async_borrowed, retaining rendered
request inputs in an nginx request pool until callback or cancellation.r_client_io.h; the client
still owns discovery state, authentication, packets, policy, deadlines, and
response selection.ratelimitly_policy standard, single_round, or the complete
custom form to an r_request_policy_t, validates its derived horizon
against the credential, and passes it unchanged to the C client.custom policy whose oldest_preference is smaller than its
replay_gap delivers its verdict at the later round deadline instead. The
standard and single_round policies are unaffected, because their
preference equals their gap in every round.The version lock covers more than source and wire compatibility. rl-nginx
depends on the following callback and ownership behavior. A C-client update is
not compatible until every item remains true:
r_client_create borrows its configuration, tenant/authentication strings,
request policy, and adapter tables only for the duration of the call. On
success the caller may release or overwrite those inputs. Resolver callbacks
invoked during creation may be synchronous, as declared by r_client_io.h.r_client_check_rate_limit_async_borrowed returning an error does not invoke
the rate callback and does not publish a live request. Returning
RCLIENT_OK publishes a live request without invoking its completion
callback before the start call returns.r_client_request_deadline_ms succeeds for a live request. Calling
r_client_on_timeout before a reported deadline leaves the request pending.
On the no-response path, at the first deadline it performs the configured
replay; at the second it enters the final receive-only interval; at the
third it invokes the timeout callback synchronously and exactly once. Any
valid response may change the next deadline or complete earlier, and the
request handle is invalid as soon as the callback completes.r_client_cancel_request synchronously retires the request without invoking
its callback. Late or duplicate datagrams cannot revive it. Once cancel
returns, the module may release all borrowed request inputs.r_client_destroy retires inflight rate requests without invoking their
callbacks. It detaches internal DNS refresh state before calling the
resolver’s cancellation hook; the hook may synchronously deliver its pending
DNS callback without re-entering a half-destroyed client.These requirements deliberately state behavior that is not fully expressible
through the C header types. They are compatibility requirements for
rl-nginx, not a claim that arbitrary C-client versions provide them.
tests/test-c-client-contract.sh links directly against the selected client
and its released responder fixture. It covers create-time borrowing,
synchronous DNS callbacks, start success and failure, callback identity and
cardinality, deadline behavior, synchronous timeout completion, cancellation,
late datagrams, inflight destruction, and resolver-cancel reentrancy.
The probe is part of make unit, the required CI hygiene job, the sanitizer
setup, and the scheduled rl-c-client/main drift job. A future lock bump must
make this probe pass before module compilation or lifecycle tests can be used as
compatibility evidence.