Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
90 changes: 90 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.0.0] - 2026-05-28

### Added

#### Core Platform
- Cross-border remittance platform built on the Stellar blockchain
- Stellar account creation, funding (testnet), and import via secret key
- XLM and non-XLM asset balance retrieval
- Trustline management (add/remove/list)
- Payment sending with optional fee-bump sponsorship for low-balance accounts
- Path payment support for cross-asset conversions
- Exchange rate service with multi-currency conversion
- AMM (Automated Market Maker) integration

#### Authentication & Security
- JWT-based authentication with refresh token rotation
- Password hashing with bcrypt
- TOTP-based multi-factor authentication (MFA) with AES-256-GCM encrypted secrets
- Google OAuth2 login
- CSRF protection middleware
- Input sanitization and validation via express-validator and Zod
- Security headers middleware (Helmet-style)
- Rate limiting per endpoint with configurable windows and whitelists

#### Database
- PostgreSQL via Prisma ORM with `@prisma/adapter-pg` driver adapter
- PgBouncer transaction-pooling support
- Database sharding across multiple PostgreSQL instances
- Soft-delete middleware for User, Transaction, and related models
- Configurable connection pool (`DB_POOL_MAX`) and query timeout (`DB_QUERY_TIMEOUT_MS`)
- Automated migrations on startup

#### Payment Streaming
- Real-time payment streams with per-stream sender secret encryption (AES-256-GCM)
- WebSocket push for live balance and stream updates

#### Notifications
- Web Push notifications (VAPID)
- Email notifications via SMTP (stubbed when unconfigured)
- SMS notifications via Twilio (stubbed when unconfigured)
- Per-user notification preferences

#### Compliance & KYC
- KYC record collection and status tracking
- AML monitoring and alert generation
- Sanctions screening
- Risk scoring
- Compliance audit logging and reporting
- Identity verification workflow

#### Analytics & Monitoring
- Request-level performance middleware with configurable alert thresholds
- OpenTelemetry distributed tracing (OTLP HTTP export)
- Winston structured logging with daily log rotation
- Prometheus-compatible metrics endpoint
- User behaviour analytics and fraud detection
- Event sourcing with projection manager, archiver, and replayer

#### Caching
- Multi-level cache: in-memory L1 + optional Redis L2
- Per-route cache middleware with configurable TTLs
- Cache invalidation on balance-changing operations
- Cache analytics, monitoring, and warming utilities

#### Infrastructure
- Docker development environment (`Dockerfile.dev`)
- CDN middleware with multi-region edge support
- Microservices gateway, service mesh, and discovery utilities
- Chaos engineering toolkit (failure injection, network partition simulation, blast-radius limiter)
- Load testing framework with k6 scenarios and bottleneck analysis
- Backup and recovery manager with optional AES encryption and configurable retention
- Scheduled jobs via internal scheduler

#### Developer Experience
- OpenAPI / Swagger documentation (`/api-docs`)
- Vitest test suite (unit, integration, performance, property-based, contract)
- Stryker mutation testing
- Prettier code formatting

[Unreleased]: https://github.com/Ethereal-Future/FuTuRe/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/Ethereal-Future/FuTuRe/releases/tag/v1.0.0
156 changes: 12 additions & 144 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,19 @@ FRONTEND_BASE_URL=http://localhost:3000
# CONFIG_ENCRYPTION_KEY=your-strong-key

PLATFORM_SECRET_KEY=
JWT_SECRET=
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
LOG_LEVEL=info
DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance

# Database
# connection_limit: max connections Prisma holds open
# Recommended: 10 (dev), 25-50 (prod). Rule of thumb: (num_cores * 2) + 1
# pool_timeout: seconds to wait for a free connection before throwing
# Recommended: 10 (dev/prod)
DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance?connection_limit=10&pool_timeout=10

# Optional: PgBouncer pooler URL (transaction pooling mode).
# When set, Prisma connects through the pooler and prepared statements are
# disabled automatically (?pgbouncer=true is appended if not already present).
# DATABASE_POOL_URL=postgresql://user:password@pgbouncer:6432/future_remittance
# DATABASE_POOL_URL=postgresql://user:password@pgbouncer:6432/future_remittance?connection_limit=25&pool_timeout=10

# Set to true to enable Prisma query logging outside of development mode.
# In APP_ENV=development, query logging is always on.
Expand All @@ -75,8 +79,8 @@ DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance
# Number of shards (default: 1 = no sharding, uses DATABASE_URL)
DB_SHARD_COUNT=1
# Per-shard connection URLs (DB_SHARD_0_URL falls back to DATABASE_URL)
# DB_SHARD_0_URL=postgresql://user:password@shard0:5432/future_remittance
# DB_SHARD_1_URL=postgresql://user:password@shard1:5432/future_remittance
# DB_SHARD_0_URL=postgresql://user:password@shard0:5432/future_remittance?connection_limit=25&pool_timeout=10
# DB_SHARD_1_URL=postgresql://user:password@shard1:5432/future_remittance?connection_limit=25&pool_timeout=10
# Max connections per shard pool
DB_POOL_MAX=10

