# WHOOSH Licensing Development Plan **Date**: 2025-09-01 **Branch**: `feature/license-gating-integration` **Status**: Ready for implementation (depends on KACHING Phase 1) **Priority**: MEDIUM - User experience and upselling integration ## Executive Summary WHOOSH currently has **zero CHORUS licensing integration**. The system operates without license validation, feature gating, or upselling workflows. This plan integrates WHOOSH with KACHING license authority to provide license-aware user experiences and revenue optimization. ## Current State Analysis ### ✅ Existing Infrastructure - React-based web application with modern UI components - Search and indexing functionality - User authentication and session management - API integration capabilities ### ❌ Missing License Integration - **No license status display** - Users unaware of their tier/limits - **No feature gating** - All features available regardless of license - **No upgrade workflows** - No upselling or upgrade prompts - **No usage tracking** - No integration with KACHING telemetry - **No quota visibility** - Users can't see usage limits or consumption ### Business Impact - **Zero upselling capability** - No way to drive license upgrades - **No usage awareness** - Customers don't know they're approaching limits - **No tier differentiation** - Premium features not monetized - **Revenue leakage** - Advanced features available to basic tier users ## Development Phases ### Phase 3A: License Status Integration (PRIORITY 1) **Goal**: Display license information and status throughout WHOOSH UI #### 1. License API Client Implementation ```typescript // src/services/licenseApi.ts export interface LicenseStatus { license_id: string; status: 'active' | 'suspended' | 'expired' | 'cancelled'; tier: 'evaluation' | 'standard' | 'enterprise'; features: string[]; max_nodes: number; expires_at: string; quotas: { search_requests: { used: number; limit: number }; storage_gb: { used: number; limit: number }; api_calls: { used: number; limit: number }; }; upgrade_suggestions?: UpgradeSuggestion[]; } export interface UpgradeSuggestion { reason: string; current_tier: string; suggested_tier: string; benefits: string[]; roi_estimate?: string; urgency: 'low' | 'medium' | 'high'; } class LicenseApiClient { private baseUrl: string; constructor(kachingUrl: string) { this.baseUrl = kachingUrl; } async getLicenseStatus(licenseId: string): Promise { const response = await fetch(`${this.baseUrl}/v1/license/status/${licenseId}`); if (!response.ok) { throw new Error('Failed to fetch license status'); } return response.json(); } async getUsageMetrics(licenseId: string): Promise { const response = await fetch(`${this.baseUrl}/v1/usage/metrics/${licenseId}`); return response.json(); } } ``` #### Backend Proxy (required in production) To avoid exposing licensing endpoints/IDs client-side and to enforce server-side checks, WHOOSH should proxy KACHING via its own backend: ```python # backend/app/api/license.py (FastAPI example) @router.get("/api/license/status") async def get_status(user=Depends(auth)): license_id = await resolve_license_id_for_org(user.org_id) res = await kaching.get(f"/v1/license/status/{license_id}") return res.json() @router.get("/api/license/quotas") async def get_quotas(user=Depends(auth)): license_id = await resolve_license_id_for_org(user.org_id) res = await kaching.get(f"/v1/license/{license_id}/quotas") return res.json() ``` And in the React client call the WHOOSH backend instead of KACHING directly: ```typescript // src/services/licenseApi.ts (frontend) export async function fetchLicenseStatus(): Promise { const res = await fetch("/api/license/status") if (!res.ok) throw new Error("Failed to fetch license status") return res.json() } ``` #### 2. License Status Dashboard Component ```typescript // src/components/license/LicenseStatusDashboard.tsx interface LicenseStatusDashboardProps { licenseId: string; } export const LicenseStatusDashboard: React.FC = ({ licenseId }) => { const [licenseStatus, setLicenseStatus] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchLicenseStatus = async () => { try { // In production, call WHOOSH backend proxy endpoints const status = await fetchLicenseStatus(); setLicenseStatus(status); } catch (error) { console.error('Failed to fetch license status:', error); } finally { setLoading(false); } }; fetchLicenseStatus(); // Refresh every 5 minutes const interval = setInterval(fetchLicenseStatus, 5 * 60 * 1000); return () => clearInterval(interval); }, [licenseId]); if (loading) return
Loading license information...
; if (!licenseStatus) return
License information unavailable
; return (
{licenseStatus.upgrade_suggestions?.map((suggestion, idx) => ( ))}
); }; ``` #### 3. License Status Header Component ```typescript // src/components/layout/LicenseStatusHeader.tsx export const LicenseStatusHeader: React.FC = () => { const { licenseStatus } = useLicenseContext(); const getStatusColor = (status: string) => { switch (status) { case 'active': return 'text-green-600'; case 'suspended': return 'text-red-600'; case 'expired': return 'text-orange-600'; default: return 'text-gray-600'; } }; return (
{licenseStatus?.tier?.toUpperCase()} License
{licenseStatus?.max_nodes} nodes max
Expires: {new Date(licenseStatus?.expires_at || '').toLocaleDateString()}
{licenseStatus?.status !== 'active' && ( )}
); }; ``` ### Phase 3B: Feature Gating Implementation (PRIORITY 2) **Goal**: Restrict features based on license tier and show upgrade prompts #### 1. Feature Gate Hook ```typescript // src/hooks/useLicenseFeatures.ts export const useLicenseFeatures = () => { const { licenseStatus } = useLicenseContext(); const hasFeature = (feature: string): boolean => { return licenseStatus?.features?.includes(feature) || false; }; const canUseAdvancedSearch = (): boolean => { return hasFeature('advanced-search'); }; const canUseAnalytics = (): boolean => { return hasFeature('advanced-analytics'); }; const canUseBulkOperations = (): boolean => { return hasFeature('bulk-operations'); }; const getMaxSearchResults = (): number => { if (hasFeature('enterprise-search')) return 10000; if (hasFeature('advanced-search')) return 1000; return 100; // Basic tier }; return { hasFeature, canUseAdvancedSearch, canUseAnalytics, canUseBulkOperations, getMaxSearchResults, }; }; ``` #### 2. Feature Gate Component ```typescript // src/components/license/FeatureGate.tsx interface FeatureGateProps { feature: string; children: React.ReactNode; fallback?: React.ReactNode; showUpgradePrompt?: boolean; } export const FeatureGate: React.FC = ({ feature, children, fallback, showUpgradePrompt = true }) => { const { hasFeature } = useLicenseFeatures(); const { licenseStatus } = useLicenseContext(); if (hasFeature(feature)) { return <>{children}; } if (fallback) { return <>{fallback}; } if (showUpgradePrompt) { return ( ); } return null; }; // Usage throughout WHOOSH: // // // ``` #### 3. Feature-Specific Gates ```typescript // src/components/search/AdvancedSearchFilters.tsx export const AdvancedSearchFilters: React.FC = () => { const { canUseAdvancedSearch } = useLicenseFeatures(); return (
{/* Advanced search filter components */}
); }; ``` ### Phase 3C: Quota Monitoring & Alerts (PRIORITY 3) **Goal**: Show usage quotas and proactive upgrade suggestions #### 1. Quota Usage Components ```typescript // src/components/license/QuotaUsageCard.tsx interface QuotaUsageCardProps { quotas: LicenseStatus['quotas']; } export const QuotaUsageCard: React.FC = ({ quotas }) => { const getUsagePercentage = (used: number, limit: number): number => { return Math.round((used / limit) * 100); }; const getUsageColor = (percentage: number): string => { if (percentage >= 90) return 'bg-red-500'; if (percentage >= 75) return 'bg-yellow-500'; return 'bg-green-500'; }; return (

