Business Objective: Transform WHOOSH from license-unaware to comprehensive license-integrated experience that drives upgrade conversions and maximizes customer lifetime value through usage visibility. Implementation Summary: 1. SECURE BACKEND PROXY INTEGRATION: - License API proxy endpoints (/api/license/status, /api/license/quotas) - Server-side license ID resolution (no frontend exposure) - Mock data support for development and testing - Intelligent upgrade suggestion algorithms 2. COMPREHENSIVE FRONTEND LICENSE INTEGRATION: - License API Client with caching and error handling - Global License Context for state management - License Status Header for always-visible tier information - Feature Gate Component for conditional rendering - License Dashboard with quotas, features, upgrade suggestions - Upgrade Prompt Components for revenue optimization 3. APPLICATION-WIDE INTEGRATION: - License Provider integrated into App context hierarchy - License status header in main navigation - License dashboard route at /license - Example feature gates in Analytics page - Version bump: → 1.2.0 Key Business Benefits: ✅ Revenue Optimization: Strategic feature gating drives conversions ✅ User Trust: Transparent license information builds confidence ✅ Proactive Upgrades: Usage-based suggestions with ROI estimates ✅ Self-Service: Clear upgrade paths reduce sales friction Security-First Design: 🔒 All license operations server-side via proxy 🔒 No sensitive license data exposed to frontend 🔒 Feature enforcement at API level prevents bypass 🔒 Graceful degradation for license API failures Technical Implementation: - React 18+ with TypeScript and modern hooks - Context API for license state management - Tailwind CSS following existing patterns - Backend proxy pattern for security compliance - Comprehensive error handling and loading states Files Created/Modified: Backend: - /backend/app/api/license.py - Complete license proxy API - /backend/app/main.py - Router integration Frontend: - /frontend/src/services/licenseApi.ts - API client with caching - /frontend/src/contexts/LicenseContext.tsx - Global license state - /frontend/src/hooks/useLicenseFeatures.ts - Feature checking logic - /frontend/src/components/license/* - Complete license UI components - /frontend/src/App.tsx - Context integration and routing - /frontend/package.json - Version bump to 1.2.0 This Phase 3A implementation provides the complete foundation for license-aware user experiences, driving revenue optimization through intelligent feature gating and upgrade suggestions while maintaining excellent UX and security best practices. Ready for KACHING integration and Phase 3B advanced features. 🤖 Generated with Claude Code (claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
21 KiB
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
// 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<LicenseStatus> {
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<UsageMetrics> {
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:
# 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:
// src/services/licenseApi.ts (frontend)
export async function fetchLicenseStatus(): Promise<LicenseStatus> {
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
// src/components/license/LicenseStatusDashboard.tsx
interface LicenseStatusDashboardProps {
licenseId: string;
}
export const LicenseStatusDashboard: React.FC<LicenseStatusDashboardProps> = ({ licenseId }) => {
const [licenseStatus, setLicenseStatus] = useState<LicenseStatus | null>(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 <div>Loading license information...</div>;
if (!licenseStatus) return <div>License information unavailable</div>;
return (
<div className="license-status-dashboard">
<LicenseStatusCard status={licenseStatus} />
<QuotaUsageCard quotas={licenseStatus.quotas} />
{licenseStatus.upgrade_suggestions?.map((suggestion, idx) => (
<UpgradeSuggestionCard key={idx} suggestion={suggestion} />
))}
</div>
);
};
3. License Status Header Component
// 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 (
<div className="flex items-center space-x-4 text-sm">
<div className={`font-medium ${getStatusColor(licenseStatus?.status || '')}`}>
{licenseStatus?.tier?.toUpperCase()} License
</div>
<div className="text-gray-500">
{licenseStatus?.max_nodes} nodes max
</div>
<div className="text-gray-500">
Expires: {new Date(licenseStatus?.expires_at || '').toLocaleDateString()}
</div>
{licenseStatus?.status !== 'active' && (
<button className="bg-blue-600 text-white px-3 py-1 rounded text-xs hover:bg-blue-700">
Renew License
</button>
)}
</div>
);
};
Phase 3B: Feature Gating Implementation (PRIORITY 2)
Goal: Restrict features based on license tier and show upgrade prompts
1. Feature Gate Hook
// 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
// src/components/license/FeatureGate.tsx
interface FeatureGateProps {
feature: string;
children: React.ReactNode;
fallback?: React.ReactNode;
showUpgradePrompt?: boolean;
}
export const FeatureGate: React.FC<FeatureGateProps> = ({
feature,
children,
fallback,
showUpgradePrompt = true
}) => {
const { hasFeature } = useLicenseFeatures();
const { licenseStatus } = useLicenseContext();
if (hasFeature(feature)) {
return <>{children}</>;
}
if (fallback) {
return <>{fallback}</>;
}
if (showUpgradePrompt) {
return (
<UpgradePrompt
feature={feature}
currentTier={licenseStatus?.tier || 'unknown'}
/>
);
}
return null;
};
// Usage throughout WHOOSH:
// <FeatureGate feature="advanced-analytics">
// <AdvancedAnalyticsPanel />
// </FeatureGate>
3. Feature-Specific Gates
// src/components/search/AdvancedSearchFilters.tsx
export const AdvancedSearchFilters: React.FC = () => {
const { canUseAdvancedSearch } = useLicenseFeatures();
return (
<FeatureGate feature="advanced-search">
<div className="advanced-filters">
{/* Advanced search filter components */}
</div>
<UpgradePrompt
feature="advanced-search"
message="Unlock advanced search filters with Standard tier"
benefits={[
"Date range filtering",
"Content type filters",
"Custom field search",
"Saved search queries"
]}
/>
</FeatureGate>
);
};
Phase 3C: Quota Monitoring & Alerts (PRIORITY 3)
Goal: Show usage quotas and proactive upgrade suggestions
1. Quota Usage Components
// src/components/license/QuotaUsageCard.tsx
interface QuotaUsageCardProps {
quotas: LicenseStatus['quotas'];
}
export const QuotaUsageCard: React.FC<QuotaUsageCardProps> = ({ 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 (
<div className="quota-usage-card bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">Usage Overview</h3>
{Object.entries(quotas).map(([key, quota]) => {
const percentage = getUsagePercentage(quota.used, quota.limit);
return (
<div key={key} className="mb-4">
<div className="flex justify-between text-sm font-medium">
<span>{key.replace('_', ' ').toUpperCase()}</span>
<span>{quota.used.toLocaleString()} / {quota.limit.toLocaleString()}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2 mt-1">
<div
className={`h-2 rounded-full ${getUsageColor(percentage)}`}
style={{ width: `${percentage}%` }}
/>
</div>
{percentage >= 80 && (
<div className="text-xs text-orange-600 mt-1">
⚠️ Approaching limit - consider upgrading
</div>
)}
</div>
);
})}
</div>
);
};
2. Upgrade Suggestion Component
// src/components/license/UpgradeSuggestionCard.tsx
interface UpgradeSuggestionCardProps {
suggestion: UpgradeSuggestion;
}
export const UpgradeSuggestionCard: React.FC<UpgradeSuggestionCardProps> = ({ 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 (
<div className={`upgrade-suggestion border-l-4 p-4 rounded ${getUrgencyColor(suggestion.urgency)}`}>
<div className="flex items-center justify-between">
<div>
<h4 className="font-semibold">{suggestion.reason}</h4>
<p className="text-sm text-gray-600 mt-1">
Upgrade from {suggestion.current_tier} to {suggestion.suggested_tier}
</p>
{suggestion.roi_estimate && (
<p className="text-sm font-medium text-green-600 mt-1">
Estimated ROI: {suggestion.roi_estimate}
</p>
)}
</div>
<button
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
onClick={() => handleUpgradeRequest(suggestion)}
>
Upgrade Now
</button>
</div>
<div className="mt-3">
<p className="text-sm font-medium">Benefits:</p>
<ul className="text-sm text-gray-600 mt-1">
{suggestion.benefits.map((benefit, idx) => (
<li key={idx} className="flex items-center">
<span className="text-green-500 mr-2">✓</span>
{benefit}
</li>
))}
</ul>
</div>
</div>
);
};
Phase 3D: Self-Service Upgrade Workflows (PRIORITY 4)
Goal: Enable customers to upgrade licenses directly from WHOOSH
1. Upgrade Request Modal
// src/components/license/UpgradeRequestModal.tsx
export const UpgradeRequestModal: React.FC = () => {
const [selectedTier, setSelectedTier] = useState<string>('');
const [justification, setJustification] = useState<string>('');
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 (
<Modal>
<div className="upgrade-request-modal">
<h2>Request License Upgrade</h2>
<TierComparisonTable
currentTier={licenseStatus?.tier}
highlightTier={selectedTier}
onTierSelect={setSelectedTier}
/>
<textarea
placeholder="Tell us about your use case and why you need an upgrade..."
value={justification}
onChange={(e) => setJustification(e.target.value)}
className="w-full p-3 border rounded"
/>
<UsageEvidencePanel licenseId={licenseStatus?.license_id} />
<div className="flex justify-end space-x-3 mt-6">
<button onClick={onClose}>Cancel</button>
<button
onClick={handleUpgradeRequest}
className="bg-blue-600 text-white px-6 py-2 rounded"
>
Submit Request
</button>
</div>
</div>
</Modal>
);
};
2. Contact Sales Integration
// src/components/license/ContactSalesWidget.tsx
export const ContactSalesWidget: React.FC = () => {
const { licenseStatus } = useLicenseContext();
const generateSalesContext = () => ({
license_id: licenseStatus?.license_id,
current_tier: licenseStatus?.tier,
usage_summary: getUsageSummary(),
pain_points: identifyPainPoints(),
upgrade_urgency: calculateUpgradeUrgency(),
});
return (
<div className="contact-sales-widget">
<h3>Need a Custom Solution?</h3>
<p>Talk to our sales team about enterprise features and pricing.</p>
<button
onClick={() => openSalesChat(generateSalesContext())}
className="bg-green-600 text-white px-4 py-2 rounded"
>
Contact Sales
</button>
<div className="text-xs text-gray-500 mt-2">
Your usage data will be shared to provide personalized recommendations
</div>
</div>
);
};
Implementation Files Structure
WHOOSH/
├── src/
│ ├── services/
│ │ ├── licenseApi.ts # KACHING API client
│ │ └── usageTracking.ts # Usage metrics collection
│ ├── hooks/
│ │ ├── useLicenseContext.ts # License state management
│ │ └── useLicenseFeatures.ts # Feature gate logic
│ ├── components/
│ │ ├── license/
│ │ │ ├── LicenseStatusDashboard.tsx
│ │ │ ├── FeatureGate.tsx
│ │ │ ├── QuotaUsageCard.tsx
│ │ │ ├── UpgradeSuggestionCard.tsx
│ │ │ └── UpgradeRequestModal.tsx
│ │ └── layout/
│ │ └── LicenseStatusHeader.tsx
│ ├── contexts/
│ │ └── LicenseContext.tsx # Global license state
│ └── utils/
│ ├── licenseHelpers.ts # License utility functions
│ └── usageAnalytics.ts # Usage calculation helpers
├── public/
│ └── license-tiers.json # Tier comparison data
└── docs/
└── license-integration.md # Integration documentation
Configuration Requirements
Environment Variables
# KACHING integration
REACT_APP_KACHING_URL=https://kaching.chorus.services # Dev only; in prod, use backend proxy
# Do NOT expose license keys/IDs in client-side configuration
# Feature flags
REACT_APP_ENABLE_LICENSE_GATING=true
REACT_APP_ENABLE_UPGRADE_PROMPTS=true
# Sales integration
REACT_APP_SALES_CHAT_URL=https://sales.chorus.services/chat
REACT_APP_SALES_EMAIL=sales@chorus.services
License Context Configuration
// src/config/licenseConfig.ts
export const LICENSE_CONFIG = {
tiers: {
evaluation: {
display_name: 'Evaluation',
max_search_results: 50,
features: ['basic-search'],
color: 'gray'
},
standard: {
display_name: 'Standard',
max_search_results: 1000,
features: ['basic-search', 'advanced-search', 'analytics'],
color: 'blue'
},
enterprise: {
display_name: 'Enterprise',
max_search_results: -1, // unlimited
features: ['basic-search', 'advanced-search', 'analytics', 'bulk-operations', 'enterprise-support'],
color: 'purple'
}
},
upgrade_thresholds: {
search_requests: 0.8, // Show upgrade at 80% quota usage
storage_gb: 0.9, // Show upgrade at 90% storage usage
api_calls: 0.85 // Show upgrade at 85% API usage
}
};
Testing Strategy
Unit Tests Required
- Feature gate hook functionality
- License status display components
- Quota usage calculations
- Upgrade suggestion logic
Integration Tests Required
- End-to-end license status fetching
- Feature gating across different components
- Upgrade request workflow
- Usage tracking integration
User Experience Tests
- License tier upgrade flows
- Feature restriction user messaging
- Quota limit notifications
- Sales contact workflows
Success Criteria
Phase 3A Success
- License status displayed prominently in UI
- Real-time quota usage monitoring
- Tier information clearly communicated to users
Phase 3B Success
- Features properly gated based on license tier
- Upgrade prompts appear for restricted features
- Clear messaging about tier limitations
Phase 3C Success
- Quota usage alerts trigger at appropriate thresholds
- Upgrade suggestions appear based on usage patterns
- Usage trends drive automated upselling
Phase 3D Success
- Self-service upgrade request workflow functional
- Sales team integration captures relevant context
- Customer can understand upgrade benefits clearly
Overall Success
- Increased license upgrade conversion rate
- Users aware of their license limitations
- Proactive upgrade suggestions drive revenue
- Seamless integration with KACHING license authority
Business Impact Metrics
Revenue Metrics
- License upgrade conversion rate (target: 15% monthly)
- Average revenue per user increase (target: 25% annually)
- Feature adoption rates by tier
User Experience Metrics
- License status awareness (target: 90% of users know their tier)
- Time to upgrade after quota warning (target: <7 days)
- Support tickets related to license confusion (target: <5% of total)
Technical Metrics
- License API response times (target: <200ms)
- Feature gate reliability (target: 99.9% uptime)
- Quota usage accuracy (target: 100% data integrity)
Dependencies
- KACHING Phase 1 Complete: Requires license server with quota APIs
- User Authentication: Must identify users to fetch license status
- Usage Tracking: Requires instrumentation to measure quota consumption
Security Considerations
- License ID Protection: Never expose license keys/IDs in client-side code; resolve license_id server-side
- API Authentication: Secure backend→KACHING with service credentials; frontend talks only to WHOOSH backend
- Feature Bypass Prevention: Enforce entitlements server-side for any sensitive operations
- Usage Data Privacy: Comply with data protection regulations for usage tracking
This plan transforms WHOOSH from license-unaware to a comprehensive license-integrated experience that drives revenue optimization and user satisfaction.