463 Commits
Author SHA1 Message Date
9qeklajcandGitHub f90db08205 Merge pull request #654 from Routstr/audit/money-path-red-tests-github
test: money-path audit — 8 RED tests for live fund-loss vulnerabilities
2026-08-08 00:51:00 +02:00
9qeklajc 239e2e5d5d test: mock store_cashu_transaction in credit_balance unit tests
The three credit_balance unit tests mock the DB session but let
credit_balance call the real store_cashu_transaction_with_retry, which
opens its own session against the global engine. In CI that database has
no cashu_transactions table; since storage failures now propagate
(a60b04ae) instead of being silently swallowed, the tests failed with
sqlite3.OperationalError. Patch the audit store like the existing
propagation test already does.
2026-08-08 00:47:35 +02:00
9qeklajcandGitHub 6e833f608c Merge pull request #652 from Routstr/fix/reduce-docker-build-context
fix(docker): exclude runtime state from build context
2026-08-08 00:38:13 +02:00
9qeklajcandGitHub d26ff08877 Merge pull request #640 from Routstr/test/issue-639-forwarded-model-id
test: reproduce forwarded model ID alias regression
2026-08-06 23:43:48 +02:00
9qeklajc a60b04aea0 fix: propagate Cashu transaction storage failures 2026-08-06 23:35:37 +02:00
9qeklajc bc5c56ecc3 clean up 2026-08-06 22:58:44 +02:00
thefux 667f9bf6bb test: money-path audit — 8 RED tests for live fund-loss vulnerabilities
Comprehensive audit of all money-moving code paths on current main.
Found 8 live vulnerabilities where users, providers, or node runners
can lose funds, plus 1 false-green in the existing emergency refund
test suite.

Live vulnerabilities (all RED — tests assert correct/safe behaviour):

V-E1  send_refund() swallows DB failure after minting a refund token
      base.py ~line 3625 — except Exception: pass
V-E2  Emergency refund (chat) — same except: pass
      base.py ~line 3992 (existing test is a false green — 500-char
      window too short)
V-E3  Emergency refund (responses API) — identical pattern
      base.py ~line 4972
V-E4  Balance refund endpoint swallows DB failure
      balance.py ~line 628
V-E5  credit_balance() swallows 'in' transaction DB failure
      wallet.py ~line 1715
V-E6  EHBP refund token — except: pass after store
      ehbp.py ~line 762
V-E7  EHBP 'in' transaction — except: pass after store
      ehbp.py ~line 1028
V-E8  Admin withdraw returns token even when DB store fails
      admin.py ~line 475

V-E9  Window regression guard (GREEN) — documents the false-green in
      the existing test_emergency_refund_no_try_except_pass

Test results: 8 failed, 1 passed.
2026-08-06 20:58:06 +00:00
thefux de62a27fb6 fix(docker): exclude runtime state from build context 2026-08-06 19:57:27 +00:00
9qeklajc bdc19a6907 fix: preserve nullable forwarded model aliases 2026-08-06 21:54:13 +02:00
9qeklajc 943e083a89 make provider id unique 2026-08-05 00:49:17 +02:00
9qeklajc c7e5fba910 better fallback 2026-08-05 00:32:06 +02:00
9qeklajc 27f53948ca clean up 2026-08-04 22:59:50 +02:00
thefux 48c69857ed fix: treat 'quote not found' as definitive unpaid for invoice expiry
When the mint no longer has a Lightning quote (e.g. after TTL purge or
restart), check_invoice_payment() was logging an ERROR and returning False.
This caused the periodic_invoice_watcher to keep polling the same dead
quote every 10s forever, producing infinite log spam.

Now _is_quote_not_found() detects 'Mint Error: quote not found (Code: 0)'
and returns True, allowing _expire_invoice_if_authoritatively_unpaid()
to mark the invoice as expired so the watcher stops polling it.

