SDKs
Security
Payment verification, replay protection, rate limiting
Solvela operates in a zero-trust model: every request must prove payment before receiving service. The gateway never stores private keys, never trusts client-provided data without verification, and degrades safely when dependencies are unavailable.
Payment Verification
Transaction Verification Pipeline
Every PAYMENT-SIGNATURE header is validated through a multi-step pipeline:
- Header size limit: 50KB maximum to prevent DoS via oversized headers
- Decoding: base64 or raw JSON parsing
- Replay protection: Redis
SET NX EX 120or in-memory LRU fallback - Transaction deserialization: parse as Solana
VersionedTransaction - Instruction validation: verify
TransferCheckeddiscriminator - ATA derivation: compute expected Associated Token Accounts
- Amount validation: transferred amount >= quoted price
- Recipient validation: destination matches gateway wallet USDC ATA
- USDC mint validation: token mint matches configured USDC mint
Amount Bypass Prevention
The gateway computes costs using integer arithmetic on USDC atomic units (6 decimal places) to prevent floating-point rounding exploits. The compute_actual_atomic_cost function ensures the actual amount charged matches what was quoted.
Replay Protection
Transaction signatures are tracked to prevent double-spending:
- Redis (primary):
SET NX EX 120-- if the key already exists, the payment is rejected - In-memory LRU (fallback): bounded to 10,000 entries when Redis is unavailable
- The 120-second TTL exceeds Solana's ~60-second blockhash expiry window
Warning
When Redis is unavailable, replay protection uses an in-memory LRU cache. This is a degraded mode -- it cannot protect against replays across gateway restarts or multi-instance deployments. Always use Redis in production.
SSRF Prevention
The service marketplace validates endpoints against private network ranges:
- Registration time: endpoint hostname is resolved and checked against RFC 1918 / loopback / link-local ranges
- Proxy time: the target hostname is re-validated before forwarding
- Defense-in-depth: both layers must pass for a request to be proxied
Blocked ranges:
127.0.0.0/8(loopback)10.0.0.0/8(private)172.16.0.0/12(private)192.168.0.0/16(private)169.254.0.0/16(link-local)::1(IPv6 loopback)fc00::/7(IPv6 unique local)
Rate Limiting
Rate limiting is per-wallet (Solana pubkey), not per-IP:
- Extracts the wallet address from the payment header or request context
- Prevents spoofing via
X-Forwarded-For(ignored entirely) - Configurable limits per endpoint
- Cleanup cooldown prevents stale entries from consuming memory
When the rate limit is exceeded, the gateway returns 429 Too Many Requests with a Retry-After header.
Admin Endpoints
The following endpoints require Authorization: Bearer <SOLVELA_ADMIN_TOKEN>:
| Endpoint | Purpose |
|---|---|
GET /metrics | Prometheus metrics |
GET /v1/escrow/health | Escrow claim processor status |
POST /v1/services/register | Service marketplace registration |
Token comparison uses constant-time equality (subtle::ConstantTimeEq equivalent) to prevent timing attacks.
When SOLVELA_ADMIN_TOKEN is not set, the metrics endpoint is hidden and returns 404 Not Found (rather than advertising its existence). Org-management endpoints that need an admin token return 503 Service Unavailable with "admin endpoint not configured". A request that supplies a wrong/missing token to a configured admin endpoint returns 401 Unauthorized.
CORS
CORS is configured restrictively:
- Development (
SOLVELA_ENV != "production"): allowslocalhost:3000,localhost:8080,127.0.0.1:3000 - Production (
SOLVELA_ENV=production): only origins listed inSOLVELA_CORS_ORIGINS - Allowed methods: GET, POST, PUT, PATCH, DELETE, OPTIONS (the enterprise org/team/budget endpoints use PUT and DELETE)
- Allowed headers:
Content-Type,Authorization,PAYMENT-SIGNATURE,X-Request-Id,X-Session-Id,X-Solvela-Debug(canonical) plus legacyX-RCR-Debugfor pre-rebrand clients
SDK and agent clients are unaffected by CORS since they do not run in browsers.
Secret Management
Rules
- All secrets come from environment variables, never config files
- API keys and fee payer keys are redacted in all
Debugoutput via customfmt::Debugimplementations - Fee payer private keys (
SolanaConfig.fee_payer_key,fee_payer_keys) show[REDACTED]in logs - Provider API keys (
ProvidersConfig) show[REDACTED]when set,Nonewhen absent
Key Rotation
Fee payer keys support rotation:
- Primary key:
SOLVELA_SOLANA__FEE_PAYER_KEY - Additional keys:
SOLVELA_SOLANA__FEE_PAYER_KEY_2through_8 - The
FeePayerPoolrotates across healthy keys automatically - Failed keys enter a 60-second cooldown before reuse
Security Headers
Every response includes:
| Header | Value | Purpose |
|---|---|---|
X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
X-Frame-Options | DENY | Prevents clickjacking |
Referrer-Policy | no-referrer | Prevents referrer leakage |
X-Solvela-Request-Id | UUID | Request correlation (always present). The legacy X-RCR-Request-Id mirror is also emitted for pre-rebrand clients. |
Prompt Guard
The gateway includes middleware for detecting:
- Prompt injection: patterns that attempt to override system prompts
- Jailbreak attempts: patterns that bypass safety filters
- PII exposure: personal information in prompts
Flagged requests are rejected with 400 before any payment is processed.
Input Validation
- Billed completion tokens are capped at
min(max_tokens, model max-output, 8192)— the 8192 default ceiling prevents unbounded cost when a request omitsmax_tokens - Session IDs: max 128 characters,
[a-zA-Z0-9\-_]only - Wallet addresses: base58 character set, 32-44 characters
- Service IDs: alphanumeric + hyphens only
- Service endpoints: HTTPS required, no HTTP
- Request body: 10MB limit (Tower
RequestBodyLimitLayer)