Plan Enforcement
How Technical Debt Radar enforces plan limits on both frontend and backend — feature gating, error responses, and the full enforcement matrix.
Plan Enforcement
Technical Debt Radar enforces plan limits at every layer --- API guards on the backend, UI gates on the frontend, and CLI checks before AI operations. When a user attempts to access a feature not included in their plan, they receive a clear error with instructions to upgrade.
Full Enforcement Matrix
This table shows every gated feature and the minimum plan required to access it.
| Feature | Free | Solo | Pro | Team | Enterprise |
|---|---|---|---|---|---|
| CLI: radar scan | Yes | Yes | Yes | Yes | Yes |
| CLI: radar init | Yes | Yes | Yes | Yes | Yes |
| CLI: radar validate | Yes | Yes | Yes | Yes | Yes |
| CLI: radar run | Yes | Yes | Yes | Yes | Yes |
| CLI: radar badge | Yes | Yes | Yes | Yes | Yes |
| CLI: radar fix | --- | Yes | Yes | Yes | Yes |
| CLI: radar summary | --- | Yes | Yes | Yes | Yes |
| CLI: radar pack | --- | --- | Yes | Yes | Yes |
| GitHub Action (deterministic) | Yes | Yes | Yes | Yes | Yes |
| PR merge gate (status check) | --- | Yes | Yes | Yes | Yes |
| AI PR comments | --- | --- | Yes | Yes | Yes |
| AI cross-file analysis | --- | --- | Yes | Yes | Yes |
| GitLab integration | --- | --- | Yes | Yes | Yes |
| Dashboard: debt score | --- | Yes | Yes | Yes | Yes |
| Dashboard: trend charts | --- | Yes | Yes | Yes | Yes |
| Dashboard: arch graph | --- | --- | Yes | Yes | Yes |
| Dashboard: policy editor | --- | --- | Yes | Yes | Yes |
| Dashboard: rule packs | --- | --- | Yes | Yes | Yes |
| Dashboard: team analytics | --- | --- | --- | Yes | Yes |
| README badge | --- | Yes | Yes | Yes | Yes |
| Slack notifications | --- | --- | Yes | Yes | Yes |
| Webhook API | --- | --- | Yes | Yes | Yes |
| Coverage delta tracking | --- | --- | Yes | Yes | Yes |
| SSO (SAML/OIDC) | --- | --- | --- | --- | Yes |
| Self-hosted deployment | --- | --- | --- | --- | Yes |
| SLA guarantee | --- | --- | --- | --- | Yes |
Resource Limits
| Resource | Free | Solo | Pro | Team | Enterprise |
|---|---|---|---|---|---|
| Repositories | 2 | 5 | Unlimited | Unlimited | Unlimited |
| Members | 1 | 1 | Unlimited | Unlimited | Unlimited |
| Organizations | 1 | 1 | 3 | 10 | Unlimited |
| AI credits/month | 0 | 50 | 500 | 2,000 | Unlimited |
Backend Enforcement
All plan checks happen server-side in the NestJS API. The frontend never makes the final decision about feature access.
PlanGuard
The PlanGuard is a NestJS guard that checks the organization's active plan before allowing a request to proceed:
@UseGuards(PlanGuard)
@RequiresPlan('pro')
@Post('cross-file-analysis')
async runCrossFileAnalysis(@Body() dto: CrossFileDto) {
// Only Pro+ orgs reach this handler
}
Error Response Format
When a request is blocked by plan enforcement, the API returns a 403 Forbidden response with a structured error body:
{
"statusCode": 403,
"error": "PlanLimitExceeded",
"message": "AI cross-file analysis requires the Pro plan or higher.",
"currentPlan": "solo",
"requiredPlan": "pro",
"upgradeUrl": "https://app.radar.dev/acme-engineering/settings/billing?upgrade=pro",
"feature": "cross-file-analysis"
}
Every plan-gated error includes:
| Field | Description |
|---|---|
currentPlan | The organization's current plan identifier |
requiredPlan | The minimum plan needed for this feature |
upgradeUrl | Direct link to the billing page with the recommended plan pre-selected |
feature | Machine-readable feature identifier |
Resource Limit Errors
When an organization hits a resource limit (repos, members, orgs), the error format is similar:
{
"statusCode": 403,
"error": "ResourceLimitExceeded",
"message": "Your Solo plan supports up to 5 repositories. You have 5 active repositories.",
"currentPlan": "solo",
"requiredPlan": "pro",
"upgradeUrl": "https://app.radar.dev/acme-engineering/settings/billing?upgrade=pro",
"resource": "repositories",
"currentUsage": 5,
"limit": 5
}
Credit Exhaustion Errors
When AI credits are exhausted:
{
"statusCode": 403,
"error": "CreditsExhausted",
"message": "AI credits exhausted. Deterministic analysis continues. Credits reset on April 15.",
"currentPlan": "pro",
"creditsRemaining": 0,
"creditsTotal": 500,
"resetDate": "2026-04-15T00:00:00Z",
"buyCreditsUrl": "https://app.radar.dev/acme-engineering/settings/billing/credits"
}
Frontend Enforcement
The frontend uses plan information to render appropriate UI states. This is a user experience layer --- it does not replace backend enforcement.
Feature Gates
Components check the organization's plan before rendering feature UI:
// Feature is hidden entirely if plan is insufficient
{org.plan >= Plan.PRO && (
<CrossFileAnalysisPanel />
)}
// Feature shows an upgrade prompt
{org.plan < Plan.PRO ? (
<UpgradePrompt
feature="AI Cross-File Analysis"
requiredPlan="pro"
currentPlan={org.plan}
/>
) : (
<CrossFileAnalysisPanel />
)}
Upgrade Prompt Component
When a user encounters a gated feature, the UpgradePrompt component displays:
- The feature name and a brief description of what it does
- The user's current plan
- The required plan with its price
- A direct link to the upgrade page
This avoids dead-end UI states. Users always know what they need to do and how much it costs.
Disabled States
For features that are visible but not usable on the current plan:
- Buttons are disabled with a tooltip explaining the plan requirement
- Menu items show a plan badge (e.g., "PRO" next to "Architecture Graph")
- Charts show placeholder states with an upgrade call-to-action
CLI Enforcement
The CLI checks plan requirements before making AI API calls:
$ radar fix .
Error: AI Fix Generation requires the Solo plan or higher.
Your current plan: Free
Upgrade at: https://app.radar.dev/settings/billing?upgrade=solo
Tip: You can still identify violations with `radar scan .` (free)
and fix them manually, or use `radar scan --format ai-prompt` to
generate instructions for your own AI tool.
The CLI also checks credit balance before AI operations:
$ radar fix .
Error: AI credits exhausted (0/50 remaining).
Credits reset on: April 15, 2026
Buy more: radar credits buy --pack small
Or use: radar scan --format ai-prompt (free, uses your own AI tool)
How Enforcement Is Implemented
Request arrives
→ AuthGuard: verify JWT token
→ OrgRolesGuard: verify user has role in org
→ PlanGuard: verify org plan includes feature
→ CreditGuard: verify org has available credits (AI only)
→ Handler executes
Each guard runs in order. If any guard rejects the request, the subsequent guards do not execute. This ensures clear, specific error messages --- a user sees "upgrade to Pro" rather than a generic "forbidden" error.