Zelta/Documentation

Rate Limits

Zelta implements rate limiting at multiple layers to protect the platform and ensure fair usage.

Overview

Rate limits are applied per device (or per IP for unauthenticated endpoints) using a sliding window algorithm. This prevents any single device from overwhelming the system.

Edge Function Limits

Each API endpoint has specific rate limits:

| Endpoint | Limit | Window | Identifier | |----------|-------|--------|------------| | /check-update | 10 | 1 minute | Device ID | | /report-status | 20 | 1 minute | Device ID | | /provision-device | 5 | 1 minute | IP Address | | /device-heartbeat | 5 | 1 minute | Device ID | | /log-events | 60 | 1 minute | Device ID | | /device-command | 20 | 1 minute | Device ID |

Internal/Cron Endpoints

These endpoints are called by scheduled jobs:

| Endpoint | Limit | Window | Notes | |----------|-------|--------|-------| | /process-rollouts | 5 | 1 minute | Internal cron | | /process-scheduled-updates | 5 | 1 minute | Internal cron | | /report-usage | 10 | 1 minute | Stripe billing |

MQTT Broker Limits

The MQTT bridge also enforces rate limits:

Per-Device Limits

| Topic Pattern | Limit | Window | |---------------|-------|--------| | heartbeat | 2 | 1 minute | | status | 10 | 1 minute | | logs | 20 | 1 minute | | check | 6 | 1 minute | | download | 120 | 1 minute | | provision | 3 | 1 minute |

Broker-Level Limits

max_queued_messages: 100
max_inflight_messages: 20
max_inflight_bytes: 65536
max_queued_bytes: 262144

Rate Limit Response

When a request exceeds the rate limit, the API returns:

HTTP Status: 429 Too Many Requests

Response Body:

{
  "error": "Rate limit exceeded",
  "message": "Too many requests. Limit: 10/min",
  "limit": 10,
  "current": 11,
  "remaining": 0,
  "retry_after": "2025-01-15T10:31:00Z"
}

Response Headers: | Header | Description | |--------|-------------| | X-RateLimit-Limit | Maximum requests allowed per window | | X-RateLimit-Remaining | Requests remaining in current window | | X-RateLimit-Reset | ISO 8601 timestamp when window resets | | Retry-After | Seconds until requests are allowed again |

Device-Side Handling

Your embedded code should handle rate limits gracefully:

// Example: Exponential backoff on rate limit
int retry_delay_ms = 1000;

while (true) {
    int status = zelta_check_for_update(callback, NULL);
    
    if (status == ZELTA_RATE_LIMITED) {
        LOG_WRN("Rate limited, retrying in %d ms", retry_delay_ms);
        k_sleep(K_MSEC(retry_delay_ms));
        retry_delay_ms = MIN(retry_delay_ms * 2, 60000);  // Max 60s
    } else {
        retry_delay_ms = 1000;  // Reset on success
        break;
    }
}

Abuse Detection

Devices that repeatedly exceed rate limits are automatically flagged:

How It Works

  1. Each rate limit violation increments a counter on the device record
  2. After 10 violations within 1 hour, the device is flagged as "rate limit abuse"
  3. Flagged devices appear with an orange warning icon (⚠️) in the dashboard
  4. A log entry is created for each violation

Viewing Flagged Devices

  1. Go to Devices in the dashboard
  2. Look for the "Rate Limited" stat card showing the count
  3. Flagged devices show an orange warning icon
  4. Hover over the icon to see the hit count

Clearing the Flag

Admins can clear the rate limit flag:

  1. Open the device dropdown menu (⋮)
  2. Click "Clear Rate Limit Flag"
  3. The flag and hit counter are reset

Database Fields

The following fields track rate limit abuse on devices:

| Field | Type | Description | |-------|------|-------------| | rate_limit_hits | integer | Total violations | | last_rate_limited_at | timestamp | Most recent violation | | rate_limit_abuse_flag | boolean | True if flagged |

Best Practices

For Firmware Developers

  1. Use appropriate intervals - Don't check for updates every second
  2. Implement backoff - Increase delay after rate limit errors
  3. Cache responses - Store update check results locally
  4. Batch operations - Combine multiple log entries into one request

Recommended Check Intervals

| Operation | Recommended Interval | |-----------|---------------------| | Update check | Every 1-4 hours | | Heartbeat | Every 60 seconds | | Status report | On state change only | | Log upload | Batch every 5 minutes |

For Dashboard Users

  1. Monitor flagged devices - May indicate misconfigured firmware
  2. Review logs - Check which endpoint is being hit
  3. Update firmware - Fix aggressive polling in device code
  4. Contact support - If legitimate use case needs higher limits

Customizing Limits

Rate limits are configured in the database and can be adjusted:

Default Configuration

Located in cloud/functions/_shared/rate-limit.ts:

export const RATE_LIMITS = {
  'check-update':     { limit: 10, windowSeconds: 60 },
  'device-heartbeat': { limit: 5, windowSeconds: 60 },
  'report-status':    { limit: 20, windowSeconds: 60 },
  'log-events':       { limit: 60, windowSeconds: 60 },
  'device-command':   { limit: 20, windowSeconds: 60 },
  'provision-device': { limit: 5, windowSeconds: 60 },
  // ...
}

Enterprise Customization

Enterprise customers can request custom rate limits. Contact support with:

  • Your organization ID
  • Which endpoints need adjustment
  • Expected request patterns
  • Use case justification

Monitoring

Logs

Rate limit events are logged to:

  • Supabase Edge Function logs
  • Device logs table (for violations)
  • MQTT bridge logs (for MQTT limits)

Metrics

Track rate limiting via:

  • Dashboard "Rate Limited" device count
  • Device detail page showing violation history
  • Supabase dashboard → Edge Functions → Logs

Related Documentation