Problem
The relay currently has no connection limits or rate limiting. On mobile devices with limited resources, this can lead to:
- Resource exhaustion: Too many concurrent WebSocket connections
- Memory overflow: Each connection consumes memory for subscriber maps
- Battery drain: Processing messages from unlimited clients
- DoS vulnerability: Malicious clients can overwhelm the relay
Mobile-Specific Concerns
Mobile devices running fonstr in Termux have:
- Limited RAM: Typically 2-8GB shared with other apps
- CPU constraints: ARM processors with thermal throttling
- Battery limitations: Excessive processing drains battery quickly
- Network limits: Mobile data connections may be metered
Proposed Solutions
Option 1: Basic connection limits
const MAX_CONNECTIONS = parseInt(process.env.MAX_CONNECTIONS) || 50
const connections = new Set()
fastify.register(async function (fastify) {
fastify.get('/', { websocket: true }, async (con, req) => {
// Check connection limit
if (connections.size >= MAX_CONNECTIONS) {
con.socket.close(1013, 'Too many connections')
return
}
connections.add(con.socket)
con.socket.on('close', () => {
connections.delete(con.socket)
subscribers.delete(con.socket)
})
})
})
Option 2: Per-IP connection limits
const connectionsByIP = new Map()
const MAX_CONNECTIONS_PER_IP = parseInt(process.env.MAX_CONNECTIONS_PER_IP) || 5
// In websocket handler
const clientIP = req.ip || req.socket.remoteAddress
const ipConnections = connectionsByIP.get(clientIP) || 0
if (ipConnections >= MAX_CONNECTIONS_PER_IP) {
con.socket.close(1013, 'Too many connections from IP')
return
}
Option 3: Rate limiting
const rateLimits = new Map() // IP -> { count, resetTime }
const RATE_LIMIT_WINDOW = 60000 // 1 minute
const RATE_LIMIT_MAX = parseInt(process.env.RATE_LIMIT_MAX) || 100
function checkRateLimit(ip) {
const now = Date.now()
const limit = rateLimits.get(ip)
if (!limit || now > limit.resetTime) {
rateLimits.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW })
return true
}
if (limit.count >= RATE_LIMIT_MAX) {
return false
}
limit.count++
return true
}
Option 4: Resource-based adaptive limits
function getAdaptiveLimit() {
const memUsage = process.memoryUsage()
const heapUsed = memUsage.heapUsed / 1024 / 1024 // MB
// Reduce connection limit if memory usage is high
if (heapUsed > 100) return Math.max(10, MAX_CONNECTIONS * 0.5)
if (heapUsed > 50) return Math.max(25, MAX_CONNECTIONS * 0.75)
return MAX_CONNECTIONS
}
Implementation Strategy
Phase 1: Basic limits
- Global connection limit (default: 50)
- Per-IP connection limit (default: 5)
- Clean up disconnected sockets
Phase 2: Rate limiting
- Message rate limiting per IP
- Event submission rate limiting
- REQ query rate limiting
Phase 3: Adaptive limits
- Memory-based connection adjustment
- CPU usage monitoring
- Battery level consideration (if available)
Configuration Options
# Environment variables
MAX_CONNECTIONS=50 # Total connections
MAX_CONNECTIONS_PER_IP=5 # Per IP limit
RATE_LIMIT_MAX=100 # Messages per minute per IP
RATE_LIMIT_WINDOW=60000 # Rate limit window (ms)
ENABLE_RATE_LIMITING=true # Enable rate limiting
Mobile-Optimized Defaults
const MOBILE_DEFAULTS = {
MAX_CONNECTIONS: 25,
MAX_CONNECTIONS_PER_IP: 3,
RATE_LIMIT_MAX: 50,
ENABLE_ADAPTIVE_LIMITS: true
}
Benefits for Mobile
- Resource protection: Prevents memory/CPU exhaustion
- Battery preservation: Limits processing overhead
- Stability: Prevents relay crashes from resource exhaustion
- DoS protection: Basic protection against abuse
- Predictable performance: Consistent response times
Implementation Considerations
Memory management:
- Clean up tracking maps when connections close
- Periodic cleanup of stale rate limit entries
- Monitor memory usage for adaptive limits
Backwards compatibility:
- Limits disabled by default (for now)
- Graceful connection rejection
- Informative close codes/messages
Mobile detection:
const isMobile = process.env.MOBILE_MODE === 'true' ||
process.platform === 'android' ||
process.env.TERMUX_VERSION !== undefined
Error Handling
- WebSocket close codes for different rejection reasons
- Clear error messages in NOTICE events
- Logging of connection rejections (if logging enabled)
Testing Requirements
- Verify connection limits work correctly
- Test rate limiting accuracy
- Validate memory cleanup on disconnect
- Test adaptive limits under load
- Ensure no memory leaks from tracking maps
This feature would make fonstr much more robust for mobile deployment and protect against resource exhaustion.
Problem
The relay currently has no connection limits or rate limiting. On mobile devices with limited resources, this can lead to:
Mobile-Specific Concerns
Mobile devices running fonstr in Termux have:
Proposed Solutions
Option 1: Basic connection limits
Option 2: Per-IP connection limits
Option 3: Rate limiting
Option 4: Resource-based adaptive limits
Implementation Strategy
Phase 1: Basic limits
Phase 2: Rate limiting
Phase 3: Adaptive limits
Configuration Options
Mobile-Optimized Defaults
Benefits for Mobile
Implementation Considerations
Memory management:
Backwards compatibility:
Mobile detection:
Error Handling
Testing Requirements
This feature would make fonstr much more robust for mobile deployment and protect against resource exhaustion.