Plans & Constraints
Relevant source files
The following files were used as context for generating this wiki page:
Plans & Constraints manages workspace entitlements, resource allocation, and metered usage within the Vantage platform. The system enforces functional boundaries based on a workspace's tier—typically "free" or "pro"—to ensure fair resource distribution and cost control. It tracks static counts, such as projects and keywords, alongside metered metrics like daily lead scoring and monthly deep searches.
The module provides atomic consumption logic to prevent race conditions during usage updates and implements "grandfathering" logic for existing data that exceeds new plan restrictions.
Architecture and Data Model
The system stores plan definitions and usage data in Postgres, managed through Drizzle ORM. The data model separates fixed workspace metadata from dynamic usage tracking and configurable plan limits.
Core Tables
workspace: Stores the primary plan identifier (freeorpro) and the monthly budget for each tenant.plan_limit: Defines specific integer values for various features (e.g., maximum keywords) per plan tier.workspace_usage: Tracks metered consumption per workspace for specific UTC periods (days or months).
The diagram shows the relationship between workspaces, their tier-based limits, and their metered consumption records.
Sources: lib/db/schema.ts:507-534, drizzle/0000_m1_core.sql:5-17
Limit Evaluation Logic
The system distinguishes between Static Limits (simple count checks) and Metered Limits (time-bound consumption).
Static Limits
Static limits govern quantities like projects, keywords, and sources. The checkCountLimit function evaluates whether adding a new item exceeds the allowed threshold.
- Keywords: Enforces a cap on monitoring topics. The system allows human reviewers to maintain existing "grandfathered" topics even if they exceed current plan limits, provided the user does not add more.
- Projects: Enforces limits based on the highest tier plan owned by a user across all their workspaces.
Sources: lib/plans/limits.ts:208-237, test/plans.test.ts:98-112
Metered Limits
Metered limits track usage over UTC-based windows.
| Key | Period | Description |
|---|---|---|
scored_leads_per_day | Day (YYYY-MM-DD) | Limits daily lead generation. |
deep_searches_per_month | Month (YYYY-MM-01) | Limits monthly intensive search operations. |
Sources: lib/plans/limits.ts:16-29, test/plans.test.ts:79-82
Atomic Consumption
To prevent over-consumption in high-concurrency environments, Vantage uses a single-statement atomic upsert for metered usage. The consume function issues a SQL statement that includes a WHERE guard. This guard verifies that the incremented value remains below the plan limit retrieved in the same transaction.
The sequence diagram illustrates the atomic guard preventing usage from exceeding the plan cap.
Sources: lib/plans/limits.ts:109-150, test/plans.test.ts:133-157
Plan Enforcement in Operations
The system integrates these constraints directly into the lead generation and profiling workflows.
Opportunity Queue Constraints
During the automated build of opportunities, the system attempts to consume units for each potential new lead. If the consume call returns allowed: false, the process flags the workspace as budget_limited and halts lead creation for that period.
Sources: test/plan-enforcement.test.ts:57-64
Scheduling Constraints
Scan cadences are restricted by plan-defined intervals. The isScanDue function calculates if a source is eligible for polling by comparing the current time against the lastPolledAt timestamp, accounting for a 10-minute "slack" to accommodate cron jitter.
Sources: lib/plans/limits.ts:40-45, test/plans.test.ts:84-91
Implementation Details
// lib/plans/limits.ts
/** Atomic consumption logic for metered limits */
export async function consume(
workspaceId: string,
key: MeteredKey,
n = 1,
now: Date = new Date(),
): Promise<ConsumeResult> {
const meta = METERED[key];
const col = sql.raw(`"${meta.column}"`);
const qualified = sql.raw(`"workspace_usage"."${meta.column}"`);
const period = usagePeriod(key, now);
// The limit is read inside the statement so SQL edits apply instantly
const limitExpr = sql`coalesce(...)`;
const result = await getDb().execute(sql`
insert into "workspace_usage" ("workspace_id", "period", ${col})
select ${workspaceId}::uuid, ${period}::date, ${n}::int
where ${n}::int <= ${limitExpr}
on conflict ("workspace_id", "period") do update
set ${col} = ${qualified} + ${n}::int, "updated_at" = now()
where ${qualified} + ${n}::int <= ${limitExpr}
returning ${col} as "used", ${limitExpr} as "limit"`);
// ... handling result
}
Sources: lib/plans/limits.ts:109-141
The Plans & Constraints system ensures the Vantage platform scales predictably by anchoring all resource-intensive operations to a centralized enforcement engine. This architecture facilitates plan upgrades and prevents service abuse through rigorous database-level validation.