Expand All @@ -100,7 +104,6 @@ PERF_ALERT_ERROR_RATE=0.1
# NEW_RELIC_LICENSE_KEY=
# Optional: DataDog API key (set to enable DataDog APM)
# DD_API_KEY=
LOG_LEVEL=info

# Email notifications (optional — stub used if not set)
# EMAIL_HOST=smtp.example.com
Expand All @@ -113,12 +116,14 @@ LOG_LEVEL=info
# TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# TWILIO_AUTH_TOKEN=your-auth-token
# TWILIO_FROM_NUMBER=+10000000000

# Backup & Recovery
BACKUP_DIR=./backups
# 64-char hex key (openssl rand -hex 32) — required for encrypted backups
# BACKUP_ENC_KEY=
BACKUP_RETENTION_DAYS=7
BACKUP_INTERVAL_HOURS=24

# Redis (optional — falls back to in-memory L1 cache if not set)
# REDIS_URL=redis://localhost:6379

Expand All @@ -145,140 +150,3 @@ CACHE_TTL_FEE_S=120
# ALERT_EMAIL=ops@example.com
# Slack webhook URL for notifications
# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
# Backend environment configuration (example)
#
# Copy to `.env` and adjust as needed:
# cp .env.example .env

# App environment: development | test | production
APP_ENV=development
CONFIG_VERSION=1

# Enable hot-reloading when `.env*` files change (ignored in APP_ENV=test)
CONFIG_WATCH=false

# Server
PORT=3001
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173

# Stellar
STELLAR_NETWORK=testnet
HORIZON_URL=https://horizon-testnet.stellar.org

# Required when sending non-XLM assets
ASSET_ISSUER=

# Fee Bump Transactions
# Platform account secret key used to sponsor transaction fees for buyers with low XLM balance
# When set, payments from accounts below FEE_BUMP_THRESHOLD_XLM will be wrapped in a fee bump
PLATFORM_FEE_ACCOUNT_SECRET=
# XLM balance threshold below which fee bumping is applied (default: 2)
FEE_BUMP_THRESHOLD_XLM=2

# Payment Streaming
# AES-256-GCM key used to encrypt per-stream sender secrets at rest (required)
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
STREAM_SECRET_ENCRYPTION_KEY=

# Security
JWT_SECRET=change-me

# MFA (TOTP) Encryption Key
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
MFA_ENCRYPTION_KEY=

# OAuth2 Configuration
# Google OAuth2 credentials (get from https://console.cloud.google.com)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# Server URLs for OAuth callbacks
SERVER_BASE_URL=http://localhost:3001
FRONTEND_BASE_URL=http://localhost:3000

# WebSocket
# HMAC secret for signing outbound WebSocket message envelopes
# WS_MSG_SECRET=your-strong-random-secret

# Optional: decrypt ENC(...) values
# CONFIG_ENCRYPTION_KEY=your-strong-key

PLATFORM_SECRET_KEY=
JWT_SECRET=
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
LOG_LEVEL=info
DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance

# Optional: PgBouncer pooler URL (transaction pooling mode).
# When set, Prisma connects through the pooler and prepared statements are
# disabled automatically (?pgbouncer=true is appended if not already present).
# DATABASE_POOL_URL=postgresql://user:password@pgbouncer:6432/future_remittance

# Set to true to enable Prisma query logging outside of development mode.
# In APP_ENV=development, query logging is always on.
# PRISMA_QUERY_LOG=true

# Database Sharding
# Number of shards (default: 1 = no sharding, uses DATABASE_URL)
DB_SHARD_COUNT=1
# Per-shard connection URLs (DB_SHARD_0_URL falls back to DATABASE_URL)
# DB_SHARD_0_URL=postgresql://user:password@shard0:5432/future_remittance
# DB_SHARD_1_URL=postgresql://user:password@shard1:5432/future_remittance
# Max connections per shard pool
DB_POOL_MAX=10

# Database query timeout (milliseconds)
# Applied as PostgreSQL statement_timeout on each connection AND as a Node.js
# Promise.race guard on every Prisma operation. Prevents slow/hung queries from
# holding pool connections indefinitely. Default: 5000 (5 s).
DB_QUERY_TIMEOUT_MS=5000

RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=100
RATE_LIMIT_MESSAGE="Too many requests, please try again later"
RATE_LIMIT_WHITELIST=192.168.1.1,10.0.0.0/8

