Indexer: narrow fee-discount registry health API (#373) #891

Merged
PlasticDigits merged 2 commits from issue-373-fee-discount-registry-health into main 2026-06-13 03:34:34 +00:00
PlasticDigits commented 2026-06-13 03:00:59 +00:00 (Migrated from gitlab.com)

Summary

Implements GitLab #373 — a dedicated GET /api/v1/health/fee-discount endpoint for ops/integrators to detect fee-discount registry LCD outages without extending generic GET /health or exposing per-trader registry errors.

  • Background loop probes registry config via LcdClient every 60s when FEE_DISCOUNT_ADDRESS is set
  • Shared in-memory snapshot: configured, fee_discount_registry_ok (null when unconfigured), consecutive_lcd_failures
  • Repeated probe failures log at tracing::error (≥2 consecutive); recovery at info
  • HTTP body is sanitized (no LCD URLs, upstream text, or trader enumeration)
  • Documented as the narrow observability exception in docs/indexer-invariants.md and skills/AGENTS_INDEXER_API_LCD_SECURITY.md

Acceptance checklist

Criterion Verification Result
GET /api/v1/health/fee-discount returns configured, fee_discount_registry_ok, consecutive_lcd_failures cd indexer && cargo test --test api_fee_discount_health configured_successful_probe_reports_ok -- --test-threads=1 PASS
Unconfigured: configured=false, fee_discount_registry_ok=null cd indexer && cargo test --test api_fee_discount_health unconfigured_fee_discount_health_returns_null_ok -- --test-threads=1 PASS
LCD failure increments counter, no upstream leak in body cd indexer && cargo test --test api_fee_discount_health lcd_failure_increments_counter_without_leaking_upstream_text -- --test-threads=1 PASS
Repeated probe failures emit tracing::error (≥2) cd indexer && cargo test fee_discount_registry_health::tests::repeated_lcd_failure_increments_counter --lib + code review fee_discount_registry_health.rs PASS
Generic GET /health unchanged cd indexer && cargo test --test api_fee_discount_health generic_health_unchanged -- --test-threads=1 PASS
Security sanitization spot-check cd indexer && cargo test --test security -- --test-threads=1 PASS
Unit probe tests cd indexer && cargo test fee_discount_registry_health --lib PASS
On-chain swap_uses_full_fee_when_discount_registry_query_fails unchanged Not re-run (no contract changes) SKIP

Third-party verification

make setup-indexer-postgres
cd indexer && cargo test fee_discount_registry_health --lib
cd indexer && cargo test --test api_fee_discount_health -- --test-threads=1
cd indexer && cargo test --test security -- --test-threads=1
curl -sS http://127.0.0.1:3001/health
curl -sS http://127.0.0.1:3001/api/v1/health/fee-discount

With indexer running and FEE_DISCOUNT_ADDRESS set, block registry LCD access and confirm fee_discount_registry_ok: false with no http://, cosmwasm, or All LCD endpoints failed in the JSON body.


Note

Low Risk
Narrow read-only health endpoint with sanitized JSON; no auth, routing, or on-chain contract changes.

Overview
GitLab #373 tightens GET /api/v1/health/fee-discount as the ops-facing signal for fee-discount registry LCD reachability, without changing generic GET /health.

The in-memory health model now distinguishes configured vs unconfigured via from_config / configured / unconfigured: when the registry address is set, fee_discount_registry_ok stays null until the first probe (no optimistic true on startup). Probe failures record LcdError only in tracing (warn / error with consecutive counts); the HTTP JSON still exposes only configured, fee_discount_registry_ok, and consecutive_lcd_failures.

Integration coverage adds api_fee_discount_health cases (generic health unchanged, unconfigured null OK, LCD failures increment counter without upstream leaks, successful probe, no trader query params) plus wiremock unit tests on the probe loop. docs/indexer-invariants.md and skills/AGENTS_INDEXER_API_LCD_SECURITY.md document this as observability exception H6b and re-ticket references from #365 to #373.

Reviewed by Cursor Bugbot for commit 85b2af8e5f. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary Implements GitLab #373 — a dedicated `GET /api/v1/health/fee-discount` endpoint for ops/integrators to detect fee-discount registry LCD outages without extending generic `GET /health` or exposing per-trader registry errors. - Background loop probes registry `config` via `LcdClient` every 60s when `FEE_DISCOUNT_ADDRESS` is set - Shared in-memory snapshot: `configured`, `fee_discount_registry_ok` (`null` when unconfigured), `consecutive_lcd_failures` - Repeated probe failures log at `tracing::error` (≥2 consecutive); recovery at `info` - HTTP body is sanitized (no LCD URLs, upstream text, or trader enumeration) - Documented as the narrow observability exception in `docs/indexer-invariants.md` and `skills/AGENTS_INDEXER_API_LCD_SECURITY.md` ## Acceptance checklist | Criterion | Verification | Result | |-----------|--------------|--------| | `GET /api/v1/health/fee-discount` returns `configured`, `fee_discount_registry_ok`, `consecutive_lcd_failures` | `cd indexer && cargo test --test api_fee_discount_health configured_successful_probe_reports_ok -- --test-threads=1` | PASS | | Unconfigured: `configured=false`, `fee_discount_registry_ok=null` | `cd indexer && cargo test --test api_fee_discount_health unconfigured_fee_discount_health_returns_null_ok -- --test-threads=1` | PASS | | LCD failure increments counter, no upstream leak in body | `cd indexer && cargo test --test api_fee_discount_health lcd_failure_increments_counter_without_leaking_upstream_text -- --test-threads=1` | PASS | | Repeated probe failures emit `tracing::error` (≥2) | `cd indexer && cargo test fee_discount_registry_health::tests::repeated_lcd_failure_increments_counter --lib` + code review `fee_discount_registry_health.rs` | PASS | | Generic `GET /health` unchanged | `cd indexer && cargo test --test api_fee_discount_health generic_health_unchanged -- --test-threads=1` | PASS | | Security sanitization spot-check | `cd indexer && cargo test --test security -- --test-threads=1` | PASS | | Unit probe tests | `cd indexer && cargo test fee_discount_registry_health --lib` | PASS | | On-chain `swap_uses_full_fee_when_discount_registry_query_fails` unchanged | Not re-run (no contract changes) | SKIP | ## Third-party verification ```bash make setup-indexer-postgres cd indexer && cargo test fee_discount_registry_health --lib cd indexer && cargo test --test api_fee_discount_health -- --test-threads=1 cd indexer && cargo test --test security -- --test-threads=1 curl -sS http://127.0.0.1:3001/health curl -sS http://127.0.0.1:3001/api/v1/health/fee-discount ``` With indexer running and `FEE_DISCOUNT_ADDRESS` set, block registry LCD access and confirm `fee_discount_registry_ok: false` with no `http://`, `cosmwasm`, or `All LCD endpoints failed` in the JSON body. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Narrow read-only health endpoint with sanitized JSON; no auth, routing, or on-chain contract changes. > > **Overview** > **GitLab #373** tightens **`GET /api/v1/health/fee-discount`** as the ops-facing signal for fee-discount registry LCD reachability, without changing generic **`GET /health`**. > > The in-memory health model now distinguishes **configured vs unconfigured** via **`from_config` / `configured` / `unconfigured`**: when the registry address is set, **`fee_discount_registry_ok` stays `null` until the first probe** (no optimistic `true` on startup). Probe failures record **`LcdError` only in `tracing`** (warn / error with consecutive counts); the HTTP JSON still exposes only **`configured`**, **`fee_discount_registry_ok`**, and **`consecutive_lcd_failures`**. > > Integration coverage adds **`api_fee_discount_health`** cases (generic health unchanged, unconfigured null OK, LCD failures increment counter without upstream leaks, successful probe, no trader query params) plus wiremock unit tests on the probe loop. **`docs/indexer-invariants.md`** and **`skills/AGENTS_INDEXER_API_LCD_SECURITY.md`** document this as observability exception **H6b** and re-ticket references from #365 to #373. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 85b2af8e5ffadddf89594895b1a40ed893904fcc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ghost1 commented 2026-06-13 03:01:06 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-13 03:01:13 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-13 03:02:58 +00:00 (Migrated from gitlab.com)

Security review — MR !891

Commit reviewed: a165749c4c8c61438cfac75edf40ef9369706c05

Scope: GET /api/v1/health/fee-discount, background fee-discount registry LCD probe (fee_discount_registry_health.rs), router wiring, docs/tests for H6b sanitization.

Method: Diff review; traced request path (no attacker-controlled input to LCD/DB); verified response is a fixed struct clone (configured, fee_discount_registry_ok, consecutive_lcd_failures only); confirmed upstream LcdError text stays in tracing logs, not HTTP body; compared middleware (global rate limit, no per-request LCD); checked prior MR notes (no prior security threads).

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

  • Handler is read-only over an in-memory snapshot; query params are ignored (no injection/SSRF surface).
  • LCD probe uses operator-configured FEE_DISCOUNT_ADDRESS and fixed {"config":{}} query on a 60s background loop — not request-amplifiable.
  • HTTP JSON cannot leak LCD URLs, wasm paths, or trader/registry detail; integration tests assert forbidden substrings absent.
  • Same auth posture as existing public /health (global tower_governor only).

Inline threads: none (no findings to anchor).

## Security review — MR !891 **Commit reviewed:** `a165749c4c8c61438cfac75edf40ef9369706c05` **Scope:** `GET /api/v1/health/fee-discount`, background fee-discount registry LCD probe (`fee_discount_registry_health.rs`), router wiring, docs/tests for H6b sanitization. **Method:** Diff review; traced request path (no attacker-controlled input to LCD/DB); verified response is a fixed struct clone (`configured`, `fee_discount_registry_ok`, `consecutive_lcd_failures` only); confirmed upstream `LcdError` text stays in `tracing` logs, not HTTP body; compared middleware (global rate limit, no per-request LCD); checked prior MR notes (no prior security threads). **Outcome:** `FINDINGS: 0` medium+ Security review: **no medium+ findings** on this diff. - Handler is read-only over an in-memory snapshot; query params are ignored (no injection/SSRF surface). - LCD probe uses operator-configured `FEE_DISCOUNT_ADDRESS` and fixed `{"config":{}}` query on a 60s background loop — not request-amplifiable. - HTTP JSON cannot leak LCD URLs, wasm paths, or trader/registry detail; integration tests assert forbidden substrings absent. - Same auth posture as existing public `/health` (global `tower_governor` only). **Inline threads:** none (no findings to anchor).
PlasticDigits commented 2026-06-13 03:17:07 +00:00 (Migrated from gitlab.com)

added 89 commits

  • a165749c...edbe2887 - 88 commits from branch main
  • 85b2af8e - Merge origin/main into issue-373-fee-discount-registry-health

Compare with previous version

added 89 commits <ul><li>a165749c...edbe2887 - 88 commits from branch <code>main</code></li><li>85b2af8e - Merge origin/main into issue-373-fee-discount-registry-health</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/173/diffs?diff_id=1850141067&start_sha=a165749c4c8c61438cfac75edf40ef9369706c05)
PlasticDigits commented 2026-06-13 03:17:10 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main after integrating #365's poller-backed cancellable probe loop.

