Stellar Insights is an analytics and preflight API for Stellar assets, with a Rust backend, a Next.js frontend, and TypeScript/Python SDKs.
git clone https://github.com/Ndifreke000/stellar-insights.git
cd stellar-insights
# backend (Rust, SQLite created on first start)
cd backend && cp .env.example .env && cargo run
cargo test
# frontend
cd ../frontend && npm install && npm run dev
# SDKs
cd ../sdk/typescript && npm install && npm test
cd ../python && pip install -e .[dev] && pytestTry the core endpoint: curl -s "http://localhost:8080/api/v1/preflight?source_asset=USDC&destination_asset=NGN&amount_usd=2500" (port per your .env).
All API routes are protected by a single rate limiting middleware. Every response — including successful 200 responses and rate-limited 429 responses — carries the standard IETF RateLimit-* headers so SDKs and clients can implement backoff without parsing error bodies:
| Header | Description |
|---|---|
RateLimit-Limit |
Maximum number of requests allowed in the current window. |
RateLimit-Remaining |
Number of requests remaining in the current window. |
RateLimit-Reset |
Seconds until the current window resets. |
When a request is rate limited, the response is a 429 Too Many Requests with the same RateLimit-* headers (with RateLimit-Remaining: 0) plus a Retry-After header indicating how long the client should wait before retrying.
Example:
curl -i "http://localhost:8080/api/v1/preflight?source_asset=USDC&destination_asset=NGN&amount_usd=2500"
# HTTP/1.1 200 OK
# RateLimit-Limit: 60
# RateLimit-Remaining: 59
# RateLimit-Reset: 42A bare CODE (e.g. USDC) is still accepted and resolves to the corridor for
that code; when several issuers exist for the same code, use CODE:ISSUER to
select the one you mean. Malformed asset strings are rejected at the boundary
with the standard error response.
Pro access is time-limited. To avoid silent expiry — where an integrator's production traffic suddenly drops to free-tier limits — the backend sends renewal reminders and keeps a short grace period after expiry.
- Reminders. A scheduler notifies the account owner (email and/or webhook) 7 days and 1 day before the Pro subscription expires. Each reminder is sent at most once per window, so restarts do not duplicate notifications.
- Grace period. After the expiry timestamp passes, Pro entitlements remain active for a configurable grace window (default 3 days) so a late renewal does not interrupt production. Once the grace period ends, the account falls back to free-tier limits.
Both are configured via environment variables:
| Env var | Default | Meaning |
|---|---|---|
SUBSCRIPTION_REMINDER_DAYS |
7,1 |
Comma-separated days-before-expiry at which reminders fire |
SUBSCRIPTION_GRACE_PERIOD_DAYS |
3 |
Days Pro stays active after expiry before falling back to free tier |
Reminders are delivered through the same notification channel used for other account events (email when configured, otherwise the account's webhook).
The dashboard signs wallets in with SEP-10.
The home domain is server-authoritative: the frontend fetches it from the
backend config endpoint (GET /api/v1/auth/config) and passes it to the SEP-10
challenge request. The client no longer derives the home domain from
window.location.hostname, so sign-in works on preview deployments and
localhost, and a client cannot pick the domain that is validated. The backend
ignores any client-supplied home domain and always uses its configured value
(SEP10_HOME_DOMAIN).
SQLite only. This is a compile-time property, not a configuration one:
sqlx is built with only the sqlite feature, backend/src/database.rs uses
SqlitePool/SqliteConnectOptions directly, and every migration is written in
SQLite-flavoured SQL.
Setting a postgresql:// DATABASE_URL does not switch databases — it
fails to parse as SqliteConnectOptions and the backend refuses to start.
Adding Postgres support is a code change. Whether to make it is being tracked
in issue #1876.
The backend integrates with payraider via SNAPSHOT_CONTRACT_ID.
contracts/.env.testnet is generated by
deploy-contracts-testnet.sh — prefer reading it over copying IDs from here.
| Contract | Env var | Testnet ID (historical) |
|---|---|---|
| PayRaider | PAYRAIDER_CONTRACT_ID / SNAPSHOT_CONTRACT_ID |
CAPHQZ4BBT43HU5EUSJAOPKWB66HGLTN4AKJUALV3R2RXS4A6IOXWUTL |
All other contract crates live under `c
/* … truncated 1537 chars — edit only what you need near the top … */