Skip to content

5.11.0 — 2026-10-13

← 5.12.0 | 5.10.0 → | ↑ 5.x series

Added

Configs - Auth

  • AUTH.JWT_LEEWAY - Clock-skew tolerance in seconds applied to exp, nbf and iat when verifying tokens (default 0).

Configs - Keycloak

  • KEYCLOAK.REMOTE_CACHE_TTL_SECONDS - Opt-in cache for UserInfo and UMA permission lookups in KeycloakUtils (default 0, off). A revoked token stays accepted for up to this long when enabled.

Helpers - Utils

  • verify_totp replay guard - verify_totp takes an optional replay_guard(step) callable that is called with the matched time step for a valid code. Returning False rejects the code.
  • keep_zwnj option - clean_spacing and normalize_persian_text can keep the zero-width non-joiner instead of turning it into a space.

Helpers - Interceptors

  • request_validation() - Context manager exported from archipy.helpers.interceptors.grpc.exception; a Pydantic ValidationError raised inside it is reported as INVALID_ARGUMENT.
  • trusted_proxy_hosts - The rate-limit interceptors read x-forwarded-for only when the direct peer is a trusted proxy, so callers behind a load balancer no longer share one bucket.

Changed

Helpers - Utils

  • BREAKING: HMAC signing keys have a minimum length - JWTUtils raises InvalidArgumentError when SECRET_KEY is shorter than RFC 7518 requires: 32 bytes for HS256, 48 for HS384 and 64 for HS512. It checks on both create and decode, and refuses the none algorithm.
  • Password hash format - Hashes are stored as pbkdf2_sha256$iterations$salt$hash, and the default AUTH.HASH_ITERATIONS is now 600000.
    • Hashes in the old format still verify, using the configured iterations and then 100000
    • PASSWORD_HISTORY_SIZE=0 disables the history check, and negative values are rejected
  • TOTP expiry and secrets - generate_totp reports an expiry no later than the end of the verification window. Secrets that are valid base32 are decoded, so authenticator apps produce matching codes.
  • BREAKING: additional_claims cannot redefine registered claims - JWTUtils.create_token and the token helpers raise InvalidArgumentError when additional_claims contains iss, aud, jti or any key already set by the token data (for example sub, type and token_version). Time claims such as exp can still be overridden.
  • Persian punctuation - normalize_punctuation converts ASCII ,, ?, ;, % and _ only next to Persian text, so numbers (1,000), URLs, e-mails and identifiers are left intact. remove_punctuation still strips them.

Helpers - Interceptors

  • BREAKING: handler ValidationError maps to INTERNAL - A Pydantic ValidationError raised by handler code is a server fault, so it is no longer reported as INVALID_ARGUMENT.
    • Wrap request parsing in request_validation() to keep INVALID_ARGUMENT
    • Pass validation_errors_as_invalid_argument=True to the exception interceptors to restore the old mapping
  • Interceptor order - customized_interceptors accepts a sequence and keeps its order; a set still works but has no guaranteed order.
  • Rate-limit startup - A failed rate-limit setup raises when GRPC_RATE_LIMIT.FAIL_CLOSED is set instead of starting an unprotected server.

Fixed

Helpers - Interceptors

  • Deliberate aborts are kept - The sync exception interceptor no longer rewrites context.abort() (for example RESOURCE_EXHAUSTED from the rate limiter) to INTERNAL, and the async one no longer aborts a second time after AbortError.
  • Internal details stay server-side - Unexpected errors send a bare InternalError; the error text, type and method names are no longer put in trailing metadata. Validation errors no longer echo the submitted input, and trailing metadata is capped at 4096 bytes and serialized with default=str.
  • Rate limits behind other interceptors - The base interceptor closures use functools.wraps, so decorated RPCs are still limited when a custom interceptor follows the rate limiter.
  • Rate-limit edge cases - A handler that returns None no longer runs twice when failing open, a rejected call refunds the quota of earlier windows, a failing identifier_fn skips limiting instead of pooling callers, and JWT verification runs off the event loop. Window metadata and the auth config are cached.
  • Metrics statuses - Cancelled RPCs and closed streams are recorded as cancelled, the final gRPC status code is recorded as rpc.grpc.status_code, and handled client errors count as ok.
  • generic_exception_handler - Records the real exception instead of a bare UnknownError.
  • Rate-limit windows - Negative time components are rejected.

Helpers - Utils

  • TOTP - The code length comes from TOTP_LENGTH, so a one-digit code can no longer match, and non-ASCII digits are rejected instead of raising TypeError.
  • Keycloak auth - grpc_auth and async_grpc_auth support streaming handlers and keep the auth context through the stream, aborts pass through, token parsing is case-insensitive on the scheme and rejects repeated or empty authorization values, and failure causes are logged instead of sent to the client.
  • OTel - A failed initialization retries only after a 30 second back-off, shut-down providers are tracked in a WeakSet, the Pushgateway loop sends a final push on shutdown, and the shared instrument cache is locked.
  • Datetime - A %z offset parsed from a string is kept, the holiday cache is locked and bounded, daterange counts calendar dates, and a non-JSON holiday response raises UnknownError.
  • Strings - Noon and Zal tables no longer corrupt text, snake_to_camel_case keeps inner capitals, replace_numbers_with_mask works in one pass, the currency mask no longer matches backslashes, and punctuation spacing no longer splits file.txt.
  • Files and national codes - Allowed extensions match case-insensitively and with a leading dot; the national-code validator rejects non-ASCII digits and repeated-digit codes.
  • Singleton - Uses a reentrant lock, so a singleton can build another singleton.

Tests

  • BDD coverage - Added scenarios to the existing TOTP, password, JWT, datetime, string, file, base utils, singleton, error handling, app utils, gRPC helpers, rate-limit, OTel and Keycloak features, including real in-process sync and async gRPC servers for the abort, metrics and rate-limit behavior.