Skip to content

feat(price): Phase 3 — El Toque fiat-cross provider (CUP/MLC)#778

Merged
grunch merged 4 commits into
mainfrom
feat/price-phase3-eltoque
Jun 18, 2026
Merged

feat(price): Phase 3 — El Toque fiat-cross provider (CUP/MLC)#778
grunch merged 4 commits into
mainfrom
feat/price-phase3-eltoque

Conversation

@grunch

@grunch grunch commented Jun 16, 2026

Copy link
Copy Markdown
Member

Phase 3 of docs/PRICE_PROVIDERS.md — El Toque (fiat-cross CUP/MLC)

Phase (spec §9, §11.3): 3 — depends on Phase 2 (#773, merged). Adds the El Toque provider: a fiat-cross quoter that contributes CUP and MLC to the aggregate. Unlike the direct quoters it reports Quote::PerBase { base: "USD" }, resolved to a per-BTC figure against the aggregated USD/BTC anchor by Phase 0's existing resolve_per_baseno aggregation-core change.

Per the §5.4 extension contract this is one adapter file + one registry arm + one config block + one fixture; aggregate.rs, store.rs, the scheduler and every order handler are untouched.

What it does

El Toque publishes the informal Cuban market rate as CUP per foreign unit (it is not a BTC source). The adapter parses its CUP-denominated tasas payload and emits, doing the §11.3 cross math internally:

Currency Quote emitted Derivation
CUP PerBase { base: "USD", value: cup_per_usd } straight from tasas.USD
MLC PerBase { base: "USD", value: cup_per_usd / cup_per_mlc } MLC_per_USD = (CUP per USD) / (CUP per MLC)
  • Anchor = USD only (the §11.3 EUR-fallback question was declined for this phase). If every direct USD quoter is down for a tick, CUP/MLC fall back to last-known-good.
  • Bearer token required when enabled → startup error otherwise (§7); token redacted from Debug/logs (§10.3).
  • Scoped to only = ["CUP", "MLC"]; the adapter independently emits only CUP/MLC, so the two agree. CUP/MLC are already in the §6.6 fiat allowlist, so they survive to aggregation.

Request & response (confirmed against the live API)

The El Toque request line — previously provisional — is now confirmed with the maintainer:

  • Request: GET {url}/v1/trmi?date_from=…&date_to=… with Authorization: Bearer <token>. The endpoint requires a [date_from, date_to] range (wire format YYYY-MM-DD HH:MM:SS, URL-encoded by reqwest) and returns the most recent rate published within it. fetch therefore queries a rolling 48h window ending "now" (LOOKBACK_HOURS) — wide enough to absorb the ~daily TRMI update cadence plus any UTC↔Cuba (UTC-4/-5) skew, so a quiet day still resolves a recent rate instead of empty tasas.
  • Response: a CUP-denominated tasas object plus the timestamp of the returned rate (date/hour/minutes/seconds, ignored by the parser). El Toque uses ECU for the euro:
    { "tasas": { "USD": 490.0, "MLC": 200.0, "ECU": 540.0, "TRX": 150.0, "BTC": 490.0, "USDT_TRC20": 525.0 },
      "date": "2022-10-27", "hour": 7, "minutes": 59, "seconds": 30 }
  • The shipped fixture (tests/fixtures/price/eltoque_trmi.json) is a real captured response; the parse path applies the §11.3 cross math and is fully unit-tested.

Still open (§11.3 Q1/Q3, maintainer questions): whether the chosen plan exposes CUP/MLC/USD in one call, and confirming El Toque's CUP and Yadio's CUP track the same informal rate. The template ships enabled = false; keep it off until a token is provisioned and the operator opts in.

Files

  • src/price/providers/eltoque.rs — the adapter (+ 8 unit tests).
  • src/price/providers/mod.rspub mod eltoque;.
  • src/price/manager.rs — registry arm (build_provider) builds El Toque, failing fast without a token; the old "unimplemented" rejection test is replaced by with-token/without-token coverage.
  • settings.tpl.toml — real [price.providers.eltoque] block (enabled = false).
  • docs/PRICE_PROVIDERS.md — Phase 3 status + §11.3 confirmed request/response note.
  • tests/fixtures/price/eltoque_trmi.json — captured El Toque /v1/trmi response.

Test evidence

  • 8 adapter tests: CUP/MLC PerBase emission · MLC cross math · no-USD-anchor → nothing · non-positive dropped · parse error · token required · Debug redaction · trailing-slash strip.
  • Registry tests updated: builds with token / rejects without token.
  • End-to-end PerBase resolution (El Toque CUP via USD anchor) is already covered by the Phase 0 aggregate_tick_unions_partial_coverage test.
  • Full suite: 472 passed, 0 failed; cargo clippy --all-targets --all-features + cargo fmt clean.

🧪 Manual testing — step by step

Prereqs: a working mostrod dev setup (LND not required for price checks; the daemon logs price ticks regardless of trading activity). To exercise everything without burning a real token, point El Toque at a tiny local mock serving the shipped fixture. (The live API requires a real Bearer token; once you have one, swap url/token for the real endpoint and the same checks apply.)

1. Startup validation — token is required (spec §7)

  1. Add an El Toque block without a token to settings.toml:
    [price.providers.eltoque]
    enabled = true
    url = "https://tasas.eltoque.com"
    only = ["CUP", "MLC"]
  2. Start mostrod. Expected: it refuses to start with
    price provider 'eltoque': enabled provider requires a \token` (Bearer API key) …`.
  3. Add token = "any-string" and restart — startup proceeds. ✔️

2. Cross-math + PerBase resolution against a local mock

This exercises the parse path and the USD-anchor resolution without a real token.

  1. Serve the fixture locally at the /v1/trmi path. From the repo root:
    mkdir -p /tmp/eltoque/v1
    cp tests/fixtures/price/eltoque_trmi.json /tmp/eltoque/v1/trmi
    (cd /tmp/eltoque && python3 -m http.server 8099)
    (The adapter sends GET {url}/v1/trmi?date_from=…&date_to=… with Authorization: Bearer <token>; Python's http.server strips the query string and ignores the header, returning the JSON file.)
  2. Configure Yadio (for the USD anchor) + El Toque (mock):
    [price]
    update_interval_seconds = 60
    
    [price.providers.yadio]
    enabled = true
    url = "https://api.yadio.io"
    
    [price.providers.eltoque]
    enabled = true
    url = "http://127.0.0.1:8099"
    token = "dev"
    only = ["CUP", "MLC"]
  3. Run RUST_LOG=mostrod=info cargo run -- -d <data-dir> and watch one tick. Expected: both price: yadio ok (…) and price: eltoque ok (2 currencies).
  4. Inspect the kind-30078 event (e.g. nak req -k 30078 -a <mostro pubkey> <relay>) and verify with the fixture's numbers (USD=490, MLC=200 CUP):
    • CUP/BTC ÷ USD/BTC ≈ 490 (informal CUP per USD), and
    • CUP/BTC ÷ MLC/BTC ≈ 200 (1 MLC ≈ 200 CUP, matching the source).
    • The source tag includes eltoque for CUP/MLC.

3. Anchor dependency — no USD source ⇒ CUP/MLC can't resolve (spec §11.3)

  1. With the mock still running, disable every direct quoter (so no USD/BTC anchor exists), leaving only El Toque enabled.
  2. Restart and watch a tick: eltoque ok (2 currencies) still polls, but get_price("CUP") / ("MLC") produce no fresh aggregate — the PerBase quotes are dropped for lack of a USD anchor, and the currencies fall back to last-known-good (or are absent on a cold store). Re-enable Yadio → CUP/MLC resolve again next tick. ✔️

4. Scoping — El Toque contributes only CUP/MLC

  1. Note the fixture's tasas also lists USD, ECU (EUR) and crypto. Confirm the published event's eltoque-sourced currencies are only CUP and MLC: El Toque never contributes USD/EUR (the adapter emits only CUP/MLC, and only = ["CUP","MLC"] backstops it). USD/EUR come from the direct quoters alone. ✔️

5. Secret never leaks (spec §10.3)

  1. Run with RUST_LOG=mostrod=debug and a recognisable token (e.g. token = "SECRET123").
  2. Grep the logs: the token must never appear. The provider's Debug prints token: "<redacted>". ✔️

6. Regression — disabled / legacy untouched

  1. Set enabled = false on El Toque (or remove the block). Expected: daemon runs exactly as in Phase 2 — no El Toque poll, CUP served by Yadio's informal rate alone, no behaviour change. ✔️
  2. Delete the whole [price] block: legacy single-source Yadio path still works (Phase 1 migration), untouched by this PR. ✔️

Note for reviewers: the request line is now confirmed (date-ranged /v1/trmi, Bearer auth) and the fixture is a real capture. The remaining work before any operator enables El Toque in production is provisioning a token and resolving the §11.3 Q1/Q3 maintainer questions; the provider ships enabled = false until then.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added the El Toque Phase 3 price provider for CUP/MLC quotes with provisional enablement via configuration.
    • Uses a token-authenticated TRMI request over a rolling 48-hour window and derives CUP→MLC from the USD anchor when available.
    • Fails to start (and won’t provide quotes) if the required Bearer token is missing; provider is still disabled in production until provisioning/opt-in is confirmed.
  • Documentation
    • Updated Phase 3 status and added detailed provisional wiring guidance, including the provider configuration.
  • Bug Fixes
    • Added a warning when legacy Mostro price URL settings are ignored in favor of configured price aggregation.
  • Tests
    • Expanded coverage for El Toque provider setup, parsing, and token redaction.

…ional wiring

Implements Phase 3 of docs/PRICE_PROVIDERS.md (§9, §11.3): the El Toque
provider, a fiat-cross quoter that contributes CUP and MLC as
`Quote::PerBase { base: "USD" }`, resolved against the aggregated USD/BTC
anchor by Phase 0's existing `resolve_per_base` (no aggregation-core change).

Per §5.4 this is one adapter file + one registry arm + one config block +
one fixture — the aggregation core, store, scheduler and handlers are
untouched.

- src/price/providers/eltoque.rs: ElToqueProvider. Parses El Toque's
  CUP-denominated `tasas` payload and emits:
    CUP -> PerBase{USD, cup_per_usd}
    MLC -> PerBase{USD, cup_per_usd / cup_per_mlc}   (§11.3 cross math)
  Bearer-token auth (required when enabled -> startup error otherwise,
  §7); token redacted from Debug/logs (§10.3). Anchor = USD only (the
  §11.3 EUR-fallback question was declined for this phase).
- manager.rs: registry arm builds El Toque (fails fast without a token);
  the old "unimplemented" rejection and its test are replaced with
  with-token / without-token coverage.
- settings.tpl.toml: real [price.providers.eltoque] block (enabled=false).
- docs/PRICE_PROVIDERS.md: Phase 3 marked in review; §11.3 status note.

PROVISIONAL: the tasas API is token-gated, so a real payload could not be
captured. The parse path is grounded in the confirmed CUP-denominated
`tasas` shape and is fully unit-tested; the request line in `fetch` (path
/v1/trmi, GET, no date params) is best-effort from third-party
reverse-engineering and the shipped fixture is a representative sample,
not a capture. All clearly flagged in comments and the spec — to be
finalised against the official docs in a follow-up. Keep enabled=false in
production until then.

Tests: 8 new El Toque adapter tests + updated registry tests. Full suite
472 passed; cargo clippy + fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a new ElToqueProvider Rust adapter for CUP/MLC price quoting against El Toque's token-gated tasas API. The adapter uses USD as the anchor rate and derives MLC via cross-rate math. It is wired into PriceManager's provider registry but shipped disabled in the config template, with documentation marking Phase 3 as provisional pending official API confirmation. A runtime warning notifies operators when the legacy price URL is overridden by the multi-provider configuration.

Changes

ElToque Provider Adapter

Layer / File(s) Summary
ElToqueProvider struct, parse, and fetch logic
src/price/providers/eltoque.rs, tests/fixtures/price/eltoque_trmi.json
Defines ElToqueResponse deserialization target, ElToqueProvider struct with token-redacting Debug impl and token/URL validation in new(), parse() with USD-anchored CUP quoting and MLC cross-rate math (dropping non-positive/non-finite values), and PriceProvider::fetch() performing bearer-authenticated GET to {url}/v1/trmi with rolling 48h date window. Unit tests cover fixture parsing, cross-math, missing USD anchor, invalid rates, parse errors, token enforcement, debug redaction, and URL normalization. The eltoque_trmi.json fixture provides the tasas payload shape used by tests.
Provider module export
src/price/providers/mod.rs
Exports pub mod eltoque to make the ElToqueProvider publicly available from the providers module.
PriceManager registry wiring
src/price/manager.rs
Imports ElToqueProvider, updates Phase 1/2/3 invariant documentation to reflect El Toque as a Phase 3 provider, changes build_provider's ProviderId::ElToque arm from a hard "not implemented" error to ElToqueProvider::new(cfg)?, and replaces the prior unimplemented-provider rejection test with token-present and token-missing construction tests.
Configuration template and documentation updates
settings.tpl.toml, docs/PRICE_PROVIDERS.md
Adds a disabled [price.providers.eltoque] TOML block with only = ["CUP", "MLC"] scoping. Updates the Phase 3 table entry in PRICE_PROVIDERS.md from "pending" to "in review / provisional wiring" and adds a shipped-status note covering USD-only anchor, rolling 48h request window, fixture limitations, and production-disabled status pending official API confirmation.
Runtime settings warning integration
src/main.rs
When [price] settings are configured, logs a warning listing enabled provider IDs and noting that the legacy mostro.bitcoin_price_api_url setting is ignored in the multi-source provider configuration.

Sequence Diagram(s)

sequenceDiagram
  participant PriceManager
  participant ElToqueProvider
  participant reqwest as reqwest::Client
  participant API as El Toque API

  rect rgba(70, 130, 180, 0.5)
    note over PriceManager,ElToqueProvider: Startup — build_provider
    PriceManager->>ElToqueProvider: new(cfg) — validate token, normalize URL
    ElToqueProvider-->>PriceManager: Ok(ElToqueProvider) or Err(missing token)
  end

  rect rgba(60, 179, 113, 0.5)
    note over PriceManager,API: Runtime — fetch cycle
    PriceManager->>ElToqueProvider: fetch(http)
    ElToqueProvider->>reqwest: GET {url}/v1/trmi + Authorization: Bearer <token>
    reqwest->>API: HTTP GET /v1/trmi?date_from=…&date_to=…
    API-->>reqwest: 200 {tasas:{USD,MLC,...}}
    reqwest-->>ElToqueProvider: response body
    ElToqueProvider->>ElToqueProvider: parse — anchor USD→CUP, derive cup_per_usd/cup_per_mlc for MLC
    ElToqueProvider-->>PriceManager: ProviderQuotes([CUP, MLC])
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • MostroP2P/mostro#747: Introduces the Phase 0 abstractions (PriceProvider, ProviderId, ProviderConfig) that ElToqueProvider directly implements.
  • MostroP2P/mostro#753: Modifies the same build_provider registry logic in src/price/manager.rs to integrate a different provider adapter (Yadio), following an identical integration pattern.
  • MostroP2P/mostro#773: Modifies src/price/manager.rs's provider wiring/validation logic for ProviderId::ElToque, explicitly rejecting it as unimplemented in Phase 2, which this PR changes to enable and construct.

Suggested reviewers

  • AndreaDiazCorreia
  • BraCR10
  • Catrya
  • arkanoider

Poem

🐇 A token was asked, and a token was given,
The CUP and MLC rates finally driven.
USD anchors the math — cross it with flair,
The fixture says "tasas," the parse does its share.
Production stays off till the real docs arrive,
But the rabbit has wired it — Phase 3's alive! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing Phase 3 of the price provider specification by adding the El Toque fiat-cross provider for CUP/MLC quotes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/price-phase3-eltoque

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codaMW codaMW left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on regtest exercised everything that doesn't depend on the live (token-gated) El Toque endpoint, per the manual-testing notes. The grounded parts check out; ACK on that basis, with the provisional request-wiring caveat below.

Build + tests
• Builds clean.
• The 10 El Toque tests pass: parse → CUP/MLC PerBase emission, the MLC cross-math (cup_per_usd / cup_per_mlc), no-USD-anchor → nothing, non-positive dropped, parse error, token-required, Debug redaction, trailing-slash strip, plus the two registry arms (builds with token / rejects without).
• Full suite: 471 pass. The one failure is test_lnurl_validation_with_test_server (AddrInUse / port bind) Lightning subsystem, untouched here, fails identically on main pre-existing local-env flake, not from this PR.

Live tick against a local mock (Scenario 2 + 5)
Served the shipped fixture at /v1/trmi via python3 -m http.server, with Yadio enabled for the USD anchor:

price: yadio ok (127 currencies)
price: eltoque ok (2 currencies)
price: published exchange rates to Nostr (123 currencies)

So El Toque polls, parses, emits exactly CUP + MLC, and they resolve against the aggregated USD/BTC anchor the PerBase path works end-to-end, stable across ticks. Scoping holds (only the 2 currencies, not USD/ECU/crypto from the tasas payload).

Secret redaction (Scenario 5): I ran with token = "SECRET123" and grepped the full log the token appears 0 times. Debug redaction holds at runtime, matching the debug_redacts_token unit test.

Provisional wiring (acknowledged, not a blocker)
The parse path is grounded and well-tested; the fetch request line (path /v1/trmi, GET, params) is the reverse-engineered part that can't be verified without the token-gated API. That's clearly flagged in the module comment + docs §11.3, the template ships enabled = false, and per the description it was agreed before implementation. So I'm treating it as a known, safely-gated limitation rather than a defect the follow-up to confirm the request against the official docs + drop in a real fixture should land before anyone enables it in production, as the PR states.

Nice touch: the parser being lenient on individual null rates (Option) so one bad entry can't fail the whole poll.

grunch and others added 3 commits June 17, 2026 17:48
Apply the El Toque API details confirmed with the maintainer, finalising
the previously-provisional request wiring:

- fetch now sends the required [date_from, date_to] range:
  GET /v1/trmi?date_from=…&date_to=… (YYYY-MM-DD HH:MM:SS, URL-encoded
  by reqwest), Bearer auth unchanged. The endpoint returns the most
  recent rate within the range, so we query a rolling 48h window ending
  "now" (LOOKBACK_HOURS) — wide enough to absorb the ~daily TRMI update
  cadence and UTC↔Cuba skew without returning empty tasas.
- tests/fixtures/price/eltoque_trmi.json: replaced the representative
  sample with a real captured response. This also corrects the timestamp
  shape — hour/minutes/seconds are separate integers, not a "HH:MM:SS"
  string (the parser ignores them either way).
- Dropped the PROVISIONAL/reverse-engineering language from the module
  doc and docs/PRICE_PROVIDERS.md; documented the confirmed request and
  response shapes. Tests updated to the captured values (USD=490,
  MLC=200 → MLC/USD = 490/200).

Q1/Q3 (§11.3 maintainer questions) remain open; provider stays
enabled=false until a token is provisioned.

Tests: full price suite 91 passed; cargo clippy --all-targets + fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /v1/trmi endpoint rejects any [date_from, date_to] range of 24h or
more with `400 "El intervalo de tiempo debe ser menor a 24 horas"`, so
every poll failed regardless of token. Lower LOOKBACK_HOURS from 48 to
23 (1h margin under the hard cap, still spanning a full daily TRMI
cycle) and document the constraint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NvSncWrjedLiAtAV4DTJR
When `[price]` is configured the multi-source manager drives aggregation
and the legacy `[mostro].bitcoin_price_api_url` is not consulted. Emit a
startup WARN naming the legacy value and the enabled providers so an
operator who still has the legacy key set isn't misled into thinking it
takes effect.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015NvSncWrjedLiAtAV4DTJR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main.rs`:
- Around line 223-227: The tracing::warn! call is logging the full
bitcoin_price_api_url which may contain embedded credentials (userinfo, query
tokens, etc.). Parse the bitcoin_price_api_url as a URL object and construct a
sanitized version containing only the scheme, host, and path while stripping
userinfo, query parameters, and fragments. Replace the direct
mostro_settings.bitcoin_price_api_url reference in the log message with the
sanitized URL string to prevent credential leakage in logs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d009bcc-3cc5-4719-8362-dccd1fe3c879

📥 Commits

Reviewing files that changed from the base of the PR and between eb6ee30 and 63918c0.

📒 Files selected for processing (1)
  • src/main.rs

Comment thread src/main.rs
Comment on lines +223 to +227
tracing::warn!(
"price: legacy `bitcoin_price_api_url` = \"{}\" is ignored for price \
aggregation because `[price]` is configured; using enabled providers: {}",
mostro_settings.bitcoin_price_api_url,
enabled,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid logging bitcoin_price_api_url verbatim.

Logging the full legacy URL can leak credentials if operators embed auth data (userinfo or query tokens) in the URL. Log a redacted/sanitized form instead (e.g., scheme + host + path, strip userinfo/query/fragment).

🔒 Suggested fix
+            let legacy_url_for_log = reqwest::Url::parse(&mostro_settings.bitcoin_price_api_url)
+                .map(|mut u| {
+                    let _ = u.set_username("");
+                    let _ = u.set_password(None);
+                    u.set_query(None);
+                    u.set_fragment(None);
+                    u.to_string()
+                })
+                .unwrap_or_else(|_| "<invalid_url>".to_string());
             tracing::warn!(
                 "price: legacy `bitcoin_price_api_url` = \"{}\" is ignored for price \
                  aggregation because `[price]` is configured; using enabled providers: {}",
-                mostro_settings.bitcoin_price_api_url,
+                legacy_url_for_log,
                 enabled,
             );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tracing::warn!(
"price: legacy `bitcoin_price_api_url` = \"{}\" is ignored for price \
aggregation because `[price]` is configured; using enabled providers: {}",
mostro_settings.bitcoin_price_api_url,
enabled,
let legacy_url_for_log = reqwest::Url::parse(&mostro_settings.bitcoin_price_api_url)
.map(|mut u| {
let _ = u.set_username("");
let _ = u.set_password(None);
u.set_query(None);
u.set_fragment(None);
u.to_string()
})
.unwrap_or_else(|_| "<invalid_url>".to_string());
tracing::warn!(
"price: legacy `bitcoin_price_api_url` = \"{}\" is ignored for price \
aggregation because `[price]` is configured; using enabled providers: {}",
legacy_url_for_log,
enabled,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main.rs` around lines 223 - 227, The tracing::warn! call is logging the
full bitcoin_price_api_url which may contain embedded credentials (userinfo,
query tokens, etc.). Parse the bitcoin_price_api_url as a URL object and
construct a sanitized version containing only the scheme, host, and path while
stripping userinfo, query parameters, and fragments. Replace the direct
mostro_settings.bitcoin_price_api_url reference in the log message with the
sanitized URL string to prevent credential leakage in logs.

@Catrya Catrya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@grunch grunch merged commit 82f1923 into main Jun 18, 2026
8 checks passed
@grunch grunch deleted the feat/price-phase3-eltoque branch June 18, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants