From c43183978aeb6cadeb08cd28a9ced66f475c1451 Mon Sep 17 00:00:00 2001 From: fusion44 Date: Thu, 23 Apr 2026 10:48:41 +0200 Subject: [PATCH 01/16] feat: Swap out poerty2nix for uv2nix; remove devshell from flake --- flake.lock | 134 ++++++++++++++++++----------------------------------- flake.nix | 93 ++++++++++++++++++------------------- 2 files changed, 90 insertions(+), 137 deletions(-) diff --git a/flake.lock b/flake.lock index 6f2dd85..049d1b3 100644 --- a/flake.lock +++ b/flake.lock @@ -18,45 +18,6 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nix-github-actions": { - "inputs": { - "nixpkgs": [ - "poetry2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1729742964, - "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1739446958, @@ -73,69 +34,62 @@ "type": "github" } }, - "poetry2nix": { + "pyproject-build-systems": { "inputs": { - "flake-utils": "flake-utils_2", - "nix-github-actions": "nix-github-actions", "nixpkgs": [ "nixpkgs" ], - "systems": "systems_3", - "treefmt-nix": "treefmt-nix" + "pyproject-nix": [ + "pyproject-nix" + ], + "uv2nix": [ + "uv2nix" + ] }, "locked": { - "lastModified": 1746721691, - "narHash": "sha256-M01XrCCnM6/iTm5L4NYc0/GFJAnuxFsEsn9gdkB7pgY=", - "owner": "fusion44", - "repo": "poetry2nix", - "rev": "cc3336142abb08a2dd3eda202e572a26a39c68bc", + "lastModified": 1776659114, + "narHash": "sha256-qapCOQmR++yZSY43dzrp3wCrkOTLpod+ONtJWBk6iKU=", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "rev": "ffaa2161dd5d63e0e94591f86b54fc239660fb2e", "type": "github" }, "original": { - "owner": "fusion44", - "ref": "nb", - "repo": "poetry2nix", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", "type": "github" } }, - "root": { + "pyproject-nix": { "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "poetry2nix": "poetry2nix" - } - }, - "systems": { + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "lastModified": 1776715674, + "narHash": "sha256-Gs1VnEkCkkRZxJQAC/Dhz0Jbfi22mFXChbtNg9w/Ybg=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "69f57f27e52a87c54e28138a75ec741cd46663c9", "type": "github" }, "original": { - "owner": "nix-systems", - "repo": "default", + "owner": "pyproject-nix", + "repo": "pyproject.nix", "type": "github" } }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "pyproject-build-systems": "pyproject-build-systems", + "pyproject-nix": "pyproject-nix", + "uv2nix": "uv2nix" } }, - "systems_3": { + "systems": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -150,24 +104,26 @@ "type": "github" } }, - "treefmt-nix": { + "uv2nix": { "inputs": { "nixpkgs": [ - "poetry2nix", "nixpkgs" + ], + "pyproject-nix": [ + "pyproject-nix" ] }, "locked": { - "lastModified": 1730120726, - "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15", + "lastModified": 1776718528, + "narHash": "sha256-XeGmo/BhkFXd8vVyendr3X4mQmw7CEkeQcpy7AHbVcg=", + "owner": "pyproject-nix", + "repo": "uv2nix", + "rev": "60982c30e16db3e0cba6c0ed13f0894b06ab2bf1", "type": "github" }, "original": { - "owner": "numtide", - "repo": "treefmt-nix", + "owner": "pyproject-nix", + "repo": "uv2nix", "type": "github" } } diff --git a/flake.nix b/flake.nix index 10a15b1..0ed157e 100644 --- a/flake.nix +++ b/flake.nix @@ -4,68 +4,65 @@ inputs = { flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + pyproject-nix = { + url = "github:pyproject-nix/pyproject.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + uv2nix = { + url = "github:pyproject-nix/uv2nix"; + inputs.pyproject-nix.follows = "pyproject-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + pyproject-build-systems = { + url = "github:pyproject-nix/build-system-pkgs"; + inputs.pyproject-nix.follows = "pyproject-nix"; + inputs.uv2nix.follows = "uv2nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, nixpkgs, flake-utils, - poetry2nix, + pyproject-nix, + uv2nix, + pyproject-build-systems, + ... }: let name = "blitz-api"; + # Load the uv workspace once; reused across systems. + workspace = uv2nix.lib.workspace.loadWorkspace {workspaceRoot = ./.;}; + + # Pure Nix overlay derived from pyproject.toml + uv.lock. Prefer + # prebuilt wheels — a handful of deps (grpcio, psutil, pyzmq) are + # painful to compile from source and wheels work fine on linux/darwin. + overlay = workspace.mkPyprojectOverlay { + sourcePreference = "wheel"; + }; + systems = flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - inherit (poetry2nix.lib.mkPoetry2Nix {inherit pkgs;}) mkPoetryApplication overrides mkPoetryEnv; - poetryDev = mkPoetryEnv { - projectDir = ./.; - preferWheels = true; - overrides = overrides.withDefaults (final: prev: { - ruff = prev.ruff.override { - preferWheel = true; - }; - }); - }; + python = pkgs.python312; + + pythonSet = + (pkgs.callPackage pyproject-nix.build.packages { + inherit python; + }) + .overrideScope ( + nixpkgs.lib.composeManyExtensions [ + pyproject-build-systems.overlays.default + overlay + ] + ); in { packages = { default = self.packages.${system}.${name}; - ${name} = mkPoetryApplication { - projectDir = ./.; - meta.mainProgram = name; - }; - poetryDev = mkPoetryEnv { - projectDir = ./.; - preferWheels = true; - }; - }; - - devShells.default = pkgs.mkShell { - # TODO: dirty dirty to be able to run the app. May break other packages. - # https://discourse.nixos.org/t/using-nix-shells-without-polluting-repositories/37362 - shellHook = '' - export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ - with pkgs; - lib.makeLibraryPath [pkgs.stdenv.cc.cc.lib] - }" - ''; - nativeBuildInputs = with pkgs; [ - stdenv.cc.cc - poetry - poetryDev - pyright - alejandra - statix - ruff - ruff-lsp - redis - pueue - sshpass - - bitcoind - lnd - clightning - pueue - ]; + ${name} = pythonSet.mkVirtualEnv "${name}-env" workspace.deps.default; }; }); From 5fc0d04c2e0d490e3de8c48feb9f7e789d7c56eb Mon Sep 17 00:00:00 2001 From: fusion44 Date: Thu, 23 Apr 2026 11:18:09 +0200 Subject: [PATCH 02/16] feat: implement CLN LN backend in Nix module. --- modules/blitz_api.nix | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/modules/blitz_api.nix b/modules/blitz_api.nix index 9718766..dca7c2c 100644 --- a/modules/blitz_api.nix +++ b/modules/blitz_api.nix @@ -154,7 +154,10 @@ in { connectionType = lib.mkOption { type = types.enum ["none" "lnd_grpc" "cln_jrpc"]; default = "none"; - description = "Whether lighgning is enabled and which implementation is used."; + description = '' + Which lightning backend (if any) to wire up. "none" means the + API runs in Bitcoin-only mode and no LN service is required. + ''; }; lnd = { @@ -171,6 +174,20 @@ in { description = "The port to connect to"; }; }; + + cln = { + jrpcPath = mkOption { + type = types.path; + default = "${config.services.clightning.dataDir}/bitcoin/lightning-rpc"; + defaultText = literalExpression + ''"''${config.services.clightning.dataDir}/bitcoin/lightning-rpc"''; + description = '' + Absolute path to the clightning JSON-RPC socket. Must be + readable by the blitz-api service user (see the clightning + group membership added when connectionType = "cln_jrpc"). + ''; + }; + }; }; jwt = { @@ -283,6 +300,9 @@ in { home = mkIf (cfg.home != null) cfg.home; group = cfg.group; isSystemUser = true; + # CLN JSON-RPC mode needs socket access via the clightning group. + extraGroups = + lib.optional (cfg.ln.connectionType == "cln_jrpc") "clightning"; }; }; @@ -353,8 +373,8 @@ in { '' } ${ - lib.strings.optionalString (cfg.ln.connectionType == "cln_grpc") '' - echo "# CLN TODO" + lib.strings.optionalString (cfg.ln.connectionType == "cln_jrpc") '' + echo "BAPI_CLN_JRPC_PATH=${cfg.ln.cln.jrpcPath}" >> .env '' } @@ -365,7 +385,13 @@ in { services.${name} = rec { wantedBy = ["multi-user.target"]; - requires = ["bitcoind.service" "lnd.service"]; + # Bitcoind is always required; pull in the matching lightning + # service only when the connectionType asks for it. "none" runs + # the API in Bitcoin-only mode. + requires = + ["bitcoind.service"] + ++ lib.optional (cfg.ln.connectionType == "lnd_grpc") "lnd.service" + ++ lib.optional (cfg.ln.connectionType == "cln_jrpc") "clightning.service"; after = requires ++ ["blitz-api-setup-env.target" "nix-bitcoin-secrets.target"]; description = "${name} server daemon"; environment = lib.mkMerge [ @@ -379,7 +405,11 @@ in { # nbLib.defaultHardening // { ExecStart = "${cfg.package}/bin/api --port ${toString cfg.port} --host ${cfg.host} --root_path ${cfg.rootPath}"; - ExecStartPre = [ + # LND artifacts need to live inside the service's dataDir so + # the .env's BAPI_LND_MACAROON / BAPI_LND_CERT paths resolve. + # Nothing to copy for CLN (socket access is handled via the + # `clightning` group on cfg.user) or "none". + ExecStartPre = lib.optionals (cfg.ln.connectionType == "lnd_grpc") [ (nbLib.rootScript "${name}-prepare-data-dir" '' install -D -o ${cfg.user} -g ${cfg.group} ${lnd.networkDir}/admin.macaroon \ '${cfg.dataDir}/macaroons/admin.macaroon' From e691a956ae0fd74a004bbf170617f924d1471e3e Mon Sep 17 00:00:00 2001 From: fusion44 Date: Thu, 23 Apr 2026 13:34:20 +0200 Subject: [PATCH 03/16] feat: add celery services, persist auto-generated secrets --- modules/blitz_api.nix | 79 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/modules/blitz_api.nix b/modules/blitz_api.nix index dca7c2c..bed41f0 100644 --- a/modules/blitz_api.nix +++ b/modules/blitz_api.nix @@ -18,14 +18,20 @@ parts = strings.splitString ":" bitcoind.zmqpubrawblock; bitcoindZmqPort = builtins.elemAt parts 2; + # Persist auto-generated secrets so they survive rebuilds. Without + # this the JWT secret + local login password would regenerate on + # every nixos-rebuild, invalidating every token a logged-in client + # holds. Scripts are embedded into the env-file generator below; + # the `.jwt-secret` and `.login-password` sidecar files live in + # `${cfg.dataDir}`. jwtSecretScript = if cfg.jwt.secretFile != null then "$(head -n1 ${lib.escapeShellArg cfg.jwt.secretFile})" - else "$(cat /dev/urandom | tr -dc '[:alnum:]' | head -c 50)"; + else ''"$(head -n1 "${cfg.dataDir}/.jwt-secret")"''; loginPasswordScript = if cfg.passwordFile != null then "$(head -n1 ${lib.escapeShellArg cfg.passwordFile})" - else "$(cat /dev/urandom | tr -dc '[:alnum:]' | head -c 50)"; + else ''"$(head -n1 "${cfg.dataDir}/.login-password")"''; fullDotEnvPath = if cfg.generateDotEnvFile then "${cfg.dataDir}/.env" @@ -347,6 +353,22 @@ in { cd "${cfg.dataDir}" chown root: . chmod 0700 . + + # Seed persistent JWT secret + login password on first boot; + # re-read on subsequent activations. + ${lib.optionalString (cfg.jwt.secretFile == null) '' + if [ ! -s .jwt-secret ]; then + tr -dc '[:alnum:]' < /dev/urandom | head -c 50 > .jwt-secret + chmod 600 .jwt-secret + fi + ''} + ${lib.optionalString (cfg.passwordFile == null) '' + if [ ! -s .login-password ]; then + tr -dc '[:alnum:]' < /dev/urandom | head -c 50 > .login-password + chmod 600 .login-password + fi + ''} + echo "BAPI_JWT_SECRET=${jwtSecretScript}" >> .env echo "BAPI_JWT_ALGORITHM=${cfg.jwt.algorithm}" >> .env echo "BAPI_JWT_EXPIRY_TIME=${toString cfg.jwt.expiry}" >> .env @@ -428,6 +450,59 @@ in { ReadWritePaths = [cfg.dataDir]; }; }; + + # Celery worker handles async tasks dispatched by the FastAPI + # process (hardware polls, LN forward notifications, etc.). It + # reads the same .env as the main service and needs Redis + the + # prepared dataDir (LND macaroon/cert copied by blitz-api's + # ExecStartPre). Requiring blitz-api.service gives us both + # ordering and a guarantee that the prep step has finished. + services."${name}-celery-worker" = { + wantedBy = ["multi-user.target"]; + requires = ["redis.service" "${name}.service"]; + after = ["redis.service" "${name}.service"]; + description = "${name} Celery worker"; + environment = lib.mkMerge [ + (lib.mkIf cfg.generateDotEnvFile { + BAPI_ENV_PATH = "${cfg.dataDir}/.env"; + }) + cfg.env + ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/celery -A app.celery_app worker --loglevel=info"; + WorkingDirectory = cfg.dataDir; + User = cfg.user; + Group = cfg.group; + Restart = "always"; + SyslogIdentifier = "${name}-celery-worker"; + ReadWritePaths = [cfg.dataDir]; + }; + }; + + # Celery beat runs the periodic task scheduler. Single instance + # (never run more than one beat per celery app). Same env as + # worker. + services."${name}-celery-beat" = { + wantedBy = ["multi-user.target"]; + requires = ["redis.service" "${name}.service"]; + after = ["redis.service" "${name}.service"]; + description = "${name} Celery beat scheduler"; + environment = lib.mkMerge [ + (lib.mkIf cfg.generateDotEnvFile { + BAPI_ENV_PATH = "${cfg.dataDir}/.env"; + }) + cfg.env + ]; + serviceConfig = { + ExecStart = "${cfg.package}/bin/celery -A app.celery_app beat --loglevel=info"; + WorkingDirectory = cfg.dataDir; + User = cfg.user; + Group = cfg.group; + Restart = "always"; + SyslogIdentifier = "${name}-celery-beat"; + ReadWritePaths = [cfg.dataDir]; + }; + }; }; services.nginx = mkIf cfg.nginx.enable { From 1081b7161a2e5c6d7b2ba929e53ccc103f1470dd Mon Sep 17 00:00:00 2001 From: fusion44 Date: Thu, 7 May 2026 19:18:39 +0200 Subject: [PATCH 04/16] fix(lightning): get_ln_info returns stub in bitcoin-only mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ln` is only bound to LnNode() when `BAPI_LN_NODE != "none"`. Every `await ln.X()` call in this file therefore NameErrors when the operator runs in bitcoin-only mode. The most-hit caller is NativePythonSystem.get_system_info() at app/system/impl/native_python.py:32, which always reads `lninfo.alias` and `lninfo.color` to populate SystemInfo. The NameError there breaks the SSE warmup pipeline (get_full_client_warmup_data_bitcoinonly in app/api/warmup.py:116): warmup_new_connections() — calls get_full_client_warmup_data_bitcoinonly() → get_system_info() (via NativePythonSystem) → get_ln_info() → NameError: name 'ln' is not defined The exception bubbles into the asyncio.gather(), which means new SSE subscribers in bitcoin-only mode never receive `btc_info` / `system_info` / `installed_app_status`. Their dashboards stay empty forever (only periodic `hardware_info` and `ping` events flow, because those run on separate gather loops). Two fixes in this commit: 1. Return a stub LnInfo from get_ln_info() when ln_node == "none". All required fields filled with empty strings / zeros; the stub round-trips through any caller that reads alias/color/num_* without error. Other ln-method callers (get_wallet_balance, list_invoices, ...) keep their original behavior — they'll only ever fire from LN-aware code paths anyway. 2. Drop the duplicate `if ln_node != "none": ln = LnNode()` block at lines 69-70 (clearly a merge artifact). Operator workaround used until this lands: enable lnd. After this fix, bitcoin-only mode works end-to-end against the nixblitz plugin streamer. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/lightning/service.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/lightning/service.py b/app/lightning/service.py index 263eb7f..7fc21c7 100644 --- a/app/lightning/service.py +++ b/app/lightning/service.py @@ -66,9 +66,6 @@ if ln_node != "none": ln = LnNode() -if ln_node != "none": - ln = LnNode() - async def initialize_ln_repo() -> AsyncGenerator[InitLnRepoUpdate, None]: async for u in ln.initialize(): @@ -169,6 +166,29 @@ async def channel_close(channel_id: int, force_close: bool) -> str: async def get_ln_info() -> LnInfo: + # Bitcoin-only mode (`BAPI_LN_NODE=none`) leaves the module-level + # `ln` global unbound — every `await ln.X()` in this file would + # NameError. The most-hit caller in bitcoin-only mode is + # `NativePythonSystem.get_system_info()`, which always reads + # `lninfo.alias` and `lninfo.color` to populate `SystemInfo`. + # That call breaks the SSE warmup pipeline (`get_full_client_warmup_data_bitcoinonly`) + # so new SSE subscribers never receive `btc_info`. Return a stub + # so the bitcoin-only happy path keeps working. + if ln_node == "none": + return LnInfo( + implementation="none", + version="", + commit_hash="", + identity_pubkey="", + identity_uri="", + alias="", + color="#000000", + num_pending_channels=0, + num_active_channels=0, + num_inactive_channels=0, + num_peers=0, + block_height=0, + ) ln_info = await ln.get_ln_info() if PLATFORM == APIPlatform.RASPIBLITZ: ln_info.identity_uri = await redis_get("ln_default_address") From 3e34201f6a10f5bcbdba7d88d21ce99a2190177e Mon Sep 17 00:00:00 2001 From: fusion44 Date: Fri, 8 May 2026 12:39:33 +0200 Subject: [PATCH 05/16] refactor(flake): expose lib.mkBlitzApi for consumer-pkgs builds Extract the Python virtualenv build into `mkBlitzApi { pkgs, python ? null }` and expose it as `lib.mkBlitzApi`. Rewrite `overlays.default` to call the builder against the consumer's `prev` instead of pulling from `self.packages.${system}` (which captured this flake's pinned nixpkgs at flake-eval time and shut consumer overlays out of the build hook). Motivation: NixBlitz pulls blitz-api's flake via `builtins.getFlake` to keep the plugin self-contained. Without a pkgs-agnostic builder, the operator's nixpkgs overlays (Pi 5 jemalloc page-size patch and similar platform fixes) couldn't reach blitz-api's build, so the package would either fail to compile on Pi 5 or silently link against this flake's pinned nixpkgs and pull a second snapshot of glibc / openssl / etc. into the operator's closure. The `packages.${system}.${name}` attribute is preserved as a convenience pre-built using this flake's pinned nixpkgs; consumers that want consumer-pkgs builds should use `overlays.default` or `lib.mkBlitzApi` directly. Co-Authored-By: Claude Opus 4.7 (1M context) --- flake.nix | 78 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/flake.nix b/flake.nix index 0ed157e..58c0eb8 100644 --- a/flake.nix +++ b/flake.nix @@ -35,54 +35,80 @@ }: let name = "blitz-api"; - # Load the uv workspace once; reused across systems. + # Load the uv workspace once. Pure Nix value — independent of + # the consumer's pkgs, only depends on this repo's source tree. workspace = uv2nix.lib.workspace.loadWorkspace {workspaceRoot = ./.;}; - # Pure Nix overlay derived from pyproject.toml + uv.lock. Prefer - # prebuilt wheels — a handful of deps (grpcio, psutil, pyzmq) are - # painful to compile from source and wheels work fine on linux/darwin. - overlay = workspace.mkPyprojectOverlay { + # Pyproject-nix overlay derived from pyproject.toml + uv.lock. + # Prefer prebuilt wheels — a handful of deps (grpcio, psutil, + # pyzmq) are painful to compile from source and wheels work + # fine on linux/darwin. Also pkgs-independent. + pyprojectOverlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; }; + # Build the blitz-api Python virtualenv against the CALLER's + # pkgs. Exposed as `lib.mkBlitzApi` so consumers (NixBlitz + # plugin, downstream embedders) can build the package using + # their own nixpkgs — important when the consumer needs a + # platform-specific patch that this flake's pinned + # nixos-unstable doesn't carry yet. + # + # Without this, the package would be built once at THIS flake's + # eval time using THIS flake's pinned nixpkgs, and consumer + # overlays couldn't reach the build hook. + mkBlitzApi = { + pkgs, + python ? null, + }: let + py = + if python != null + then python + else pkgs.python312; + pythonSet = (pkgs.callPackage pyproject-nix.build.packages {python = py;}) + .overrideScope ( + pkgs.lib.composeManyExtensions [ + pyproject-build-systems.overlays.default + pyprojectOverlay + ] + ); + in + pythonSet.mkVirtualEnv "${name}-env" workspace.deps.default; + + # Library output — what callers reach for to build the package + # under their own pkgs. + libOutput = { + lib = {inherit mkBlitzApi;}; + }; + systems = flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - python = pkgs.python312; - - pythonSet = - (pkgs.callPackage pyproject-nix.build.packages { - inherit python; - }) - .overrideScope ( - nixpkgs.lib.composeManyExtensions [ - pyproject-build-systems.overlays.default - overlay - ] - ); in { packages = { default = self.packages.${system}.${name}; - ${name} = pythonSet.mkVirtualEnv "${name}-env" workspace.deps.default; + # Convenience: a pre-built package using THIS flake's pinned + # nixpkgs. Most consumers should use `overlays.default` or + # `lib.mkBlitzApi` instead so the package builds against + # their pkgs. + ${name} = mkBlitzApi {inherit pkgs;}; }; }); overlays.overlays = { + # Re-runs `mkBlitzApi` against the consumer's `prev` pkgs so the + # consumer's overlays (Pi 5 page-size patch, custom uv pin, etc.) + # are in scope when the package is built. default = final: prev: { - ${name} = self.packages.${prev.stdenv.hostPlatform.system}.${name}; + ${name} = mkBlitzApi {pkgs = prev;}; }; }; module = { - nixosModules.default = { - pkgs, - lib, - config, - ... - }: { + nixosModules.default = {...}: { imports = [./modules/blitz_api.nix]; nixpkgs.overlays = [self.overlays.default]; }; }; in - systems // overlays // module; + systems // overlays // module // libOutput; } From 88b7059ffa14d32eb7103d7c7531c28554778738 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 23:00:39 +0000 Subject: [PATCH 06/16] chore(deps): bump pyjwt from 2.10.1 to 2.12.0 Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.10.1 to 2.12.0. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/compare/2.10.1...2.12.0) --- updated-dependencies: - dependency-name: pyjwt dependency-version: 2.12.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- requirements.txt | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c684ba0..baa5020 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "uvicorn[standard]==0.40.0", "pydantic==2.12.5", "typing-extensions==4.15.0", - "pyjwt==2.10.1", + "pyjwt==2.12.0", "python-decouple==3.8", "psutil==7.2.2", "requests==2.32.5", diff --git a/requirements.txt b/requirements.txt index 679ca72..3d86953 100644 --- a/requirements.txt +++ b/requirements.txt @@ -138,7 +138,7 @@ pydantic-settings==2.9.1 # via fastapi pygments==2.19.1 # via rich -pyjwt==2.10.1 +pyjwt==2.12.0 # via blitz-api (pyproject.toml) python-dateutil==2.9.0.post0 # via celery diff --git a/uv.lock b/uv.lock index 91fecfc..05316bc 100644 --- a/uv.lock +++ b/uv.lock @@ -195,7 +195,7 @@ requires-dist = [ { name = "loguru", specifier = "==0.7.3" }, { name = "psutil", specifier = "==7.2.2" }, { name = "pydantic", specifier = "==2.12.5" }, - { name = "pyjwt", specifier = "==2.10.1" }, + { name = "pyjwt", specifier = "==2.12.0" }, { name = "python-decouple", specifier = "==3.8" }, { name = "pyzmq", specifier = "==27.1.0" }, { name = "requests", specifier = "==2.32.5" }, @@ -1065,11 +1065,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.10.1" +version = "2.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/10/e8192be5f38f3e8e7e046716de4cae33d56fd5ae08927a823bb916be36c1/pyjwt-2.12.0.tar.gz", hash = "sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02", size = 102511, upload-time = "2026-03-12T17:15:30.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, + { url = "https://files.pythonhosted.org/packages/15/70/70f895f404d363d291dcf62c12c85fdd47619ad9674ac0f53364d035925a/pyjwt-2.12.0-py3-none-any.whl", hash = "sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e", size = 29700, upload-time = "2026-03-12T17:15:29.257Z" }, ] [[package]] From 7c1b6aebb9af9774572a851f27a38296050f14e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:34:27 +0000 Subject: [PATCH 07/16] chore(deps): bump deepdiff from 8.6.1 to 8.6.2 Bumps [deepdiff](https://github.com/seperman/deepdiff) from 8.6.1 to 8.6.2. - [Release notes](https://github.com/seperman/deepdiff/releases) - [Changelog](https://github.com/qlustered/deepdiff/blob/master/docs/changelog.rst) - [Commits](https://github.com/seperman/deepdiff/commits) --- updated-dependencies: - dependency-name: deepdiff dependency-version: 8.6.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- requirements.txt | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index baa5020..e3a13e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "async-timeout==5.0.1", "grpcio==1.76.0", "grpcio-tools==1.76.0", - "deepdiff==8.6.1", + "deepdiff==8.6.2", "loguru==0.7.3", "celery[redis]==5.6.2", ] diff --git a/requirements.txt b/requirements.txt index 3d86953..1fa462f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -55,7 +55,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # click # loguru # uvicorn -deepdiff==8.6.1 +deepdiff==8.6.2 # via blitz-api (pyproject.toml) dnspython==2.7.0 # via email-validator diff --git a/uv.lock b/uv.lock index 05316bc..e174030 100644 --- a/uv.lock +++ b/uv.lock @@ -188,7 +188,7 @@ requires-dist = [ { name = "anyio", specifier = "==4.9.0" }, { name = "async-timeout", specifier = "==5.0.1" }, { name = "celery", extras = ["redis"], specifier = "==5.6.2" }, - { name = "deepdiff", specifier = "==8.6.1" }, + { name = "deepdiff", specifier = "==8.6.2" }, { name = "fastapi", extras = ["standard-no-fastapi-cloud-cli"], specifier = "==0.128.0" }, { name = "grpcio", specifier = "==1.76.0" }, { name = "grpcio-tools", specifier = "==1.76.0" }, @@ -438,14 +438,14 @@ wheels = [ [[package]] name = "deepdiff" -version = "8.6.1" +version = "8.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "orderly-set" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/76/36c9aab3d5c19a94091f7c6c6e784efca50d87b124bf026c36e94719f33c/deepdiff-8.6.1.tar.gz", hash = "sha256:ec56d7a769ca80891b5200ec7bd41eec300ced91ebcc7797b41eb2b3f3ff643a", size = 634054, upload-time = "2025-09-03T19:40:41.461Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/50/767448e792d41bfb6094ee317a355c1cb221dca24b2e178e2203bbea2a77/deepdiff-8.6.2.tar.gz", hash = "sha256:186dcbd181e4d76cef11ab05f802d0056c5d6083c5a6748c1473e9d7481e183e", size = 634860, upload-time = "2026-03-18T17:16:33.785Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/e6/efe534ef0952b531b630780e19cabd416e2032697019d5295defc6ef9bd9/deepdiff-8.6.1-py3-none-any.whl", hash = "sha256:ee8708a7f7d37fb273a541fa24ad010ed484192cd0c4ffc0fa0ed5e2d4b9e78b", size = 91378, upload-time = "2025-09-03T19:40:39.679Z" }, + { url = "https://files.pythonhosted.org/packages/2b/5f/c52bd1255db763d0cdcb7084d2e90c42119cb229302c56bdf1d0aa78abd2/deepdiff-8.6.2-py3-none-any.whl", hash = "sha256:4d22034a866c3928303a9332c279362f714192d9305bac17c498720d095fd1b4", size = 91979, upload-time = "2026-03-18T17:16:32.171Z" }, ] [[package]] From 2e687b644bac9247703f47e393e6ea8e5c6db7c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:29:45 +0000 Subject: [PATCH 08/16] chore(deps): bump requests from 2.32.5 to 2.33.0 Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- requirements.txt | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e3a13e9..082d5a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "pyjwt==2.12.0", "python-decouple==3.8", "psutil==7.2.2", - "requests==2.32.5", + "requests==2.33.0", "pyzmq==27.1.0", "aiohttp==3.13.3", "async-timeout==5.0.1", diff --git a/requirements.txt b/requirements.txt index 1fa462f..d259e6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -156,7 +156,7 @@ pyzmq==27.1.0 # via blitz-api (pyproject.toml) redis==4.6.0 # via kombu -requests==2.32.5 +requests==2.33.0 # via blitz-api (pyproject.toml) rich==14.0.0 # via diff --git a/uv.lock b/uv.lock index e174030..438c565 100644 --- a/uv.lock +++ b/uv.lock @@ -198,7 +198,7 @@ requires-dist = [ { name = "pyjwt", specifier = "==2.12.0" }, { name = "python-decouple", specifier = "==3.8" }, { name = "pyzmq", specifier = "==27.1.0" }, - { name = "requests", specifier = "==2.32.5" }, + { name = "requests", specifier = "==2.33.0" }, { name = "starlette", specifier = "==0.49.1" }, { name = "typing-extensions", specifier = "==4.15.0" }, { name = "uvicorn", extras = ["standard"], specifier = "==0.40.0" }, @@ -1217,7 +1217,7 @@ wheels = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.33.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1225,9 +1225,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, + { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] [[package]] From 8b20bfcb9803afbda959ec0469b5166a55e91734 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:53:17 +0000 Subject: [PATCH 09/16] chore(deps): bump pygments from 2.19.1 to 2.20.0 Bumps [pygments](https://github.com/pygments/pygments) from 2.19.1 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.19.1...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d259e6f..a9bfacc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -136,7 +136,7 @@ pydantic-extra-types==2.11.0 # via fastapi pydantic-settings==2.9.1 # via fastapi -pygments==2.19.1 +pygments==2.20.0 # via rich pyjwt==2.12.0 # via blitz-api (pyproject.toml) From 7a269712cc49015d3865b318ab277904d5a9d799 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:36:02 +0000 Subject: [PATCH 10/16] chore(deps): bump aiohttp from 3.13.3 to 3.13.4 --- updated-dependencies: - dependency-name: aiohttp dependency-version: 3.13.4 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- requirements.txt | 2 +- uv.lock | 76 ++++++++++++++++++++++++------------------------ 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 082d5a3..89e4805 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "psutil==7.2.2", "requests==2.33.0", "pyzmq==27.1.0", - "aiohttp==3.13.3", + "aiohttp==3.13.4", "async-timeout==5.0.1", "grpcio==1.76.0", "grpcio-tools==1.76.0", diff --git a/requirements.txt b/requirements.txt index a9bfacc..491ef47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # uv pip compile --all-extras --universal --output-file requirements.txt pyproject.toml aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.3 +aiohttp==3.13.4 # via blitz-api (pyproject.toml) aiosignal==1.4.0 # via aiohttp diff --git a/uv.lock b/uv.lock index 438c565..dd68821 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -24,42 +24,42 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, - { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, - { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, - { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, - { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, - { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, - { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, - { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, - { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, - { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, - { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, - { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, - { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, - { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, - { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, - { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, - { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/45/4a/064321452809dae953c1ed6e017504e72551a26b6f5708a5a80e4bf556ff/aiohttp-3.13.4.tar.gz", hash = "sha256:d97a6d09c66087890c2ab5d49069e1e570583f7ac0314ecf98294c1b6aaebd38", size = 7859748, upload-time = "2026-03-28T17:19:40.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/7e/cb94129302d78c46662b47f9897d642fd0b33bdfef4b73b20c6ced35aa4c/aiohttp-3.13.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8ea0c64d1bcbf201b285c2246c51a0c035ba3bbd306640007bc5844a3b4658c1", size = 760027, upload-time = "2026-03-28T17:15:33.022Z" }, + { url = "https://files.pythonhosted.org/packages/5e/cd/2db3c9397c3bd24216b203dd739945b04f8b87bb036c640da7ddb63c75ef/aiohttp-3.13.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f742e1fa45c0ed522b00ede565e18f97e4cf8d1883a712ac42d0339dfb0cce7", size = 508325, upload-time = "2026-03-28T17:15:34.714Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/d28b2722ec13107f2e37a86b8a169897308bab6a3b9e071ecead9d67bd9b/aiohttp-3.13.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dcfb50ee25b3b7a1222a9123be1f9f89e56e67636b561441f0b304e25aaef8f", size = 502402, upload-time = "2026-03-28T17:15:36.409Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d6/acd47b5f17c4430e555590990a4746efbcb2079909bb865516892bf85f37/aiohttp-3.13.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3262386c4ff370849863ea93b9ea60fd59c6cf56bf8f93beac625cf4d677c04d", size = 1771224, upload-time = "2026-03-28T17:15:38.223Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/af6e20113ba6a48fd1cd9e5832c4851e7613ef50c7619acdaee6ec5f1aff/aiohttp-3.13.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:473bb5aa4218dd254e9ae4834f20e31f5a0083064ac0136a01a62ddbae2eaa42", size = 1731530, upload-time = "2026-03-28T17:15:39.988Z" }, + { url = "https://files.pythonhosted.org/packages/81/16/78a2f5d9c124ad05d5ce59a9af94214b6466c3491a25fb70760e98e9f762/aiohttp-3.13.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e56423766399b4c77b965f6aaab6c9546617b8994a956821cc507d00b91d978c", size = 1827925, upload-time = "2026-03-28T17:15:41.944Z" }, + { url = "https://files.pythonhosted.org/packages/2a/1f/79acf0974ced805e0e70027389fccbb7d728e6f30fcac725fb1071e63075/aiohttp-3.13.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8af249343fafd5ad90366a16d230fc265cf1149f26075dc9fe93cfd7c7173942", size = 1923579, upload-time = "2026-03-28T17:15:44.071Z" }, + { url = "https://files.pythonhosted.org/packages/af/53/29f9e2054ea6900413f3b4c3eb9d8331f60678ec855f13ba8714c47fd48d/aiohttp-3.13.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bc0a5cf4f10ef5a2c94fdde488734b582a3a7a000b131263e27c9295bd682d9", size = 1767655, upload-time = "2026-03-28T17:15:45.911Z" }, + { url = "https://files.pythonhosted.org/packages/f3/57/462fe1d3da08109ba4aa8590e7aed57c059af2a7e80ec21f4bac5cfe1094/aiohttp-3.13.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5c7ff1028e3c9fc5123a865ce17df1cb6424d180c503b8517afbe89aa566e6be", size = 1630439, upload-time = "2026-03-28T17:15:48.11Z" }, + { url = "https://files.pythonhosted.org/packages/d7/4b/4813344aacdb8127263e3eec343d24e973421143826364fa9fc847f6283f/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ba5cf98b5dcb9bddd857da6713a503fa6d341043258ca823f0f5ab7ab4a94ee8", size = 1745557, upload-time = "2026-03-28T17:15:50.13Z" }, + { url = "https://files.pythonhosted.org/packages/d4/01/1ef1adae1454341ec50a789f03cfafe4c4ac9c003f6a64515ecd32fe4210/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d85965d3ba21ee4999e83e992fecb86c4614d6920e40705501c0a1f80a583c12", size = 1741796, upload-time = "2026-03-28T17:15:52.351Z" }, + { url = "https://files.pythonhosted.org/packages/22/04/8cdd99af988d2aa6922714d957d21383c559835cbd43fbf5a47ddf2e0f05/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:49f0b18a9b05d79f6f37ddd567695943fcefb834ef480f17a4211987302b2dc7", size = 1805312, upload-time = "2026-03-28T17:15:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/b48d5577338d4b25bbdbae35c75dbfd0493cb8886dc586fbfb2e90862239/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7f78cb080c86fbf765920e5f1ef35af3f24ec4314d6675d0a21eaf41f6f2679c", size = 1621751, upload-time = "2026-03-28T17:15:56.564Z" }, + { url = "https://files.pythonhosted.org/packages/bc/89/4eecad8c1858e6d0893c05929e22343e0ebe3aec29a8a399c65c3cc38311/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:67a3ec705534a614b68bbf1c70efa777a21c3da3895d1c44510a41f5a7ae0453", size = 1826073, upload-time = "2026-03-28T17:15:58.489Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5c/9dc8293ed31b46c39c9c513ac7ca152b3c3d38e0ea111a530ad12001b827/aiohttp-3.13.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6630ec917e85c5356b2295744c8a97d40f007f96a1c76bf1928dc2e27465393", size = 1760083, upload-time = "2026-03-28T17:16:00.677Z" }, + { url = "https://files.pythonhosted.org/packages/1e/19/8bbf6a4994205d96831f97b7d21a0feed120136e6267b5b22d229c6dc4dc/aiohttp-3.13.4-cp311-cp311-win32.whl", hash = "sha256:54049021bc626f53a5394c29e8c444f726ee5a14b6e89e0ad118315b1f90f5e3", size = 439690, upload-time = "2026-03-28T17:16:02.902Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f5/ac409ecd1007528d15c3e8c3a57d34f334c70d76cfb7128a28cffdebd4c1/aiohttp-3.13.4-cp311-cp311-win_amd64.whl", hash = "sha256:c033f2bc964156030772d31cbf7e5defea181238ce1f87b9455b786de7d30145", size = 463824, upload-time = "2026-03-28T17:16:05.058Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bd/ede278648914cabbabfdf95e436679b5d4156e417896a9b9f4587169e376/aiohttp-3.13.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ee62d4471ce86b108b19c3364db4b91180d13fe3510144872d6bad5401957360", size = 752158, upload-time = "2026-03-28T17:16:06.901Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/581c053253c07b480b03785196ca5335e3c606a37dc73e95f6527f1591fe/aiohttp-3.13.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c0fd8f41b54b58636402eb493afd512c23580456f022c1ba2db0f810c959ed0d", size = 501037, upload-time = "2026-03-28T17:16:08.82Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/a5ede193c08f13cc42c0a5b50d1e246ecee9115e4cf6e900d8dbd8fd6acb/aiohttp-3.13.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4baa48ce49efd82d6b1a0be12d6a36b35e5594d1dd42f8bfba96ea9f8678b88c", size = 501556, upload-time = "2026-03-28T17:16:10.63Z" }, + { url = "https://files.pythonhosted.org/packages/d6/10/88ff67cd48a6ec36335b63a640abe86135791544863e0cfe1f065d6cef7a/aiohttp-3.13.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d738ebab9f71ee652d9dbd0211057690022201b11197f9a7324fd4dba128aa97", size = 1757314, upload-time = "2026-03-28T17:16:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/8b/15/fdb90a5cf5a1f52845c276e76298c75fbbcc0ac2b4a86551906d54529965/aiohttp-3.13.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ce692c3468fa831af7dceed52edf51ac348cebfc8d3feb935927b63bd3e8576", size = 1731819, upload-time = "2026-03-28T17:16:14.558Z" }, + { url = "https://files.pythonhosted.org/packages/ec/df/28146785a007f7820416be05d4f28cc207493efd1e8c6c1068e9bdc29198/aiohttp-3.13.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e08abcfe752a454d2cb89ff0c08f2d1ecd057ae3e8cc6d84638de853530ebab", size = 1793279, upload-time = "2026-03-28T17:16:16.594Z" }, + { url = "https://files.pythonhosted.org/packages/10/47/689c743abf62ea7a77774d5722f220e2c912a77d65d368b884d9779ef41b/aiohttp-3.13.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5977f701b3fff36367a11087f30ea73c212e686d41cd363c50c022d48b011d8d", size = 1891082, upload-time = "2026-03-28T17:16:18.71Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/f7f4f318c7e58c23b761c9b13b9a3c9b394e0f9d5d76fbc6622fa98509f6/aiohttp-3.13.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54203e10405c06f8b6020bd1e076ae0fe6c194adcee12a5a78af3ffa3c57025e", size = 1773938, upload-time = "2026-03-28T17:16:21.125Z" }, + { url = "https://files.pythonhosted.org/packages/aa/06/f207cb3121852c989586a6fc16ff854c4fcc8651b86c5d3bd1fc83057650/aiohttp-3.13.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:358a6af0145bc4dda037f13167bef3cce54b132087acc4c295c739d05d16b1c3", size = 1579548, upload-time = "2026-03-28T17:16:23.588Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/e1289661a32161e24c1fe479711d783067210d266842523752869cc1d9c2/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:898ea1850656d7d61832ef06aa9846ab3ddb1621b74f46de78fbc5e1a586ba83", size = 1714669, upload-time = "2026-03-28T17:16:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/96/0a/3e86d039438a74a86e6a948a9119b22540bae037d6ba317a042ae3c22711/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7bc30cceb710cf6a44e9617e43eebb6e3e43ad855a34da7b4b6a73537d8a6763", size = 1754175, upload-time = "2026-03-28T17:16:28.18Z" }, + { url = "https://files.pythonhosted.org/packages/f4/30/e717fc5df83133ba467a560b6d8ef20197037b4bb5d7075b90037de1018e/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4a31c0c587a8a038f19a4c7e60654a6c899c9de9174593a13e7cc6e15ff271f9", size = 1762049, upload-time = "2026-03-28T17:16:30.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/28/8f7a2d4492e336e40005151bdd94baf344880a4707573378579f833a64c1/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2062f675f3fe6e06d6113eb74a157fb9df58953ffed0cdb4182554b116545758", size = 1570861, upload-time = "2026-03-28T17:16:32.953Z" }, + { url = "https://files.pythonhosted.org/packages/78/45/12e1a3d0645968b1c38de4b23fdf270b8637735ea057d4f84482ff918ad9/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d1ba8afb847ff80626d5e408c1fdc99f942acc877d0702fe137015903a220a9", size = 1790003, upload-time = "2026-03-28T17:16:35.468Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/60374e18d590de16dcb39d6ff62f39c096c1b958e6f37727b5870026ea30/aiohttp-3.13.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b08149419994cdd4d5eecf7fd4bc5986b5a9380285bcd01ab4c0d6bfca47b79d", size = 1737289, upload-time = "2026-03-28T17:16:38.187Z" }, + { url = "https://files.pythonhosted.org/packages/02/bf/535e58d886cfbc40a8b0013c974afad24ef7632d645bca0b678b70033a60/aiohttp-3.13.4-cp312-cp312-win32.whl", hash = "sha256:fc432f6a2c4f720180959bc19aa37259651c1a4ed8af8afc84dd41c60f15f791", size = 434185, upload-time = "2026-03-28T17:16:40.735Z" }, + { url = "https://files.pythonhosted.org/packages/1e/1a/d92e3325134ebfff6f4069f270d3aac770d63320bd1fcd0eca023e74d9a8/aiohttp-3.13.4-cp312-cp312-win_amd64.whl", hash = "sha256:6148c9ae97a3e8bff9a1fc9c757fa164116f86c100468339730e717590a3fb77", size = 461285, upload-time = "2026-03-28T17:16:42.713Z" }, ] [[package]] @@ -184,7 +184,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "aiohttp", specifier = "==3.13.3" }, + { name = "aiohttp", specifier = "==3.13.4" }, { name = "anyio", specifier = "==4.9.0" }, { name = "async-timeout", specifier = "==5.0.1" }, { name = "celery", extras = ["redis"], specifier = "==5.6.2" }, From c1e510d8a57238018eaeea77a7cb745faf211e81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 02:42:44 +0000 Subject: [PATCH 11/16] chore(deps-dev): bump pytest from 9.0.2 to 9.0.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 89e4805..8721a0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ api = "app.server:main" [dependency-groups] dev = [ - "pytest==9.0.2", + "pytest==9.0.3", "pytest-asyncio==1.3.0", "coverage==7.13.2", "ruff==0.14.14", diff --git a/uv.lock b/uv.lock index dd68821..d246390 100644 --- a/uv.lock +++ b/uv.lock @@ -209,7 +209,7 @@ dev = [ { name = "click", specifier = "==8.3.1" }, { name = "coverage", specifier = "==7.13.2" }, { name = "debugpy", specifier = "==1.8.20" }, - { name = "pytest", specifier = "==9.0.2" }, + { name = "pytest", specifier = "==9.0.3" }, { name = "pytest-asyncio", specifier = "==1.3.0" }, { name = "ruff", specifier = "==0.14.14" }, { name = "watchfiles", specifier = "==1.1.1" }, @@ -1074,7 +1074,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1083,9 +1083,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] [[package]] From 4c2645e7d8c10dac83fd60795c30d37cf0475e83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 21:26:32 +0000 Subject: [PATCH 12/16] chore(deps): bump python-dotenv from 1.1.0 to 1.2.2 Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 1.1.0 to 1.2.2. - [Release notes](https://github.com/theskumar/python-dotenv/releases) - [Changelog](https://github.com/theskumar/python-dotenv/blob/main/CHANGELOG.md) - [Commits](https://github.com/theskumar/python-dotenv/compare/v1.1.0...v1.2.2) --- updated-dependencies: - dependency-name: python-dotenv dependency-version: 1.2.2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 491ef47..f9ab882 100644 --- a/requirements.txt +++ b/requirements.txt @@ -144,7 +144,7 @@ python-dateutil==2.9.0.post0 # via celery python-decouple==3.8 # via blitz-api (pyproject.toml) -python-dotenv==1.1.0 +python-dotenv==1.2.2 # via # pydantic-settings # uvicorn From e33c11cb8ab899ea9b725921f583c802a89dc70b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 11:51:59 +0000 Subject: [PATCH 13/16] chore(deps): bump python-multipart from 0.0.22 to 0.0.27 Bumps [python-multipart](https://github.com/Kludex/python-multipart) from 0.0.22 to 0.0.27. - [Release notes](https://github.com/Kludex/python-multipart/releases) - [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md) - [Commits](https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.27) --- updated-dependencies: - dependency-name: python-multipart dependency-version: 0.0.27 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f9ab882..48266a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -148,7 +148,7 @@ python-dotenv==1.2.2 # via # pydantic-settings # uvicorn -python-multipart==0.0.22 +python-multipart==0.0.27 # via fastapi pyyaml==6.0.2 # via uvicorn From d415fb30fd2a64f4bc0d8af8a1b94753bd784ffb Mon Sep 17 00:00:00 2001 From: Justin Sharp Date: Wed, 6 May 2026 10:58:05 -0600 Subject: [PATCH 14/16] fix: handle missing bolt11 in CLN list_payments for keysend/BOLT12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #290 — same class of bug, different code path. list_payments() in cln_jrpc.py unconditionally indexed p["bolt11"] for any incomplete payment, causing a KeyError -> the function returned None and then list_all_tx() crashed with `TypeError: 'NoneType' object is not iterable`, returning HTTP 500 to the Transactions view. CLN's `listpays` does not include `bolt11` for keysend payments or for BOLT12 offer payments, so any node that has ever made one of those will hit this on every Transactions load. Fix: guard the bolt11 decode the same way #290 guards invoice fields — only decode when bolt11 is present, and fall back to amount_sent_msat when neither bolt11 nor amount_msat are available, so the payment still shows up in the list. Reproduced on RaspiBlitz v1.12.1 with Core Lightning v25.12.1 and a keysend payment in history. --- app/lightning/impl/cln_jrpc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/lightning/impl/cln_jrpc.py b/app/lightning/impl/cln_jrpc.py index e7ba3b2..5a9bdeb 100644 --- a/app/lightning/impl/cln_jrpc.py +++ b/app/lightning/impl/cln_jrpc.py @@ -415,8 +415,14 @@ async def list_payments( continue if include_incomplete: - b11_decoded = await self._decode_bolt11_cached(p["bolt11"]) - p["amount_msat"] = b11_decoded.num_msat + # bolt11 is absent for keysend and BOLT12 payments — only + # decode it when present and amount_msat isn't already set. + bolt11 = p.get("bolt11") + if bolt11 and "amount_msat" not in p: + b11_decoded = await self._decode_bolt11_cached(bolt11) + p["amount_msat"] = b11_decoded.num_msat + elif "amount_msat" not in p: + p["amount_msat"] = p.get("amount_sent_msat", 0) pays.append(Payment.from_cln_jrpc(p)) if reversed: From 62b6438970580d3c5269da8e1caf683607db491e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 16:58:52 +0000 Subject: [PATCH 15/16] chore(deps): bump urllib3 from 2.6.3 to 2.7.0 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.7.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 48266a1..e4e28c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -202,7 +202,7 @@ tzdata==2025.2 # tzlocal tzlocal==5.3.1 # via celery -urllib3==2.6.3 +urllib3==2.7.0 # via requests uvicorn==0.40.0 # via From 6524a4d0bd095ebaab8e08f7493f44900005ab53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 18:26:57 +0000 Subject: [PATCH 16/16] chore(deps): bump idna from 3.10 to 3.15 Bumps [idna](https://github.com/kjd/idna) from 3.10 to 3.15. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.10...v3.15) --- updated-dependencies: - dependency-name: idna dependency-version: '3.15' dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e4e28c9..10e4411 100644 --- a/requirements.txt +++ b/requirements.txt @@ -85,7 +85,7 @@ httptools==0.6.4 # via uvicorn httpx==0.28.1 # via fastapi -idna==3.10 +idna==3.15 # via # anyio # email-validator