This document describes the cross-cutting infrastructure and request gateway layer of RemitWise. Understanding this flow is essential for implementing secure endpoints and safe background jobs.
Every API request entering the application flows through the Next.js request gateway defined in middleware.ts.
graph TD
A[Incoming Request] --> B{Health Check or Playwright?}
B -- Yes --> C[Bypass Gateway] --> Route[Route Handler Execution]
B -- No --> D[Generate/Extract X-Request-ID]
D --> E[Log Request Metadata]
E --> F[Apply CORS & Security Headers]
F --> G{Is OPTIONS request?}
G -- Yes --> H[Return 204 No Content]
G -- No --> I{Body Size within limits?}
I -- No --> J[Return 413 Payload Too Large]
I -- Yes --> K{Rate Limit checked?}
K -- Exceeded --> L[Return 429 Too Many Requests]
K -- Under Limit --> M[Attach Rate Limit Headers]
M --> Route
Route --> N[Log Response Status & Duration]
N --> O[Send Response with X-Request-ID header]
- Test & Health Bypasses:
- Playwright test headers (in non-production environments) and health endpoints (
/api/health/*) bypass rate limits and size validation to ensure uptime check reliability.
- Playwright test headers (in non-production environments) and health endpoints (
- Correlation ID Generation:
- Generates or normalizes the correlation ID (
X-Request-ID) usinggenerateRequestIdin requestId.ts for logging traceability.
- Generates or normalizes the correlation ID (
- Structured Request Logging:
- Logs basic HTTP request details (method, path, user agent) but never logs request bodies to avoid credential leakage.
- CORS & Security Headers:
- Configures headers for Allowed Origins (based on the
ALLOWED_ORIGINSenv var), allows credentials, and enforces security headers:Content-Security-Policy: strictdefault-src 'none'setupStrict-Transport-Security: forces HTTPSX-Content-Type-Options:nosniffX-Frame-Options:DENYX-XSS-Protection:1; mode=block
- Configures headers for Allowed Origins (based on the
- Body Size Validation:
- Rejects requests exceeding
API_MAX_BODY_SIZE(default 1MB) with a413 Payload Too Largeerror.
- Rejects requests exceeding
- Tiered Rate Limiting:
- Evaluates rate limit buckets and responds with a
429 Too Many Requests(including standardRetry-Afterand reset headers) if thresholds are breached.
- Evaluates rate limit buckets and responds with a
RemitWise implements an in-memory sliding window rate limiter backed by an LRUCache. Clients are identified by their IP address (extracted from X-Forwarded-For or X-Real-IP).
| Tier | Limit | Scope / Match Rule | Purpose |
|---|---|---|---|
| Auth | 10 requests / min | Routes starting with /api/auth/ |
Protects authentication/session endpoints against brute force attacks. |
| Write | 50 requests / min | POST, PUT, DELETE, PATCH methods (non-auth) |
Prevents database exhaustion and contract transaction spamming. |
| General | 100 requests / min | GET requests and all other routes |
Handles standard read operations and public page views. |
Logging in RemitWise is structured and outputs JSON format, allowing easy indexing by logging aggregators.
- Enforced by
LOG_LEVELenvironment variable (debug,info,warn,error). - Default level is
info.
To prevent PII (Personally Identifiable Information) and credential leakage into logs, RemitWise enforces strict sanitization in sanitize.ts:
SENSITIVE_FIELDS(Redacted completely): Fields likepassword,secret,token,apikey,private_key,session_id,authorization,credit_card, andpinare fully replaced with[REDACTED].PARTIAL_MASK_FIELDS(Partially masked):- Emails:
user@example.combecomesus***@*** - Stellar public keys & addresses:
GBBD47...becomesGBXXXX***(viasanitizeWalletAddress) - Phone numbers:
+1234567890becomes+123***7890
- Emails:
Important
Never log raw request/response bodies without sanitizing.
Always pass payloads through sanitizeObject(data) before logging them.
Prefer logging specific identifiers rather than complete objects where possible.
For tasks that run out-of-band (e.g. processing webhooks, processing retries), RemitWise provides an in-process, drain-safe runtime in runtime.ts.
runBackgroundJob(jobName, fn): Registers an asynchronous callback with the background runtime. It tracks the running Promise in an active job set.- Graceful Shutdown Hooks:
Listens for
SIGTERMandSIGINTsignals, sets the system status toisShuttingDown() === true(blocking new background jobs), triggers registered hooks, and waits up toSHUTDOWN_TIMEOUT_MS(default 15 seconds) for in-flight jobs to drain before terminating.
The Anchor Webhook endpoint at route.ts demonstrates background job patterns:
- Shutdown Check:
- The route handler immediately rejects new webhooks with a
503 Service Unavailableif the server is shutting down.
- The route handler immediately rejects new webhooks with a
- Fast Response:
- It validates the signature, writes the raw event payload to the database, and schedules the processing logic in the background.
- Background Delegation:
- Delegation allows the API to acknowledge receipt with
200 OKin milliseconds while executing the handler asynchronously. - Example:
import { runBackgroundJob } from '@/lib/background/runtime'; export async function POST(request: NextRequest) { // ... validate signature and save event ... runBackgroundJob('anchor_webhook_event', async () => { await processWebhookEvent(eventId, handleAnchorEvent); }).catch(console.error); return NextResponse.json({ received: true, eventId }, { status: 200 }); }
- Delegation allows the API to acknowledge receipt with