Skip to content

feat: add trusted bearer-key faucet API - #2

Open
infraclaw-dash wants to merge 1 commit into
dashpay:mainfrom
infraclaw-dash:infraclaw/api-key-faucet
Open

infraclaw-dash wants to merge 1 commit into
dashpay:mainfrom
infraclaw-dash:infraclaw/api-key-faucet

Conversation

@infraclaw-dash

@infraclaw-dash infraclaw-dash commented Jul 14, 2026

Copy link
Copy Markdown

Summary

  • add a dedicated bearer-key endpoint at POST /api/v1/core-faucet
  • store only SHA-256 API-key hashes in faucet configuration
  • bypass the browser CAPTCHA and public per-IP limit while retaining a per-key rolling daily safety limit
  • keep promo codes unavailable on the trusted endpoint
  • document configuration and usage

Security

  • requests without a valid bearer token are rejected before address validation or payout
  • key IDs, rather than plaintext keys, are used for rate limiting and logs
  • the public /api/core-faucet behavior is unchanged

Validation

  • Docker image builds successfully
  • python -m unittest discover -s tests -v (7 tests pass)
  • OpenAPI schema includes /api/v1/core-faucet

Summary by CodeRabbit

  • New Features
    • Added a trusted server-to-server faucet endpoint with bearer-key authentication.
    • Added per-key rolling daily payout limits and Retry-After responses when limits are reached.
    • Trusted requests accept an address only and bypass browser CAPTCHA and public IP rate limits.
  • Documentation
    • Documented trusted API configuration, authentication, request requirements, limits, and usage rules.
  • Tests
    • Added coverage for API-key validation, authentication, successful payouts, invalid keys, and daily-limit enforcement.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Trusted faucet access is added through bearer API keys configured as SHA-256 hashes, with per-key daily limits, shared payout handling, documentation, deployment configuration, and automated tests.

Trusted Faucet API

Layer / File(s) Summary
Configuration and API contract
.env.example, app/config.py, docker-compose.yml, README.md
Adds trusted API key hash and daily-limit settings, deployment defaults, and documentation for the authenticated endpoint.
API-key authentication
app/services/api_keys.py
Validates configured SHA-256 mappings and authenticates bearer tokens using constant-time comparisons.
Trusted payout flow
app/routers/faucet.py
Extracts shared payout handling, preserves public faucet recording behavior, and adds authenticated per-key throttling for POST /v1/core-faucet.
Authentication and endpoint validation
tests/test_api_keys.py, tests/test_trusted_faucet.py
Tests key parsing, authentication outcomes, successful payouts, usage recording, and daily-limit rejection.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TrustedClient
  participant api_core_faucet
  participant api_key_rate_limiter
  participant dispense_core_dash
  TrustedClient->>api_core_faucet: POST /v1/core-faucet with bearer key and address
  api_core_faucet->>api_key_rate_limiter: Check key daily allowance
  api_key_rate_limiter-->>api_core_faucet: Allow or reject with retry period
  api_core_faucet->>dispense_core_dash: Validate address and send DASH
  dispense_core_dash-->>api_core_faucet: CoreFaucetResponse
  api_core_faucet->>api_key_rate_limiter: Record successful key request
  api_core_faucet-->>TrustedClient: CoreFaucetResponse
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a trusted bearer-key faucet API.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Warning

⚠️ This pull request shows signs of AI-generated slop (redundant_comments). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
.env.example (1)

27-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Key ordering flagged by dotenv-linter.

FAUCET_API_KEY_DAILY_LIMIT should be placed before FAUCET_API_KEY_HASHES alphabetically.

♻️ Suggested reorder
-FAUCET_API_KEY_HASHES=
-FAUCET_API_KEY_DAILY_LIMIT=100
+FAUCET_API_KEY_DAILY_LIMIT=100
+FAUCET_API_KEY_HASHES=
🤖 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 @.env.example around lines 27 - 28, Reorder the FAUCET_API_KEY configuration
entries so FAUCET_API_KEY_DAILY_LIMIT appears before FAUCET_API_KEY_HASHES,
without changing either value.

Source: Linters/SAST tools

🤖 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 `@app/routers/faucet.py`:
- Around line 692-704: Replace the separate api_key_rate_limiter.is_allowed and
record_request calls around dispense_core_dash with an atomic try_reserve
operation that checks and records the key within one lock acquisition, rejecting
with the existing 429 response when reservation fails. If the payout RPC fails
or does not complete successfully, release the reservation through the
corresponding RateLimiter release operation so failed requests do not consume
the daily allowance.

---

Nitpick comments:
In @.env.example:
- Around line 27-28: Reorder the FAUCET_API_KEY configuration entries so
FAUCET_API_KEY_DAILY_LIMIT appears before FAUCET_API_KEY_HASHES, without
changing either value.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e52ce42-dd23-456c-bb85-5b503bbe53ec

📥 Commits

Reviewing files that changed from the base of the PR and between b927e60 and 9fa877b.

📒 Files selected for processing (8)
  • .env.example
  • README.md
  • app/config.py
  • app/routers/faucet.py
  • app/services/api_keys.py
  • docker-compose.yml
  • tests/test_api_keys.py
  • tests/test_trusted_faucet.py

Comment thread app/routers/faucet.py
Comment on lines +692 to +704
allowed, retry_after = api_key_rate_limiter.is_allowed(key_id)
if not allowed:
raise HTTPException(
status_code=429,
detail={
"error": "API key daily limit exceeded",
"retryAfter": retry_after,
},
headers={"Retry-After": str(retry_after)},
)

result = dispense_core_dash(body.address, settings.core_faucet_amount)
api_key_rate_limiter.record_request(key_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Daily-limit check-then-act race across the payout RPC call.

api_key_rate_limiter.is_allowed(key_id) and record_request(key_id) are separate locked operations with dispense_core_dash's network call to Dash Core RPC executed in between. Concurrent requests using the same API key can all pass the check before any of them records, letting a key exceed the intended daily safety cap.

🔒 Suggested direction: make check+reserve atomic
-    allowed, retry_after = api_key_rate_limiter.is_allowed(key_id)
-    if not allowed:
-        raise HTTPException(
-            status_code=429,
-            detail={
-                "error": "API key daily limit exceeded",
-                "retryAfter": retry_after,
-            },
-            headers={"Retry-After": str(retry_after)},
-        )
-
-    result = dispense_core_dash(body.address, settings.core_faucet_amount)
-    api_key_rate_limiter.record_request(key_id)
+    allowed, retry_after = api_key_rate_limiter.try_reserve(key_id)
+    if not allowed:
+        raise HTTPException(
+            status_code=429,
+            detail={
+                "error": "API key daily limit exceeded",
+                "retryAfter": retry_after,
+            },
+            headers={"Retry-After": str(retry_after)},
+        )
+
+    try:
+        result = dispense_core_dash(body.address, settings.core_faucet_amount)
+    except Exception:
+        api_key_rate_limiter.release(key_id)
+        raise

(try_reserve/release would need to be added to RateLimiter, performing the check-and-append under one lock acquisition.)

🤖 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 `@app/routers/faucet.py` around lines 692 - 704, Replace the separate
api_key_rate_limiter.is_allowed and record_request calls around
dispense_core_dash with an atomic try_reserve operation that checks and records
the key within one lock acquisition, rejecting with the existing 429 response
when reservation fails. If the payout RPC fails or does not complete
successfully, release the reservation through the corresponding RateLimiter
release operation so failed requests do not consume the daily allowance.

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.

1 participant