Usage Overview

{Object.entries(quotas).map(([key, quota]) => { const percentage = getUsagePercentage(quota.used, quota.limit); return (
{key.replace('_', ' ').toUpperCase()} {quota.used.toLocaleString()} / {quota.limit.toLocaleString()}
{percentage >= 80 && (
⚠️ Approaching limit - consider upgrading
)}
); })}
); }; ``` #### 2. Upgrade Suggestion Component ```typescript // src/components/license/UpgradeSuggestionCard.tsx interface UpgradeSuggestionCardProps { suggestion: UpgradeSuggestion; } export const UpgradeSuggestionCard: React.FC = ({ suggestion }) => { const getUrgencyColor = (urgency: string): string => { switch (urgency) { case 'high': return 'border-red-500 bg-red-50'; case 'medium': return 'border-yellow-500 bg-yellow-50'; default: return 'border-blue-500 bg-blue-50'; } }; return (

{suggestion.reason}

Upgrade from {suggestion.current_tier} to {suggestion.suggested_tier}

{suggestion.roi_estimate && (

Estimated ROI: {suggestion.roi_estimate}

)}

Benefits:

    {suggestion.benefits.map((benefit, idx) => (
  • {benefit}
  • ))}
); }; ``` ### Phase 3D: Self-Service Upgrade Workflows (PRIORITY 4) **Goal**: Enable customers to upgrade licenses directly from WHOOSH #### 1. Upgrade Request Modal ```typescript // src/components/license/UpgradeRequestModal.tsx export const UpgradeRequestModal: React.FC = () => { const [selectedTier, setSelectedTier] = useState(''); const [justification, setJustification] = useState(''); const handleUpgradeRequest = async () => { const request = { current_tier: licenseStatus?.tier, requested_tier: selectedTier, justification, usage_evidence: await getUsageEvidence(), contact_email: userEmail, }; // Send to KACHING upgrade request endpoint await licenseApi.requestUpgrade(request); // Show success message and close modal showNotification('Upgrade request submitted successfully!'); }; return (

Request License Upgrade