# Performance Monitoring
# Alert when any API response exceeds this threshold (ms)
PERF_ALERT_RESPONSE_MS=2000
# Alert when error rate exceeds this fraction (0.1 = 10%)
PERF_ALERT_ERROR_RATE=0.1
# Optional: New Relic license key (set to enable New Relic APM)
# NEW_RELIC_LICENSE_KEY=
# Optional: DataDog API key (set to enable DataDog APM)
# DD_API_KEY=
LOG_LEVEL=info

# Email notifications (optional — stub used if not set)
# EMAIL_HOST=smtp.example.com
# EMAIL_PORT=587
# EMAIL_USER=notifications@example.com
# EMAIL_PASS=your-smtp-password
# EMAIL_FROM=noreply@futureremit.app

# SMS notifications via Twilio (optional — stub used if not set)
# TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# TWILIO_AUTH_TOKEN=your-auth-token
# TWILIO_FROM_NUMBER=+10000000000
# Backup & Recovery
BACKUP_DIR=./backups
# 64-char hex key (openssl rand -hex 32) — required for encrypted backups
# BACKUP_ENC_KEY=
BACKUP_RETENTION_DAYS=7
BACKUP_INTERVAL_HOURS=24
# Redis (optional — falls back to in-memory L1 cache if not set)
# REDIS_URL=redis://localhost:6379

# CDN Configuration
CDN_ENABLED=false
# CDN_URL=https://cdn.example.com
# CDN_SECONDARY_URL=https://cdn2.example.com
# Comma-separated list of CDN edge regions
CDN_REGIONS=us-east-1,eu-west-1,ap-southeast-1
# Max-age for immutable static assets (seconds, default 86400 = 1 day)
CDN_CACHE_MAX_AGE_S=86400

# Cache TTLs (seconds)
CACHE_TTL_BALANCE_S=30
RATE_CACHE_TTL_S=60
CACHE_TTL_FEE_S=120
2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"start": "node src/server.js",
"test": "vitest run",
"format": "prettier --write \"src/**/*.js\"",
"lint": "eslint src/**/*.js",
"test:performance": "vitest run tests/performance.benchmark.test.js",
"load-test:regression": "k6 run load-tests/k6/scenarios/performance-regression.js",
"load-test:endpoints": "k6 run load-tests/k6/scenarios/api-endpoints.js",
Expand Down Expand Up @@ -47,6 +48,7 @@
"zod": "^3.24.4"
},
"devDependencies": {
"eslint": "^9.28.0",
"prettier": "^3.3.3"
}
}
11 changes: 11 additions & 0 deletions backend/src/routes/stellar/accounts.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import crypto from 'crypto';
import express from 'express';
import { body } from 'express-validator';
import * as StellarSDK from '@stellar/stellar-sdk';
Expand Down Expand Up @@ -64,6 +65,16 @@ router.post('/import', rules.importAccount, validate, async (req, res) => {
router.get('/:publicKey', rules.publicKeyParam, validate, async (req, res) => {
try {
const balance = await StellarService.getBalance(req.params.publicKey);

// ETag based on a hash of the balance payload (issue #356)
const etag = `"${crypto.createHash('sha256').update(JSON.stringify(balance)).digest('hex').slice(0, 16)}"`;
res.setHeader('ETag', etag);
res.setHeader('Cache-Control', 'no-cache');

if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}

res.json(balance);
} catch (error) {
logError(req, error, { publicKey: req.params.publicKey });
Expand Down
29 changes: 29 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import js from '@eslint/js';

export default [
js.configs.recommended,
{
files: ['backend/src/**/*.js'],
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
globals: {
process: 'readonly',
console: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
Buffer: 'readonly',
URL: 'readonly',
},
},
rules: {
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'no-console': 'off',
},
},
{
ignores: ['**/node_modules/**', '**/dist/**', '**/build/**'],
},
];
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"frontend"
],
"scripts": {
"prepare": "husky",
"dev": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
"dev:backend": "npm run dev --workspace=backend",
"dev:frontend": "npm run dev --workspace=frontend",
Expand Down Expand Up @@ -37,7 +38,20 @@
"test:contracts:registry": "vitest run --config vitest.contracts.config.js contracts/registry.test.js",
"test:contracts": "npm run test:contracts:consumer && npm run test:contracts:provider && npm run test:contracts:registry"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{json,md,yml,yaml}": [
"prettier --write"
]
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"eslint": "^9.28.0",
"husky": "^9.1.7",
"lint-staged": "^15.5.2",
"@pact-foundation/pact": "^16.3.0",
"@stryker-mutator/core": "^9.6.0",
"@stryker-mutator/vitest-runner": "^9.6.0",
Expand Down
Loading