Quotas & Rate Limits
Per-tenant request rate limits, storage quotas, and plan-based enforcement in CoreSDK.
Phase note. Rate limiting and quota enforcement ships in Phase 2 (
coresdk-ratelimit). The API shown is the planned surface.
Quotas & Rate Limits
Phase 2. Per-tenant quotas, monthly request caps, storage quotas, and the quota middleware ship Phase 2.
CoreSDK enforces quotas at the tenant layer — request rate limits, storage caps, and seat counts — with built-in middleware and a quota events API.
Per-tenant rate limits
Rate limits are applied per tenant before any request reaches your application logic. Exceeding the limit returns 429 Too Many Requests with a Retry-After header.
use coresdk_engine::{CoreSDK, quota::{RateLimit, Window}};
let sdk = Engine::from_env()?
.rate_limit(RateLimit {
default_rpm: 1_000, // global default
window: Window::Sliding,
per_tenant: hashmap! {
"acme" => 10_000,
"beta-corp" => 5_000,
"free-tier" => 100,
},
})
.build()
.await?;sdk = SDK.from_env() # Phase 2 planned API
.rate_limit({
"default_rpm": 1_000,
"window": "sliding",
"per_tenant": {
"acme": 10_000,
"beta-corp": 5_000,
"free-tier": 100,
},
}) \
.build()sdk, err := coresdk.NewBuilder().
RateLimit(coresdk.RateLimit{
DefaultRPM: 1_000,
Window: coresdk.WindowSliding,
PerTenant: map[string]int{
"acme": 10_000,
"beta-corp": 5_000,
"free-tier": 100,
},
}).
Build(ctx)const sdk = new CoreSDK({ tenant: "acme-corp" }) // Phase 2 planned API
.rateLimit({
defaultRpm: 1_000,
window: "sliding",
perTenant: {
acme: 10_000,
"beta-corp": 5_000,
"free-tier": 100,
},
})
.build();Request quotas (monthly caps)
Monthly request quotas cut off access once a tenant exhausts their allocation. CoreSDK resets counters at the start of each billing cycle and emits a quota.exhausted event when the cap is hit.
use coresdk_engine::quota::{RequestQuota, QuotaExhaustedAction};
let sdk = Engine::from_env()?
.request_quota(RequestQuota {
// Plan-based defaults; per-tenant overrides win
free_monthly: 10_000,
starter_monthly: 100_000,
business_monthly: 2_000_000,
enterprise_monthly: None, // unlimited
on_exhausted: QuotaExhaustedAction::Block,
notify_at_pct: vec![80, 95, 100], // emit events at these thresholds
})
.build()
.await?;
// Override for a specific tenant
sdk.quotas().set("acme", QuotaOverride {
monthly_requests: Some(5_000_000),
..Default::default()
}).await?;sdk = SDK.from_env() # Phase 2 planned API
.request_quota({
"free_monthly": 10_000,
"starter_monthly": 100_000,
"business_monthly": 2_000_000,
"enterprise_monthly": None, # unlimited
"on_exhausted": "block",
"notify_at_pct": [80, 95, 100],
}) \
.build()
# Override for a specific tenant
await sdk.quotas.set("acme", {"monthly_requests": 5_000_000})sdk, _ := coresdk.NewBuilder().
RequestQuota(coresdk.RequestQuota{
FreeMonthly: 10_000,
StarterMonthly: 100_000,
BusinessMonthly: 2_000_000,
EnterpriseMonthly: nil, // unlimited
OnExhausted: coresdk.QuotaBlock,
NotifyAtPct: []int{80, 95, 100},
}).
Build(ctx)
// Override for a specific tenant
sdk.Quotas().Set(ctx, "acme", coresdk.QuotaOverride{
MonthlyRequests: ptr(5_000_000),
})const sdk = new CoreSDK({ tenant: "acme-corp" }) // Phase 2 planned API
.requestQuota({
freeMonthly: 10_000,
starterMonthly: 100_000,
businessMonthly: 2_000_000,
enterpriseMonthly: null, // unlimited
onExhausted: "block",
notifyAtPct: [80, 95, 100],
})
.build();
// Override for a specific tenant
await sdk.quotas.set("acme", { monthlyRequests: 5_000_000 });Storage quotas
CoreSDK tracks bytes written per tenant across blob storage, audit logs, and cached state. Writes that exceed the cap return 507 Insufficient Storage.
use coresdk_engine::quota::StorageQuota;
let sdk = Engine::from_env()?
.storage_quota(StorageQuota {
free_gb: 1.0,
starter_gb: 10.0,
business_gb: 100.0,
enterprise_gb: None, // unlimited
})
.build()
.await?;
// Check current usage
let usage = sdk.quotas().storage_usage("acme").await?;
println!("{:.2} GB used of {:.2} GB", usage.used_gb, usage.limit_gb);sdk = SDK.from_env() # Phase 2 planned API
.storage_quota({
"free_gb": 1.0,
"starter_gb": 10.0,
"business_gb": 100.0,
"enterprise_gb": None, # unlimited
}) \
.build()
usage = await sdk.quotas.storage_usage("acme")
print(f"{usage.used_gb:.2f} GB used of {usage.limit_gb:.2f} GB")sdk, _ := coresdk.NewBuilder().
StorageQuota(coresdk.StorageQuota{
FreeGB: 1.0,
StarterGB: 10.0,
BusinessGB: 100.0,
EnterpriseGB: nil, // unlimited
}).
Build(ctx)
usage, _ := sdk.Quotas().StorageUsage(ctx, "acme")
fmt.Printf("%.2f GB used of %.2f GB\n", usage.UsedGB, usage.LimitGB)const sdk = new CoreSDK({ tenant: "acme-corp" }) // Phase 2 planned API
.storageQuota({
freeGb: 1.0,
starterGb: 10.0,
businessGb: 100.0,
enterpriseGb: null, // unlimited
})
.build();
const usage = await sdk.quotas.storageUsage("acme");
console.log(`${usage.usedGb.toFixed(2)} GB used of ${usage.limitGb.toFixed(2)} GB`);Plan-based limits
Resolve the effective limit for a tenant at runtime to drive upgrade prompts or pre-flight checks.
let limits = sdk.quotas().effective_limits("acme").await?;
// limits.requests_per_minute — resolved from plan + any overrides
// limits.monthly_requests
// limits.storage_gb
// limits.seats
if limits.storage_gb.map(|l| usage_gb > l).unwrap_or(false) {
return Err(AppError::StorageCapReached);
}limits = await sdk.quotas.effective_limits("acme")
# limits.requests_per_minute, .monthly_requests, .storage_gb, .seats
if limits.storage_gb and usage_gb > limits.storage_gb:
raise StorageCapReachedError()limits, _ := sdk.Quotas().EffectiveLimits(ctx, "acme")
if limits.StorageGB != nil && usageGB > *limits.StorageGB {
return ErrStorageCapReached
}const limits = await sdk.quotas.effectiveLimits("acme");
if (limits.storageGb !== null && usageGb > limits.storageGb) {
throw new StorageCapReachedError();
}Enforcing limits in middleware
Mount quota enforcement as middleware so limits are applied uniformly before handlers run.
use axum::{Router, middleware};
use coresdk_engine::middleware::QuotaLayer;
let app = Router::new()
.route("/api/*path", get(handler).post(handler))
.layer(QuotaLayer::new(sdk.clone())
.enforce_rate_limit(true)
.enforce_monthly_quota(true)
.on_exceeded(|tenant, quota_type| async move {
tracing::warn!(tenant, ?quota_type, "quota exceeded");
})
);# FastAPI
from coresdk.middleware import QuotaMiddleware
app.add_middleware(
QuotaMiddleware,
sdk=sdk,
enforce_rate_limit=True,
enforce_monthly_quota=True,
on_exceeded=lambda tenant, quota_type: logger.warning(
"quota exceeded", tenant=tenant, quota_type=quota_type
),
)// net/http
mux := http.NewServeMux()
mux.HandleFunc("/api/", handler)
h := coresdk.QuotaMiddleware(sdk,
coresdk.WithRateLimit(true),
coresdk.WithMonthlyQuota(true),
coresdk.OnExceeded(func(tenant, quotaType string) {
slog.Warn("quota exceeded", "tenant", tenant, "quota_type", quotaType)
}),
)(mux)// Express
import { quotaMiddleware } from "@coresdk/express";
app.use(
quotaMiddleware(sdk, {
enforceRateLimit: true,
enforceMonthlyQuota: true,
onExceeded: (tenant, quotaType) => {
logger.warn("quota exceeded", { tenant, quotaType });
},
})
);Next steps
- Tenant Provisioning — create tenants with plan metadata
- Custom Domains & Branding — per-tenant domain routing
- Tenant Isolation — policy namespaces and audit streams