The check is case-insensitive and requires code 0 to avoid false positives
from other quote-related errors.
2026-08-04 19:39:43 +00:00
9qeklajc 2f2820eb33 fix test 2026-08-04 01:44:01 +02:00
9qeklajc a6c129c02d harden autotopup 2026-08-04 01:32:25 +02:00
9qeklajc a2e2a5c662 clean up 2026-08-04 01:13:20 +02:00
9qeklajc 8e8a9a46b6 Merge remote-tracking branch 'origin/main' into ppq-auto-topup
# Conflicts:
#	routstr/upstream/auto_topup.py
#	routstr/wallet.py
#	tests/unit/test_wallet.py
2026-08-04 00:24:27 +02:00
9qeklajc 3b2c5a0671 Merge remote-tracking branch 'origin/main' into fix/xcashu-cost-breakdown-port 2026-08-04 00:12:37 +02:00
9qeklajc 694bc04623 harden melting 2026-08-04 00:06:53 +02:00
9qeklajc da859f2f84 clean up 2026-08-03 23:32:06 +02:00
9qeklajc dd8c4a9a8a update migartion 2026-08-03 00:05:36 +02:00
9qeklajc 2b4e4c2430 Merge remote-tracking branch 'origin/main' into fix/mint-rate-limit-and-fallback
# Conflicts:
#	tests/unit/test_fee_payout_migration.py
2026-08-02 23:29:16 +02:00
9qeklajc e903aa3a9f clean up 2026-08-02 23:16:01 +02:00
9qeklajc f3eefc2638 Merge remote-tracking branch 'origin/main' into model-paths
# Conflicts:
#	routstr/core/settings.py
#	tests/unit/test_fee_payout_migration.py
2026-08-02 22:27:39 +02:00
9qeklajc dc13cde00c chore: remove unrelated PPQ PR changes 2026-07-31 02:42:14 +02:00
9qeklajc c0aad3b3ab test: isolate BOLT11 mint selection liabilities 2026-07-31 02:16:03 +02:00
9qeklajc 19236ecc9d fix(db): increase default connection pool capacity 2026-07-31 02:14:50 +02:00
9qeklajc 7a2b485af6 Merge main into ppq-auto-topup 2026-07-31 02:10:43 +02:00
9qeklajc 2ec6b27200 fix: resolve mint fallback review comments 2026-07-31 02:10:00 +02:00
9qeklajc f9980e5c66 fix: harden PPQ auto top-up reconciliation 2026-07-31 02:06:50 +02:00
9qeklajc 60566313dc fix: return persisted API-key refund token 2026-07-31 01:19:18 +02:00
9qeklajc 443c910b9e Merge branch 'main' into fix/mint-rate-limit-and-fallback 2026-07-30 08:38:27 +02:00
9qeklajc 4cc9aef61f fix: make payouts and proxy sessions safe 2026-07-30 02:57:49 +02:00
9qeklajc 3befe063f4 fix: annotate lightning settlement test session 2026-07-30 01:19:35 +02:00
9qeklajc 895ea90bfa Merge branch 'main' into fix/mint-rate-limit-and-fallback
# Conflicts:
#	routstr/core/settings.py
#	routstr/lightning.py
#	routstr/wallet.py
#	tests/integration/test_lightning_invoice_constraints.py
#	tests/unit/test_fee_payout_migration.py
#	tests/unit/test_fetch_all_balances.py
2026-07-30 01:09:32 +02:00
9qeklajc 5ea5024608 resolve review comments 2026-07-29 22:50:33 +02:00
9qeklajc bb2a05b67c add url and specific model infos to paht 2026-07-29 00:08:34 +02:00
9qeklajc 7f729de48e test: reproduce forwarded model ID alias regression 2026-07-28 02:14:02 +02:00
9qeklajc e2f89a2645 fix: address follow-up model path review 2026-07-27 23:17:13 +02:00
9qeklajc 48c11eb7bc fix Lightning settlement test typing 2026-07-27 00:07:36 +02:00
9qeklajc c829685f80 fix Cashu fallback and Lightning settlement 2026-07-26 23:23:32 +02:00
9qeklajc 16fc548b48 fix: make model path identity selectable 2026-07-26 20:12:55 +02:00
9qeklajc 06dba681c5 fix: satisfy strict mypy in model-paths tests
Replace untyped lambdas with typed handler/provider functions; CI runs
mypy over tests as well.
2026-07-26 13:26:49 +02:00
9qeklajc f96acbb99c fix: address model-paths review findings
Provider scoping (items 1/2/6):
- Key visibility maps on (model_id.lower(), upstream_provider_id), matching
  refresh_model_maps, so a disable/override row on one provider never leaks
  onto another provider's model, and matching is case-insensitive.

Data safety (items 3/5):
- Degraded OpenRouter fetches (network error, 429, non-200, bad payload)
  return None (unknown) instead of []; a provider whose path set is unknown
  keeps its previously persisted rows instead of being wiped.
- Endpoint payload parsing moved fully inside try, with a list guard, so
  endpoints:null or non-list shapes are swallowed as documented.
- refresh with an empty live upstream list is a no-op; the unfiltered
  DELETE in the prune path is gone (prune now keys off enabled DB rows).

Hot path (items 4/12/14):
- Persist uses chunked bulk INSERTs (one statement per 500 rows) instead of
  per-row ORM adds; redundant ix_model_paths_model_id index dropped.
- Read routes filter in SQL instead of materializing the whole table, and
  output ordering is deterministic (public id + path), independent of rowid.
- Visibility no longer rebuilds fully priced Model objects per override row;
  it reads id/forwarded_model_id/canonical_slug straight off ModelRow.

Path/id contract (items 7/8/9/11):
- discovery_path_for_subprovider/discovery_base_paths hooks on
  BaseUpstreamProvider, overridden by OpenRouterUpstreamProvider, mirror
  _apply_provider_field so discovery and response stamping cannot drift
  (openrouter:OpenRouter now correctly maps to unknown).
- openrouter_author_slug falls back to a slash-containing forwarded_model_id,
  so admin-created alias rows are discoverable.
- public_model_id splits on the first slash, same as get_base_model_id, so
  discovery ids can be sent to chat completions verbatim.

Lifecycle (items 10/13):
- ENABLE_MODEL_PATHS_REFRESH kill switch; interval and flag re-read every
  loop iteration, and the task idles (not exits) while disabled.
- First 429 latches and aborts the remaining fan-out for the cycle; a
  per-cycle cache dedupes fetches across providers sharing a base URL.
- refresh_model_maps prunes paths of disabled/deleted providers so admin
  mutations take effect immediately; rows carry updated_at and both
  endpoints expose it.

Tests (item 15) rewritten through the public refresh entry point with
transport-level httpx.MockTransport fakes, FK enforcement on, and coverage
for the periodic loop. Migration re-chained onto 9c4d8e2f1a6b.
2026-07-26 13:23:31 +02:00
9qeklajc 73a3f12469 Merge remote-tracking branch 'origin/main' into model-paths 2026-07-26 13:09:07 +02:00
9qeklajc 39f801561b fix: recreate refund sweep migration on latest head 2026-07-26 12:52:24 +02:00
9qeklajc ff55788e2d fix: address PR 634 review feedback 2026-07-26 12:44:37 +02:00
9qeklajc e38cd32fa3 add tests 2026-07-26 02:40:35 +02:00
9qeklajc 423e2cba73 ppq-auto-topup 2026-07-26 02:38:07 +02:00
9qeklajc 1138cdd4ef Merge main and recreate mint URL migration 2026-07-26 00:16:10 +02:00