-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
Summary
Introduce a framework for controlling which capabilities and features are available in a free tier vs a paid (Pro) tier. This is an infrastructure concern — the framework itself, not the specific tier boundaries (those are product decisions that will evolve).
Goals
- Gate features declaratively — a single source of truth for what's free vs Pro, not scattered
if (isPro)checks throughout components - Graceful degradation — free-tier users see that a feature exists but is locked, not a broken UI with missing pieces
- Upgrade nudge, not frustration — locked features show a brief explanation and upgrade path, not error messages
- Easy to adjust — moving a feature between tiers should be a config change, not a code change
Proposed Architecture
Entitlement Registry
A central module defining all gated capabilities and their tier requirements:
// entitlements.ts
export type Tier = 'free' | 'pro';
export interface Entitlement {
id: string;
label: string; // Human-readable name
tier: Tier; // Minimum tier required
description?: string; // Shown on upgrade nudge
limit?: number; // For quantity-gated features (e.g., max mappings)
}
const ENTITLEMENTS: Record<string, Entitlement> = {
'mappings.unlimited': { id: 'mappings.unlimited', label: 'Unlimited mappings', tier: 'pro' },
'chat.unlimited': { id: 'chat.unlimited', label: 'Unlimited chat messages', tier: 'pro' },
'chat.provider.all': { id: 'chat.provider.all', label: 'All LLM providers', tier: 'pro' },
'usage.analytics': { id: 'usage.analytics', label: 'Usage analytics', tier: 'pro' },
'profiles.export': { id: 'profiles.export', label: 'Profile export', tier: 'free' },
'plugins.wasm': { id: 'plugins.wasm', label: 'WASM plugins', tier: 'pro' },
'developer.mode': { id: 'developer.mode', label: 'Developer mode', tier: 'free' },
// ... etc
};Entitlement Store
// stores/entitlements.js
import { writable, derived } from 'svelte/store';
export const currentTier = writable('free'); // Set by license validation
export function canUse(entitlementId: string): Readable<boolean> {
return derived(currentTier, $tier => {
const ent = ENTITLEMENTS[entitlementId];
if (!ent) return true; // Unknown = ungated
return tierLevel($tier) >= tierLevel(ent.tier);
});
}
export function getLimit(entitlementId: string): Readable<number | null> {
return derived(currentTier, $tier => {
// Return tier-specific limit or null (unlimited)
});
}Gating Components
A reusable Svelte wrapper for gated UI elements:
<!-- ProGate.svelte -->
<script>
export let entitlement; // e.g., 'chat.unlimited'
const allowed = canUse(entitlement);
</script>
{#if $allowed}
<slot />
{:else}
<slot name="locked">
<div class="pro-gate">
<span class="pro-badge">Pro</span>
<span class="gate-label">{getEntitlement(entitlement).label}</span>
</div>
</slot>
{/if}Usage:
<ProGate entitlement="usage.analytics">
<UsageAnalyticsPanel />
<div slot="locked">
<UpgradeNudge feature="Usage Analytics" />
</div>
</ProGate>Quantity Limits
Some features may be free up to a limit:
| Feature | Free | Pro |
|---|---|---|
| Mappings per mode | 10? | Unlimited |
| Chat messages/day | 20? | Unlimited |
| LLM providers | 1 (default) | All 5 |
| Modes | 3? | Unlimited |
| Saved profiles | 2? | Unlimited |
(Exact limits TBD — product decisions, not engineering ones.)
License Validation
Out of scope for this issue, but the framework should support:
- Offline-first: Tier determination must work without network. License key validated locally.
- No phone-home for core functionality: The daemon + TOML config always works regardless of tier (Epic: Settings Persistence Architecture (ADR-017) #559 principle — "daemon + TOML is self-contained")
- GUI-only gating: Tier restrictions are enforced in the GUI layer only. The daemon has no concept of tiers. A power user with
conductorctlgets full capability — that's acceptable.
What This Issue Does NOT Cover
- Specific tier boundaries (which features are free vs Pro) — product decision
- Payment integration, license key generation, or account management
- Cloud sync or server-side entitlement checking
- Pricing
Related Issues
- Feature: Persist mapping fire usage data for LLM-powered usage analytics #554 — Usage analytics (likely Pro-tier feature)
- Epic: Settings Persistence Architecture (ADR-017) #559 — State persistence boundaries (tier = GUI preference, not daemon concern)
- feat(gui): Developer menu with daemon/GUI output viewer #433 — Developer mode (likely free-tier, but framework should be able to gate it)
Priority
P3 — Infrastructure that needs to be in place before any Pro-tier features ship, but no urgency until tier boundaries are defined.
Acceptance Criteria
- Central entitlement registry with declarative tier assignments
- Svelte store providing reactive
canUse(entitlement)checks -
ProGatewrapper component for gated UI sections - Quantity limit support (e.g., max N mappings in free tier)
- Locked features show upgrade nudge, not broken UI
- Moving a feature between tiers requires only a registry change
- No daemon-side tier awareness — gating is GUI-only
- Framework works offline (no network dependency for tier checks)
🤖 Generated with Claude Code
Reactions are currently unavailable