Conflict resolution:

  • fee_discount_registry_health.rs: merged main's run_fee_discount_registry_probe_loop (cancellation-aware, spawned from poller) with #373's from_config/configured constructors, null-until-first-probe initial state, and tracing-only upstream error detail
  • main.rs: dropped duplicate probe spawn (#373); probe runs via poller like main
  • docs/indexer-invariants.md: kept main's #363 rate-limit row + #373 observability exception
  • Tests: retained full #373 api_fee_discount_health suite

Sanity: cargo test fee_discount_registry_health --lib and cargo test --test api_fee_discount_health -- --test-threads=1 pass.

Resolved merge conflicts with `main` after integrating #365's poller-backed cancellable probe loop. **Conflict resolution:** - `fee_discount_registry_health.rs`: merged main's `run_fee_discount_registry_probe_loop` (cancellation-aware, spawned from poller) with #373's `from_config`/`configured` constructors, null-until-first-probe initial state, and tracing-only upstream error detail - `main.rs`: dropped duplicate probe spawn (#373); probe runs via poller like main - `docs/indexer-invariants.md`: kept main's #363 rate-limit row + #373 observability exception - Tests: retained full #373 `api_fee_discount_health` suite Sanity: `cargo test fee_discount_registry_health --lib` and `cargo test --test api_fee_discount_health -- --test-threads=1` pass.
ghost1 commented 2026-06-13 03:17:26 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-13 03:18:48 +00:00 (Migrated from gitlab.com)

Security review — MR !891

Commit reviewed: 85b2af8e5ffadddf89594895b1a40ed893904fcc

Scope: Post-merge re-review of GET /api/v1/health/fee-discount, fee_discount_registry_health.rs (cancellable 60s background probe via poller), handler wiring, docs/tests (H6b). Prior review on a165749c predates the origin/main merge.

Method: Diff and full-path trace on current SHA; attacker-controlled input → sink analysis; verified HTTP body is fixed FeeDiscountRegistryHealthSnapshot only (configured, fee_discount_registry_ok, consecutive_lcd_failures); confirmed LcdError / LCD URL text stays in tracing logs only (record_probe_failure), not JSON; LCD probe uses operator FEE_DISCOUNT_ADDRESS + fixed {"config":{}} on background loop (not request-amplifiable); query params ignored (endpoint_has_no_trader_query_params); global tower_governor on api_router (same posture as /health).

Outcome: FINDINGS: 0 medium+

Security review: no medium+ findings on this diff.

Inline threads: none (no findings to anchor).

## Security review — MR !891 **Commit reviewed:** `85b2af8e5ffadddf89594895b1a40ed893904fcc` **Scope:** Post-merge re-review of `GET /api/v1/health/fee-discount`, `fee_discount_registry_health.rs` (cancellable 60s background probe via poller), handler wiring, docs/tests (H6b). Prior review on `a165749c` predates the `origin/main` merge. **Method:** Diff and full-path trace on current SHA; attacker-controlled input → sink analysis; verified HTTP body is fixed `FeeDiscountRegistryHealthSnapshot` only (`configured`, `fee_discount_registry_ok`, `consecutive_lcd_failures`); confirmed `LcdError` / LCD URL text stays in `tracing` logs only (`record_probe_failure`), not JSON; LCD probe uses operator `FEE_DISCOUNT_ADDRESS` + fixed `{"config":{}}` on background loop (not request-amplifiable); query params ignored (`endpoint_has_no_trader_query_params`); global `tower_governor` on `api_router` (same posture as `/health`). **Outcome:** `FINDINGS: 0` medium+ Security review: **no medium+ findings** on this diff. **Inline threads:** none (no findings to anchor).
PlasticDigits commented 2026-06-13 03:34:34 +00:00 (Migrated from gitlab.com)

mentioned in commit cccd4cf23e

mentioned in commit cccd4cf23e247efc3215242f320e58c7eb491353
PlasticDigits (Migrated from gitlab.com) merged commit cccd4cf23e into main 2026-06-13 03:34:34 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic!891
No description provided.