Tier Model & Resource Limits
skyflow beginner 4 min read
ELI5
Skyflow has four tiers like airline seating: Drift (free, basic), Lift ($5), Jet ($9), and Orbit ($25, unlimited). Each tier upgrades how many timelines you can analyse per hour and how much XP each analysis earns. The tier lives on the user row and is enforced by middleware before the endpoint runs.
Technical Deep Dive
Enum Definitions
The tier enum is declared in two places and they MUST stay aligned:
| Source | Type |
|---|---|
proto/skyflow/common/v1/types.proto | enum Tier { TIER_UNSPECIFIED, TIER_DRIFT, TIER_LIFT, TIER_JET, TIER_ORBIT } |
database/postgres/001_core_schema.sql | CREATE TYPE tier AS ENUM ('drift','lift','jet','orbit') |
users.tier defaults to 'drift' on insert.
TierLimits Message
classDiagram class TierLimits { +Tier tier +int32 requests_per_hour +int32 max_concurrent +int32 posts_analyzed +int32 audience_results +int32 api_calls_monthly +bool real_time_updates +bool api_access } class Tier { <<enum>> DRIFT LIFT JET ORBIT } TierLimits --> TierDocumented Per-Tier Quotas
From services/01-core-api.md § 4.3:
| Tier | Timeline analyses / hour | XP multiplier | Streak freezes |
|---|---|---|---|
| DRIFT | 5 | 1.0× | 0 |
| LIFT | 20 | 1.5× | 0 |
| JET | 50 | 2.0× | 3 / month |
| ORBIT | unlimited | 3.0× | 3 / month |
XP multipliers are stored in xp_transactions.xp_multiplier as integer hundredths (100 = 1.0×, 300 = 3.0×).
Enforcement Path
flowchart TD R[Request] --> JWT[JWTAuth middleware] JWT --> T[RequireTier or rate limiter] T -->|tier OK & under quota| H[Handler] T -->|tier too low| F1[403 upgrade_required] T -->|over quota| F2[429 rate_limit_exceeded]The RequireTier(minTier) and RateLimit() Fiber middlewares both look up the tier from c.Locals("user_id") → cached users.tier row; rate windows are stored as ratelimit:{user_id}:{path} Redis counters with 1-hour TTL.
Key Terms
- TIER_UNSPECIFIED → proto3 default zero value; never written to the DB, used as a “field absent” sentinel
- xp_multiplier (100 = 1.0×) → integer-encoded multiplier in the XP ledger to avoid float drift
- Streak freeze → JET+ feature granting 3 missed-day passes per calendar month (see skyflow-011)
- Real-time updates flag →
TierLimits.real_time_updatesgates SSE access for low-tier users
Q&A
Q: What tier is created when a user signs up?
A: TIER_DRIFT. The DB column has DEFAULT 'drift' and UserRegisteredEvent.tier carries that value through the event chain.
Q: How is the rate limit reset window implemented?
A: A Redis counter keyed ratelimit:{user_id}:{path} is INCR’d on every request; on the first hit EXPIRE is set to 1 hour. There is no rolling window — it’s a fixed bucket.
Q: Where is the per-tier XP multiplier applied?
A: In the Gamification Service when writing the xp_transactions row — it stores the multiplier integer (100/150/200/300) so downstream sums stay exact.
Q: Can a user be on two tiers at once?
A: No. subscriptions has the partial unique constraint one_active_subscription_per_user UNIQUE (user_id) WHERE status = 'active'.
Examples
Think of tiers as gym memberships. DRIFT is the free trial — you can use a few machines a day. LIFT is the basic membership; JET is premium with three “skip-day” passes. ORBIT is the all-access founder pass: come whenever you want, lift triple the points.
neighbors on the map
- Billing Architecture debugging why a user's tier did not update after payment
- Tier-Based Rate Limiting debugging 429 errors for specific users
- In-Process Rate-Limit Bucket investigating ingest 429s