|
| 1 | +# Commercial Content Moderation Restructure Plan |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Moving Fider's content moderation feature from open source (AGPL) to commercial licensing using an "open core" model. The commercial code will reside in a `commercial/` folder with a restrictive license, while the open source core provides infrastructure and gracefully degrades when commercial features aren't licensed. |
| 6 | + |
| 7 | +## Key Architectural Decisions |
| 8 | + |
| 9 | +### 1. Webpack Integration (Simple Solution) |
| 10 | +- Update `webpack.config.js` to scan both folders: |
| 11 | + ```js |
| 12 | + paths: [ |
| 13 | + ...glob.sync(`./public/**/*.{html,tsx}`, { nodir: true }), |
| 14 | + ...glob.sync(`./commercial/**/*.{html,tsx}`, { nodir: true }) |
| 15 | + ] |
| 16 | + ``` |
| 17 | +- Commercial code gets bundled but legal license prevents usage |
| 18 | +- No complex conditional compilation needed |
| 19 | + |
| 20 | +### 2. Backend Package Separation Strategy |
| 21 | + |
| 22 | +**Handlers - Dynamic Route Registration** |
| 23 | +- Open source: Register stub routes returning "upgrade" messages |
| 24 | +- Commercial: Override routes with real handlers via dynamic registration |
| 25 | +- License validation controls active routes |
| 26 | + |
| 27 | +**Services - Stub + Override Pattern** |
| 28 | +- Open source: Register stub bus handlers returning "not licensed" errors |
| 29 | +- Commercial: Override with real implementations via bus registration |
| 30 | +- Same command/query types, different implementations |
| 31 | + |
| 32 | +## What Moves to Commercial (Strong Legal Protection) |
| 33 | + |
| 34 | +### Commercial Folder Structure |
| 35 | +``` |
| 36 | +commercial/ |
| 37 | +├── LICENSE (restrictive commercial license) |
| 38 | +├── pages/ |
| 39 | +│ └── Administration/ |
| 40 | +│ └── ContentModeration.page.tsx (~300 lines) |
| 41 | +├── components/ |
| 42 | +│ └── ModerationIndicator.tsx |
| 43 | +├── handlers/ |
| 44 | +│ ├── moderation.go (ModerationPage, GetModerationItems, GetModerationCount) |
| 45 | +│ └── apiv1/ |
| 46 | +│ ├── moderation.go (all API endpoints) |
| 47 | +│ └── moderation_test.go |
| 48 | +├── services/ |
| 49 | +│ └── moderation.go (approve/decline business logic) |
| 50 | +└── init.go (service/route registration) |
| 51 | +``` |
| 52 | + |
| 53 | +### Frontend (Moves to Commercial) |
| 54 | +- Complete moderation admin UI (ContentModeration.page.tsx + styles) |
| 55 | +- Moderation indicator component showing pending counts |
| 56 | +- All moderation-specific UI components |
| 57 | + |
| 58 | +### Backend (Moves to Commercial) |
| 59 | +- All HTTP handlers (`ModerationPage`, `GetModerationItems`, `GetModerationCount`) |
| 60 | +- All API endpoints (`/api/v1/admin/moderation/*` routes) |
| 61 | +- Business logic implementations (approve/decline/verify/block functions) |
| 62 | +- Route registrations for moderation endpoints |
| 63 | +- Tests for commercial functionality |
| 64 | + |
| 65 | +## What Stays Open Source (Minimal Infrastructure) |
| 66 | + |
| 67 | +### Database/Models (Cannot Move - Bus System Dependency) |
| 68 | +- `app/models/cmd/moderation.go` - Command type definitions |
| 69 | +- `app/models/query/moderation.go` - Query type definitions |
| 70 | +- Database schema, migrations (`is_moderation_enabled`, `is_approved` columns) |
| 71 | +- Entity definitions (`ModerationItem` structs) |
| 72 | + |
| 73 | +### Settings Infrastructure |
| 74 | +- Privacy settings toggle (shows "upgrade" message if unlicensed) |
| 75 | +- Basic tenant property (`isModerationEnabled`) |
| 76 | +- License validation service |
| 77 | + |
| 78 | +### Content Flow Logic |
| 79 | +- Logic that marks content as needing approval |
| 80 | +- Basic "content requires moderation" checks throughout codebase |
| 81 | +- Content queuing when moderation is enabled |
| 82 | + |
| 83 | +### Locale Files |
| 84 | +- All `moderation.*` translation strings (used by upgrade messages too) |
| 85 | + |
| 86 | +## Technical Implementation Details |
| 87 | + |
| 88 | +### 1. Handler Registration Pattern |
| 89 | +```go |
| 90 | +// Open source routes.go - stub routes |
| 91 | +ui.Get("/admin/moderation", upgradeHandler("content-moderation")) |
| 92 | +ui.Get("/_api/admin/moderation/items", upgradeHandler("content-moderation")) |
| 93 | + |
| 94 | +// Commercial init.go - overrides routes |
| 95 | +func init() { |
| 96 | + if license.IsCommercialFeatureEnabled("content-moderation") { |
| 97 | + web.RegisterRoute("GET", "/admin/moderation", handlers.ModerationPage()) |
| 98 | + web.RegisterRoute("GET", "/_api/admin/moderation/items", handlers.GetModerationItems()) |
| 99 | + // ... all other moderation routes |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +### 2. Service Registration Pattern |
| 105 | +```go |
| 106 | +// Open source postgres.go - register stubs |
| 107 | +func (s *Service) Init() { |
| 108 | + bus.AddHandler(approvePostStub) // Returns "feature not licensed" |
| 109 | + bus.AddHandler(declinePostStub) |
| 110 | + bus.AddHandler(getModerationItemsStub) |
| 111 | + // ... other stubs |
| 112 | +} |
| 113 | + |
| 114 | +// Commercial service init - override with real handlers |
| 115 | +func (cs *CommercialService) Init() { |
| 116 | + if license.IsCommercialFeatureEnabled("content-moderation") { |
| 117 | + bus.AddHandler(approvePost) // Real implementation |
| 118 | + bus.AddHandler(declinePost) |
| 119 | + bus.AddHandler(getModerationItems) |
| 120 | + // ... other real handlers |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +### 3. License Validation Service |
| 126 | +```go |
| 127 | +// app/services/license.go |
| 128 | +type LicenseService interface { |
| 129 | + IsCommercialFeatureEnabled(feature string) bool |
| 130 | +} |
| 131 | + |
| 132 | +// Implementation checks for valid commercial license |
| 133 | +// Controls route registration and service overrides |
| 134 | +``` |
| 135 | + |
| 136 | +## Implementation Phases |
| 137 | + |
| 138 | +### Phase 1: Setup Commercial Infrastructure |
| 139 | +1. Create `commercial/` folder structure |
| 140 | +2. Add restrictive LICENSE file to commercial folder |
| 141 | +3. Update webpack.config.js to scan commercial folder |
| 142 | +4. Create license validation service interface |
| 143 | + |
| 144 | +### Phase 2: Move Frontend Components |
| 145 | +1. Move `ContentModeration.page.tsx` to `commercial/pages/Administration/` |
| 146 | +2. Move `ModerationIndicator.tsx` to `commercial/components/` |
| 147 | +3. Test that webpack builds both locations correctly |
| 148 | +4. Add license checks in components to show upgrade messages |
| 149 | + |
| 150 | +### Phase 3: Implement Backend Service Separation |
| 151 | +1. Create stub implementations for all moderation commands/queries |
| 152 | +2. Register stubs in open source postgres service |
| 153 | +3. Move real implementations to `commercial/services/moderation.go` |
| 154 | +4. Implement commercial service registration with license checks |
| 155 | + |
| 156 | +### Phase 4: Implement Route Separation |
| 157 | +1. Replace direct handler calls with upgrade handlers in routes.go |
| 158 | +2. Move real handlers to `commercial/handlers/` |
| 159 | +3. Implement dynamic route registration in commercial init |
| 160 | +4. Test route overriding works correctly |
| 161 | + |
| 162 | +### Phase 5: Testing & Validation |
| 163 | +1. Move tests to commercial folder |
| 164 | +2. Test open source build without commercial features |
| 165 | +3. Test commercial build with license validation |
| 166 | +4. Verify graceful degradation and upgrade messaging |
| 167 | +5. Test that forking open source works without commercial parts |
| 168 | + |
| 169 | +## Key Benefits |
| 170 | + |
| 171 | +### Strong Commercial Protection |
| 172 | +- Recreating moderation requires rebuilding entire UI + API + business logic |
| 173 | +- Substantial engineering effort (days/weeks) to replicate functionality |
| 174 | +- Clear legal separation under different licenses |
| 175 | + |
| 176 | +### Clean Architecture |
| 177 | +- Open source provides database infrastructure and settings |
| 178 | +- Commercial enhances with actual moderation functionality |
| 179 | +- No broken states - content flows normally when moderation disabled |
| 180 | +- True open core model: commercial builds on open source foundation |
| 181 | + |
| 182 | +### Simple Build Process |
| 183 | +- Single repository with clear folder separation |
| 184 | +- Standard webpack build process |
| 185 | +- License provides protection, not technical hiding |
| 186 | +- Easy development workflow |
| 187 | + |
| 188 | +## Migration Considerations |
| 189 | + |
| 190 | +### Existing Moderation Data |
| 191 | +- All existing moderation settings and data remain compatible |
| 192 | +- Database schema stays in open source (infrastructure) |
| 193 | +- Only the management interface becomes commercial |
| 194 | + |
| 195 | +### User Experience |
| 196 | +- **With License**: Full moderation functionality as before |
| 197 | +- **Without License**: Moderation simply disabled, standard user flow |
| 198 | +- **Upgrade Path**: Clear messaging and sales funnel for commercial features |
| 199 | + |
| 200 | +### Development Workflow |
| 201 | +- Developers can see all code (legal license controls usage) |
| 202 | +- Standard build process works for both open source and commercial |
| 203 | +- Clean separation makes it easy to add more commercial features |
| 204 | + |
| 205 | +## Success Metrics |
| 206 | + |
| 207 | +1. **Legal Protection**: Someone forking open source cannot easily recreate moderation |
| 208 | +2. **Functional Separation**: Open source works perfectly without moderation |
| 209 | +3. **Build Compatibility**: Both open source and commercial versions build successfully |
| 210 | +4. **Clean Boundaries**: Clear understanding of what's core vs commercial |
| 211 | +5. **Scalability**: Pattern works for future commercial features |
| 212 | + |
| 213 | +This plan provides genuine open core protection while maintaining clean architecture and manageable implementation complexity. |
0 commit comments