52 KiB
HiveOps Browser: Protocol Handling, Licensing & User Management
Overview
Add domain-based protocol handling (*.hiveops.com), external API-based licensing system with full feature controls (activation, tiers, expiration), and user authentication (license key + user profile).
Architecture: Two-component system:
- hiveops-browser (Electron app) - Desktop client with protocol handling and licensing enforcement
- hiveops-mgmt (Spring Boot 4 microservice) - Central management service for licensing, user profiles, and global settings
Requirements Summary
- Protocol Handling: Domain-based (*.hiveops.com) and custom protocol (hiveops://)
- Licensing: External API service with full controls (activation, feature-based tiers, time-based expiration)
- Authentication: License key + User profile system
- License Tiers: Basic, Pro, Enterprise (each with different feature sets)
- Backend: Spring Boot 4 microservice with PostgreSQL database
- Repository: Separate repository for hiveops-mgmt microservice
Implementation Plan
1. Core License Management Infrastructure
1.1 Create License Manager Module
File: src/main/license-manager.js (NEW)
Core responsibilities:
- Validate license key format (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)
- Store/retrieve license data using encrypted electron-store
- Verify license signatures (anti-tamper using HMAC-SHA256)
- Check license validity (expiration, signature)
- Feature gate checks (tier-based feature access)
- User profile management
Key methods:
validateLicenseKeyFormat(key)- Format validationstoreLicense(licenseData)- Encrypted storageisLicenseValid()- Validity check (expiration + signature)hasFeature(featureName)- Feature gate checkgetTier()- Get license tier (basic/pro/enterprise)getDaysUntilExpiration()- Expiration countdown
1.2 Create API Client Module
File: src/main/api-client.js (NEW)
External API communication:
- Base URL:
https://api.hiveops.com/v1(configurable) - 30-second timeout for all requests
- HTTPS-only with proper error handling
Key endpoints:
POST /licenses/activate- Activate license keyPOST /licenses/validate- Online validationPOST /auth/login- User loginGET /users/profile- Get user profileGET /licenses/{key}/status- Check license statusPOST /licenses/deactivate- Deactivate license
Key methods:
activateLicense(licenseKey, machineId)- Activate licensevalidateLicense(licenseKey, machineId)- Validate onlinelogin(email, password)- User authenticationgetUserProfile(authToken)- Fetch profilegetMachineId()- Generate unique device ID
1.3 Create Authentication Manager
File: src/main/auth-manager.js (NEW)
User authentication and session management:
- Store auth tokens securely
- Manage user profiles
- Handle login/logout
- Token refresh logic
Key methods:
login(email, password)- User loginlogout()- Clear sessionisAuthenticated()- Check auth statusgetUser()- Get current usergetToken()- Get auth tokenrefreshProfile()- Update user data
2. Application Lifecycle Integration
2.1 Modify Main Process
File: src/main/main.js (MODIFY)
Changes at app startup (app.whenReady):
// Add license check before main window
if (!licenseManager.isLicenseValid()) {
createLicenseActivationWindow(); // Block app until activated
} else {
createMainWindow();
// Show expiration warning if < 7 days remaining
const daysRemaining = licenseManager.getDaysUntilExpiration();
if (daysRemaining <= 7) showExpirationWarning(daysRemaining);
}
Add new window functions:
createLicenseActivationWindow()- 600x500 modal for activationopenLicenseManagement()- 600x500 modal for license infoshowExpirationWarning(days)- Dialog for expiration alerts
Add background license validation:
- Every 24 hours, validate license online
- Update local cache with server data
- Handle offline gracefully (use local validation)
- Force quit if license revoked or expired
Add IPC handlers:
activate-license- Activate via APIget-license-info- Return license datadeactivate-license- Deactivate and quitvalidate-license- Online validationhas-feature- Check feature accesslogin- User authenticationlogout- Clear sessionget-user-profile- Get user datais-authenticated- Check auth status
2.2 Update Configuration Module
File: src/main/config.js (MODIFY)
Add feature tier definitions:
const FEATURE_TIERS = {
basic: ['single-url', 'basic-navigation'],
pro: ['single-url', 'basic-navigation', 'subdomain-access', 'zoom-controls'],
enterprise: ['single-url', 'basic-navigation', 'subdomain-access', 'zoom-controls',
'custom-protocols', 'advanced-settings', 'api-access']
};
Add methods:
isFeatureEnabled(featureName)- Check if feature is in current tier- Modify
isUrlAllowed()to check license validity first
Update default-config.json:
{
"apiBaseUrl": "https://api.hiveops.com/v1"
}
2.3 Update IPC Bridge
File: src/main/preload.js (MODIFY)
Expose new APIs to renderer:
electronAPI: {
// License APIs
activateLicense: (licenseKey) => ipcRenderer.invoke('activate-license', licenseKey),
getLicenseInfo: () => ipcRenderer.invoke('get-license-info'),
deactivateLicense: () => ipcRenderer.invoke('deactivate-license'),
validateLicense: () => ipcRenderer.invoke('validate-license'),
hasFeature: (featureName) => ipcRenderer.invoke('has-feature', featureName),
// Auth APIs
login: (email, password) => ipcRenderer.invoke('login', email, password),
logout: () => ipcRenderer.invoke('logout'),
getUserProfile: () => ipcRenderer.invoke('get-user-profile'),
isAuthenticated: () => ipcRenderer.invoke('is-authenticated'),
// Window control
closeLicenseWindow: () => ipcRenderer.send('close-license-window'),
openLicenseManagement: () => ipcRenderer.send('open-license-management')
}
3. User Interface Components
3.1 License Activation Screen
Files:
src/renderer/license-activation.html(NEW)src/renderer/license-activation.js(NEW)src/renderer/license.css(NEW)
Features:
- Tabbed interface: "License Key" and "User Login"
- License key input with auto-formatting (dashes added automatically)
- Email/password login form
- Error/success message display
- Loading states for async operations
- Links to trial/purchase pages
Workflow:
- User enters license key OR logs in with credentials
- Validate format client-side
- Call API via IPC
- Show success/error
- Close window on success (main window opens)
- Quit app on cancel if no valid license
3.2 License Management Screen
Files:
src/renderer/license-management.html(NEW)src/renderer/license-management.js(NEW)
Features:
- Display license status (active/expired/trial)
- Show license tier (basic/pro/enterprise)
- Show expiration date and days remaining
- Display masked license key
- Show user profile (name, email) if logged in
- List enabled features for current tier
- Refresh button (online validation)
- Deactivate button (clear license and quit)
3.3 Update Settings Page
File: src/renderer/settings.html (MODIFY)
Add license information section:
- Display current tier
- Display expiration date
- "Manage License" button → opens license management window
Add to settings.js:
- Load license info on page load
- Display tier and expiration
- Handle "Manage License" button click
4. Protocol Handler Registration
4.1 Update Build Configuration
File: electron-builder.yml (MODIFY)
Add protocol registration:
protocols:
- name: HiveOps Browser
schemes:
- hiveops
role: Viewer
win:
fileAssociations:
- ext: hiveops
name: HiveOps Link
description: HiveOps Browser Link
role: Viewer
nsis:
include: installer-script.nsh # Custom registry script
linux:
mimeTypes:
- x-scheme-handler/hiveops
desktop:
MimeType: x-scheme-handler/hiveops
4.2 Windows Protocol Registration
File: installer-script.nsh (NEW)
NSIS script for Windows registry:
!macro customInstall
WriteRegStr HKCR "hiveops" "" "URL:HiveOps Protocol"
WriteRegStr HKCR "hiveops" "URL Protocol" ""
WriteRegStr HKCR "hiveops\DefaultIcon" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME},0"
WriteRegStr HKCR "hiveops\shell\open\command" "" '"$INSTDIR\${APP_EXECUTABLE_FILENAME}" "%1"'
!macroend
!macro customUnInstall
DeleteRegKey HKCR "hiveops"
!macroend
4.3 Protocol Handling in Main Process
File: src/main/main.js (MODIFY)
Add protocol handler setup:
// Register protocol
app.setAsDefaultProtocolClient('hiveops');
// Handle macOS protocol URLs
app.on('open-url', (event, url) => {
event.preventDefault();
handleProtocolUrl(url);
});
// Handle Windows/Linux (single instance)
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', (event, commandLine) => {
const url = commandLine.find(arg => arg.startsWith('hiveops://'));
if (url) handleProtocolUrl(url);
if (mainWindow) {
mainWindow.restore();
mainWindow.focus();
}
});
}
function handleProtocolUrl(protocolUrl) {
// Parse: hiveops://open?url=https://example.hiveops.com/path
const url = new URL(protocolUrl);
const targetUrl = url.searchParams.get('url');
if (targetUrl && config.isUrlAllowed(targetUrl)) {
mainWindow.loadURL(targetUrl);
}
}
5. Feature Gates Implementation
5.1 Feature-Gated Menu Items
File: src/main/main.js (MODIFY)
Wrap menu items with feature checks:
{
label: 'Zoom In',
click: () => {
if (!config.isFeatureEnabled('zoom-controls')) {
dialog.showMessageBoxSync({
type: 'info',
title: 'Feature Not Available',
message: 'Zoom controls require Pro or Enterprise license.'
});
return;
}
// Zoom logic
}
}
Apply to:
- Zoom controls (Pro+)
- Settings (if advanced settings are Enterprise-only)
- Multiple windows (Pro+)
- Custom protocol handling (Enterprise)
5.2 Feature-Gated URL Access
File: src/main/config.js (MODIFY)
Already implemented in isUrlAllowed():
- Basic: Single URL only
- Pro: Single URL + subdomains
- Enterprise: Single URL + subdomains + custom protocols
6. Security Implementation
6.1 Encrypted License Storage
- Use electron-store with encryption key derived from machine ID
- Store license data in:
~/.config/HiveOps/hiveops-license(Linux) - Encryption prevents tampering with local data
6.2 License Signature Verification
- Server generates HMAC-SHA256 signature:
HMAC(key + tier + expiry, secret) - Client verifies signature on every validation
- Prevents local tampering with tier/expiration
6.3 Machine ID Fingerprinting
- Use
app.getSystemId()or fallback to deterministic ID - Hash with SHA256 for consistent device identification
- Used to enforce activation limits (e.g., 3 devices per license)
6.4 Online/Offline Validation
- Primary: Local signature + expiration check (works offline)
- Secondary: Background API validation every 24 hours (when online)
- Grace period: Allow offline operation for up to 7 days
- Forced validation: On app startup if last check > 7 days ago
7. HiveOps Management Microservice (hiveops-mgmt)
Separate Repository: Create new Spring Boot 4 microservice in separate repository
7.1 Technology Stack
- Framework: Spring Boot 4.0.x
- Language: Java 21 (LTS)
- Database: PostgreSQL 16+
- Security: Spring Security 6 with JWT
- ORM: Spring Data JPA (Hibernate)
- API Docs: SpringDoc OpenAPI 3
- Build: Maven or Gradle
- Deployment: Docker containerized
7.2 Project Structure
hiveops-mgmt/
├── src/
│ ├── main/
│ │ ├── java/com/hiveops/mgmt/
│ │ │ ├── HiveOpsMgmtApplication.java
│ │ │ ├── config/
│ │ │ │ ├── SecurityConfig.java
│ │ │ │ ├── JwtConfig.java
│ │ │ │ └── CorsConfig.java
│ │ │ ├── controller/
│ │ │ │ ├── LicenseController.java
│ │ │ │ ├── AuthController.java
│ │ │ │ ├── UserController.java
│ │ │ │ └── GlobalSettingsController.java
│ │ │ ├── service/
│ │ │ │ ├── LicenseService.java
│ │ │ │ ├── AuthService.java
│ │ │ │ ├── UserService.java
│ │ │ │ ├── LicenseKeyGenerator.java
│ │ │ │ └── GlobalSettingsService.java
│ │ │ ├── repository/
│ │ │ │ ├── LicenseRepository.java
│ │ │ │ ├── UserRepository.java
│ │ │ │ ├── LicenseActivationRepository.java
│ │ │ │ └── GlobalSettingsRepository.java
│ │ │ ├── model/
│ │ │ │ ├── entity/
│ │ │ │ │ ├── License.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ ├── LicenseActivation.java
│ │ │ │ │ └── GlobalSetting.java
│ │ │ │ ├── dto/
│ │ │ │ │ ├── LicenseActivationRequest.java
│ │ │ │ │ ├── LicenseActivationResponse.java
│ │ │ │ │ ├── LoginRequest.java
│ │ │ │ │ ├── LoginResponse.java
│ │ │ │ │ └── UserProfileResponse.java
│ │ │ │ └── enums/
│ │ │ │ ├── LicenseTier.java
│ │ │ │ ├── LicenseStatus.java
│ │ │ │ └── UserRole.java
│ │ │ ├── security/
│ │ │ │ ├── JwtTokenProvider.java
│ │ │ │ ├── JwtAuthenticationFilter.java
│ │ │ │ └── UserDetailsServiceImpl.java
│ │ │ ├── exception/
│ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ ├── LicenseException.java
│ │ │ │ └── AuthenticationException.java
│ │ │ └── util/
│ │ │ ├── SignatureUtils.java
│ │ │ └── DateUtils.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── application-dev.yml
│ │ ├── application-prod.yml
│ │ └── db/migration/ # Flyway migrations
│ │ ├── V1__init_schema.sql
│ │ └── V2__seed_data.sql
│ └── test/
│ └── java/com/hiveops/mgmt/
│ ├── service/
│ ├── controller/
│ └── integration/
├── Dockerfile
├── docker-compose.yml
├── pom.xml (or build.gradle)
└── README.md
7.3 Database Schema (PostgreSQL)
Table: users
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
uuid UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(255),
role VARCHAR(50) NOT NULL DEFAULT 'USER',
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login_at TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_uuid ON users(uuid);
Table: licenses
CREATE TABLE licenses (
id BIGSERIAL PRIMARY KEY,
license_key VARCHAR(29) UNIQUE NOT NULL, -- XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
tier VARCHAR(50) NOT NULL, -- BASIC, PRO, ENTERPRISE
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE', -- ACTIVE, EXPIRED, REVOKED, TRIAL
max_activations INTEGER DEFAULT 3,
features JSONB, -- Array of feature names
issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP,
trial_ends_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
metadata JSONB -- Additional data (company, notes, etc.)
);
CREATE INDEX idx_licenses_key ON licenses(license_key);
CREATE INDEX idx_licenses_user ON licenses(user_id);
CREATE INDEX idx_licenses_status ON licenses(status);
Table: license_activations
CREATE TABLE license_activations (
id BIGSERIAL PRIMARY KEY,
license_id BIGINT NOT NULL REFERENCES licenses(id) ON DELETE CASCADE,
machine_id VARCHAR(255) NOT NULL,
platform VARCHAR(50), -- linux, windows, darwin
app_version VARCHAR(50),
activated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_validated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address INET,
hostname VARCHAR(255),
metadata JSONB,
UNIQUE(license_id, machine_id)
);
CREATE INDEX idx_activations_license ON license_activations(license_id);
CREATE INDEX idx_activations_machine ON license_activations(machine_id);
Table: global_settings
CREATE TABLE global_settings (
id BIGSERIAL PRIMARY KEY,
setting_key VARCHAR(255) UNIQUE NOT NULL,
setting_value TEXT NOT NULL,
setting_type VARCHAR(50) DEFAULT 'STRING', -- STRING, INTEGER, BOOLEAN, JSON
description TEXT,
editable BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_settings_key ON global_settings(setting_key);
Table: audit_logs
CREATE TABLE audit_logs (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE SET NULL,
action VARCHAR(100) NOT NULL, -- LICENSE_ACTIVATED, LOGIN, LICENSE_REVOKED, etc.
entity_type VARCHAR(50), -- LICENSE, USER, SETTING
entity_id BIGINT,
details JSONB,
ip_address INET,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_audit_user ON audit_logs(user_id);
CREATE INDEX idx_audit_action ON audit_logs(action);
CREATE INDEX idx_audit_created ON audit_logs(created_at);
7.4 Core Entities (JPA)
License.java
@Entity
@Table(name = "licenses")
public class License {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "license_key", unique = true, nullable = false, length = 29)
private String licenseKey;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Enumerated(EnumType.STRING)
private LicenseTier tier;
@Enumerated(EnumType.STRING)
private LicenseStatus status;
@Column(name = "max_activations")
private Integer maxActivations = 3;
@Type(JsonBinaryType.class)
@Column(columnDefinition = "jsonb")
private List<String> features;
@Column(name = "issued_at")
private LocalDateTime issuedAt;
@Column(name = "expires_at")
private LocalDateTime expiresAt;
@Column(name = "trial_ends_at")
private LocalDateTime trialEndsAt;
@OneToMany(mappedBy = "license", cascade = CascadeType.ALL)
private List<LicenseActivation> activations;
// Standard getters, setters, constructors
}
LicenseActivation.java
@Entity
@Table(name = "license_activations")
public class LicenseActivation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "license_id", nullable = false)
private License license;
@Column(name = "machine_id", nullable = false)
private String machineId;
private String platform;
@Column(name = "app_version")
private String appVersion;
@Column(name = "activated_at")
private LocalDateTime activatedAt;
@Column(name = "last_validated_at")
private LocalDateTime lastValidatedAt;
@Column(name = "ip_address")
private String ipAddress;
private String hostname;
// Standard getters, setters
}
User.java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private UUID uuid;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "password_hash", nullable = false)
private String passwordHash;
private String name;
@Enumerated(EnumType.STRING)
private UserRole role = UserRole.USER;
private Boolean enabled = true;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "last_login_at")
private LocalDateTime lastLoginAt;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<License> licenses;
// Standard getters, setters
}
7.5 REST API Endpoints
LicenseController.java
POST /api/v1/licenses/activate
@PostMapping("/activate")
public ResponseEntity<LicenseActivationResponse> activateLicense(
@RequestBody LicenseActivationRequest request
) {
// Validate license key format
// Check license exists and is active
// Check activation limit
// Create activation record
// Generate signature
// Return license data with features and signature
}
POST /api/v1/licenses/validate
@PostMapping("/validate")
public ResponseEntity<LicenseValidationResponse> validateLicense(
@RequestBody LicenseValidationRequest request
) {
// Find activation by license key + machine ID
// Update last_validated_at
// Check expiration
// Return updated license data
}
POST /api/v1/licenses/deactivate
@PostMapping("/deactivate")
public ResponseEntity<Void> deactivateLicense(
@RequestBody LicenseDeactivationRequest request,
@AuthenticationPrincipal UserDetails userDetails
) {
// Find activation
// Verify ownership or admin
// Delete activation record
// Audit log
}
GET /api/v1/licenses/{licenseKey}/status
@GetMapping("/{licenseKey}/status")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<LicenseStatusResponse> getLicenseStatus(
@PathVariable String licenseKey,
@AuthenticationPrincipal UserDetails userDetails
) {
// Verify ownership
// Return license status, tier, expiration, activation count
}
AuthController.java
POST /api/v1/auth/login
@PostMapping("/login")
public ResponseEntity<LoginResponse> login(
@RequestBody LoginRequest request
) {
// Authenticate credentials
// Generate JWT token
// Update last_login_at
// Return token + user profile + associated licenses
}
POST /api/v1/auth/register
@PostMapping("/register")
public ResponseEntity<UserProfileResponse> register(
@RequestBody UserRegistrationRequest request
) {
// Validate email uniqueness
// Hash password (BCrypt)
// Create user
// Return user profile
}
POST /api/v1/auth/refresh
@PostMapping("/refresh")
public ResponseEntity<TokenResponse> refreshToken(
@RequestHeader("Authorization") String refreshToken
) {
// Validate refresh token
// Generate new access token
// Return new token
}
UserController.java
GET /api/v1/users/profile
@GetMapping("/profile")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<UserProfileResponse> getProfile(
@AuthenticationPrincipal UserDetails userDetails
) {
// Return user profile + licenses
}
PUT /api/v1/users/profile
@PutMapping("/profile")
@PreAuthorize("hasRole('USER')")
public ResponseEntity<UserProfileResponse> updateProfile(
@RequestBody UserUpdateRequest request,
@AuthenticationPrincipal UserDetails userDetails
) {
// Update user name, email, etc.
// Return updated profile
}
GlobalSettingsController.java
GET /api/v1/settings
@GetMapping
public ResponseEntity<Map<String, Object>> getGlobalSettings() {
// Return public global settings
// e.g., allowed domains, feature flags, client update URLs
}
GET /api/v1/settings/{key}
@GetMapping("/{key}")
public ResponseEntity<GlobalSettingResponse> getSetting(
@PathVariable String key
) {
// Return specific setting value
}
PUT /api/v1/settings/{key} (Admin only)
@PutMapping("/{key}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<GlobalSettingResponse> updateSetting(
@PathVariable String key,
@RequestBody GlobalSettingUpdateRequest request
) {
// Update setting value
// Audit log
}
7.6 Service Layer
LicenseService.java
@Service
public class LicenseService {
public LicenseActivationResponse activateLicense(
String licenseKey,
String machineId,
String platform,
String appVersion,
String ipAddress
) {
// 1. Find license by key
// 2. Validate status (ACTIVE, not EXPIRED/REVOKED)
// 3. Check expiration date
// 4. Count current activations
// 5. Enforce max_activations limit
// 6. Create or update activation record
// 7. Generate HMAC signature
// 8. Return LicenseActivationResponse with:
// - license key, tier, features, expiresAt
// - signature (HMAC-SHA256)
// 9. Audit log
}
public LicenseValidationResponse validateLicense(
String licenseKey,
String machineId
) {
// 1. Find activation
// 2. Check license status
// 3. Check expiration
// 4. Update last_validated_at
// 5. Return updated license data
}
public void deactivateLicense(String licenseKey, String machineId, User user) {
// 1. Find activation
// 2. Verify ownership (user owns license OR user is admin)
// 3. Delete activation
// 4. Audit log
}
public String generateSignature(License license) {
// HMAC-SHA256(licenseKey + tier + expiresAt, SECRET_KEY)
String payload = license.getLicenseKey() + "|" +
license.getTier() + "|" +
license.getExpiresAt();
return HmacUtils.hmacSha256Hex(secretKey, payload);
}
public boolean verifySignature(License license, String signature) {
return generateSignature(license).equals(signature);
}
}
LicenseKeyGenerator.java
@Service
public class LicenseKeyGenerator {
public String generateLicenseKey(LicenseTier tier) {
// Format: TTTPP-RRRRR-RRRRR-CCCCC-RRRRR
// TTT: Tier code (BAS, PRO, ENT)
// PP: Product code (HB = HiveOps Browser)
// R: Random alphanumeric
// C: Checksum
String tierCode = tier == LicenseTier.BASIC ? "BAS" :
tier == LicenseTier.PRO ? "PRO" : "ENT";
String productCode = "HB";
String random1 = RandomStringUtils.randomAlphanumeric(5).toUpperCase();
String random2 = RandomStringUtils.randomAlphanumeric(5).toUpperCase();
String random3 = RandomStringUtils.randomAlphanumeric(5).toUpperCase();
String partialKey = tierCode + productCode + random1 + random2;
String checksum = calculateChecksum(partialKey);
return format(tierCode + productCode, random1, random2, checksum, random3);
}
private String calculateChecksum(String data) {
// Simple checksum using CRC or custom algorithm
int sum = 0;
for (char c : data.toCharArray()) {
sum += c;
}
return String.format("%05d", sum % 100000).substring(0, 5);
}
private String format(String p1, String p2, String p3, String p4, String p5) {
return p1 + "-" + p2 + "-" + p3 + "-" + p4 + "-" + p5;
}
}
7.7 Security Configuration
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().and()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/api/v1/licenses/activate").permitAll()
.requestMatchers("/api/v1/licenses/validate").permitAll()
.requestMatchers("/api/v1/settings").permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12);
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
JwtTokenProvider.java
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private long jwtExpiration = 86400000; // 24 hours
public String generateToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder()
.setSubject(user.getEmail())
.claim("userId", user.getId())
.claim("role", user.getRole())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUserEmailFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
}
7.8 Configuration Files
application.yml
spring:
application:
name: hiveops-mgmt
datasource:
url: jdbc:postgresql://localhost:5432/hiveops_mgmt
username: ${DB_USERNAME:hiveops}
password: ${DB_PASSWORD:changeme}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate # Use Flyway for migrations
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
server:
port: 8080
servlet:
context-path: /api
jwt:
secret: ${JWT_SECRET:your-secret-key-change-in-production}
expiration: 86400000 # 24 hours
license:
signature-secret: ${LICENSE_SECRET:your-license-secret-key}
max-activations-default: 3
offline-grace-period-days: 7
logging:
level:
com.hiveops.mgmt: DEBUG
org.springframework.security: DEBUG
application-prod.yml
spring:
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:hiveops_mgmt}
jpa:
show-sql: false
logging:
level:
com.hiveops.mgmt: INFO
org.springframework.security: WARN
server:
port: ${PORT:8080}
ssl:
enabled: ${SSL_ENABLED:false}
key-store: ${SSL_KEYSTORE_PATH}
key-store-password: ${SSL_KEYSTORE_PASSWORD}
7.9 Docker Configuration
Dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/hiveops-mgmt-*.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xmx512m -Xms256m"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: hiveops_mgmt
POSTGRES_USER: hiveops
POSTGRES_PASSWORD: changeme
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- hiveops-network
hiveops-mgmt:
build: .
ports:
- "8080:8080"
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: hiveops_mgmt
DB_USERNAME: hiveops
DB_PASSWORD: changeme
JWT_SECRET: your-production-jwt-secret
LICENSE_SECRET: your-production-license-secret
depends_on:
- postgres
networks:
- hiveops-network
volumes:
postgres_data:
networks:
hiveops-network:
driver: bridge
7.10 Maven Dependencies (pom.xml)
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JSON support -->
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-60</artifactId>
<version>2.21.1</version>
</dependency>
<!-- API Documentation -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
<!-- Utilities -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
8. Browser Client Updates (Connects to hiveops-mgmt)
The browser client API implementation from earlier sections remains mostly the same, but with these key changes:
api-client.js modifications:
- Base URL points to hiveops-mgmt:
https://api.hiveops.com/api/v1(adjust based on deployment) - All endpoints match the Spring Boot REST API paths
- Error handling matches Spring Boot error response format
9. API Server Requirements (Now hiveops-mgmt Implementation)
The external API must implement these endpoints (implemented in Spring Boot microservice above):
POST /v1/licenses/activate
- Validate license key
- Check activation limit (e.g., max 3 devices)
- Store machine ID
- Return license data with signature
- Status: 200 (success), 400 (invalid), 409 (max activations), 410 (expired)
POST /v1/licenses/validate
- Verify license still valid
- Check revocation status
- Return updated license data
- Status: 200 (valid), 403 (invalid/expired)
POST /v1/auth/login
- Authenticate user credentials
- Return JWT token + user profile + associated license
- Status: 200 (success), 401 (invalid credentials)
GET /v1/users/profile (Bearer token required)
- Return user profile and license list
- Status: 200 (success), 401 (unauthorized)
GET /v1/licenses/{key}/status (Bearer token required)
- Return license status, expiration, tier, features
- Return activation count (current/max)
- Status: 200 (success), 404 (not found)
POST /v1/licenses/deactivate
- Remove machine ID from activation list
- Free up activation slot
- Status: 200 (success), 404 (not found)
8. Testing Strategy
8.1 License Validation Testing
- Valid license key format accepted
- Invalid format rejected
- Expired license blocks app startup
- Valid license allows app to run
- Signature tampering detected and rejected
- Feature gates work correctly per tier
8.2 API Integration Testing
- Successful license activation
- Invalid key returns error
- Network error handled gracefully
- Timeout handled (30s)
- Offline mode uses local validation
- Online validation updates local cache
8.3 Authentication Testing
- Successful login stores token and profile
- Invalid credentials show error
- Logout clears session data
- Token expiration triggers re-login
8.4 Protocol Handler Testing
hiveops://open?url=https://test.hiveops.comopens URL- Invalid URLs blocked
- Single instance enforcement works
- Windows registry entries created on install
- Linux .desktop file includes MIME type
8.5 Feature Gate Testing
- Basic tier: Only basic features available
- Pro tier: Advanced features unlocked
- Enterprise tier: All features available
- Feature upgrade prompt shows correctly
8.6 Security Testing
- License data encryption verified
- Signature tampering detected
- Machine ID consistent across runs
- API authentication required
- Offline grace period enforced
9. Implementation Timeline
Phase 1: Microservice Backend (hiveops-mgmt) - Weeks 1-3
Week 1: Project Setup & Core Entities
- Create new Spring Boot 4 project (Maven/Gradle)
- Set up PostgreSQL database and docker-compose
- Configure Spring Security, JPA, Flyway
- Create database schema (migrations)
- Implement entity classes (User, License, LicenseActivation, GlobalSetting)
- Create repositories (Spring Data JPA)
- Write unit tests for entities
Week 2: Service Layer & Business Logic 8. Implement LicenseService (activate, validate, deactivate) 9. Implement AuthService (login, register, token management) 10. Implement UserService (profile, CRUD) 11. Implement GlobalSettingsService 12. Create LicenseKeyGenerator with checksum validation 13. Implement signature generation/verification (HMAC-SHA256) 14. Write service layer unit tests
Week 3: REST API & Security 15. Implement LicenseController with all endpoints 16. Implement AuthController (login, register, refresh) 17. Implement UserController (profile management) 18. Implement GlobalSettingsController 19. Configure JWT authentication filter 20. Add global exception handling 21. Set up API documentation (SpringDoc/Swagger) 22. Integration tests for all endpoints 23. Security testing (authentication, authorization)
Phase 2: Browser Client (hiveops-browser) - Weeks 4-6
Week 4: Client Infrastructure
24. Create license-manager.js with client-side validation
25. Create api-client.js connecting to hiveops-mgmt
26. Create auth-manager.js with token management
27. Update preload.js with new IPC APIs
28. Add IPC handlers to main.js
29. Test API communication with backend
30. Test license storage and validation
Week 5: UI Development
31. Create license-activation.html/js with dual-tab interface
32. Create license-management.html/js with status display
33. Create license.css for styling
34. Update settings.html with license info section
35. Update about.html to show tier
36. Test all UI flows with mock data
37. Integration with real backend API
Week 6: Features & Integration
38. Modify main.js startup to check license
39. Add background validation (24-hour interval)
40. Update config.js with feature gates
41. Implement feature-gated menu items
42. Add expiration warnings
43. Test end-to-end flows (client + backend)
Phase 3: Protocol Handler & Distribution - Week 7
Week 7: Protocol & Build
44. Update electron-builder.yml with protocol config
45. Create installer-script.nsh for Windows
46. Add protocol handling to main.js
47. Test protocol handler on all platforms
48. Full integration testing (client + backend + protocol)
49. Security testing and penetration tests
Phase 4: Deployment & Documentation - Week 8
Week 8: Deploy & Polish 50. Deploy hiveops-mgmt to production (Docker/K8s) 51. Configure production database (PostgreSQL) 52. Set up SSL/TLS certificates 53. Build and test installers (Windows/Linux) 54. Write user documentation 55. Write API documentation 56. Create admin panel for license management (optional) 57. Final QA and release
10. Critical Files Summary
hiveops-mgmt (Spring Boot Microservice - NEW REPOSITORY)
New Java Files (~30 files):
HiveOpsMgmtApplication.java- Main Spring Boot applicationSecurityConfig.java- Spring Security configurationJwtConfig.java- JWT configurationLicenseController.java- License REST endpointsAuthController.java- Authentication endpointsUserController.java- User management endpointsGlobalSettingsController.java- Settings endpointsLicenseService.java- License business logicAuthService.java- Authentication serviceUserService.java- User serviceLicenseKeyGenerator.java- Key generation logicGlobalSettingsService.java- Settings serviceLicense.java- License entity (JPA)User.java- User entityLicenseActivation.java- Activation entityGlobalSetting.java- Setting entityJwtTokenProvider.java- JWT token handlingJwtAuthenticationFilter.java- JWT filterGlobalExceptionHandler.java- Exception handling- DTO classes (requests/responses)
- Repository interfaces
- Utility classes
Configuration Files:
application.yml- Main configurationapplication-prod.yml- Production configpom.xml- Maven dependenciesDockerfile- Container configdocker-compose.yml- Local developmentV1__init_schema.sql- Flyway migrationinstaller-script.nsh- Windows protocol registration
hiveops-browser (Electron Client - EXISTING REPOSITORY)
New Files (9 total):
src/main/license-manager.js- License validation coresrc/main/api-client.js- External API communicationsrc/main/auth-manager.js- User authenticationsrc/renderer/license-activation.html- Activation UIsrc/renderer/license-activation.js- Activation logicsrc/renderer/license-management.html- Management UIsrc/renderer/license-management.js- Management logicsrc/renderer/license.css- License UI stylesinstaller-script.nsh- Windows protocol registration
Modified Files (6 total):
src/main/main.js- Add license checks, protocol handler, IPC handlerssrc/main/config.js- Add feature gates, license-aware URL validationsrc/main/preload.js- Expose license/auth APIssrc/renderer/settings.html- Add license info sectionconfig/default-config.json- Add apiBaseUrlelectron-builder.yml- Add protocol registrationpackage.json- Update version to 2.0.0
11. Verification Checklist
Backend (hiveops-mgmt) Verification
- Spring Boot application starts without errors
- Database schema created via Flyway
- PostgreSQL connection established
- Swagger UI accessible at
/swagger-ui.html - License activation endpoint works
- License validation endpoint works
- User login returns JWT token
- JWT authentication filter validates tokens
- License signature verification works
- Activation limit enforcement works (max 3 devices)
- License expiration checked correctly
- Global settings CRUD operations work
- Audit logs created for actions
- API error handling returns proper status codes
- Docker container builds and runs
- docker-compose brings up full stack
Client (hiveops-browser) Verification
- Fresh install shows license activation screen
- Valid license key activates successfully via API
- Invalid license key shows error from backend
- User login works and stores profile + token
- Main window opens after successful activation
- License expiration is enforced
- Feature gates work per tier (Basic/Pro/Enterprise)
- Protocol handler opens app with URL (hiveops://)
- Settings page shows license info
- License management shows all data from backend
- Deactivation calls backend API and quits
- Offline operation works (local validation)
- Background validation (24hr) calls backend
- Network errors handled gracefully
- Build creates installers with protocol registration
- Windows installer registers protocol in registry
- Linux .deb includes MIME type associations
Integration Testing
- End-to-end: Activate license from fresh install
- End-to-end: Login with user account
- End-to-end: Validate license online
- End-to-end: Deactivate license
- Backend handles concurrent activations
- Backend enforces activation limits
- Client handles backend downtime gracefully
- Offline mode works for 7 days
- Expired license blocks app startup
- Revoked license detected on validation
12. Deployment Guide
Backend Deployment (hiveops-mgmt)
Infrastructure Requirements:
- Server: 2 vCPU, 4GB RAM minimum
- Database: PostgreSQL 16+ (separate instance recommended)
- Storage: 20GB minimum
- Network: SSL/TLS certificate for HTTPS
Deployment Options:
Option 1: Docker (Recommended)
# Build and run with docker-compose
docker-compose up -d
# Or deploy to container orchestration
docker build -t hiveops-mgmt:latest .
docker push registry.hiveops.com/hiveops-mgmt:latest
Option 2: Kubernetes
# Create deployment, service, ingress
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
Option 3: Traditional Server (JAR)
# Build JAR
mvn clean package -DskipTests
# Run with production profile
java -jar target/hiveops-mgmt-1.0.0.jar \
--spring.profiles.active=prod \
--server.port=8080
Environment Variables (Production):
DB_HOST=postgres.hiveops.com
DB_PORT=5432
DB_NAME=hiveops_mgmt
DB_USERNAME=hiveops_user
DB_PASSWORD=<secure-password>
JWT_SECRET=<generate-secure-secret>
LICENSE_SECRET=<generate-secure-secret>
SSL_ENABLED=true
SSL_KEYSTORE_PATH=/etc/ssl/hiveops.jks
SSL_KEYSTORE_PASSWORD=<keystore-password>
Database Setup:
-- Create database and user
CREATE DATABASE hiveops_mgmt;
CREATE USER hiveops_user WITH ENCRYPTED PASSWORD 'secure-password';
GRANT ALL PRIVILEGES ON DATABASE hiveops_mgmt TO hiveops_user;
-- Flyway will handle schema migrations automatically
SSL/TLS Certificate:
# Generate Let's Encrypt certificate
certbot certonly --standalone -d api.hiveops.com
# Or use existing certificate
# Configure in application-prod.yml
Post-Deployment Checks:
- Health check:
curl https://api.hiveops.com/api/actuator/health - Swagger UI:
https://api.hiveops.com/api/swagger-ui.html - Test license activation endpoint
- Test user login endpoint
- Verify database connectivity
- Check logs for errors
Client Deployment (hiveops-browser)
Build Configuration:
- Update
config/default-config.json:
{
"apiBaseUrl": "https://api.hiveops.com/api/v1"
}
- Update version in
package.json:
{
"version": "2.0.0"
}
- Build installers:
# Build all platforms
./build-all.sh
# Or individually
npm run build:linux
npm run build:win
Distribution:
- Windows:
dist/customer-packages/windows/HiveOps Browser Setup 2.0.0.exe - Linux:
dist/customer-packages/linux/hiveops-browser_2.0.0_amd64.deb - AppImage:
dist/customer-packages/linux/HiveOps Browser-2.0.0.AppImage
Deployment Prerequisites:
- ✅ Backend API deployed and accessible
- ✅ Database populated with initial data
- ✅ SSL certificates configured
- ✅ DNS records set (api.hiveops.com)
- ✅ Monitoring and logging configured
- ✅ Admin user created for license management
- ✅ Initial license keys generated for testing
- ✅ Documentation prepared
- ✅ Support channel established
13. Post-Launch Monitoring
Backend Monitoring (hiveops-mgmt):
- API response times and latency
- Database query performance
- License activation/validation success rate
- Failed authentication attempts (security)
- API error rates (4xx, 5xx)
- Active license count by tier
- Server resource usage (CPU, memory, disk)
- Database connection pool utilization
Client Monitoring:
- License activation success rate
- Offline validation failures
- Feature gate usage by tier
- Protocol handler usage
- Crash reports and errors
- User feedback on licensing UX
- Update installation rates
Recommended Tools:
- Backend: Spring Boot Actuator + Prometheus + Grafana
- Database: PostgreSQL pg_stat_statements
- Logging: ELK Stack (Elasticsearch, Logstash, Kibana)
- Alerts: PagerDuty or similar
- Client: Sentry for error tracking
Implementation Summary
Two-Component Architecture
┌─────────────────────────────────────┐
│ HiveOps Browser (Electron) │
│ - License validation UI │
│ - Protocol handler (hiveops://) │
│ - Feature gates (Basic/Pro/Ent) │
│ - Offline validation (7-day grace)│
│ - Background sync (24hr) │
└─────────────┬───────────────────────┘
│ HTTPS/REST API
│ (JWT Authentication)
▼
┌─────────────────────────────────────┐
│ hiveops-mgmt (Spring Boot 4) │
│ - License activation/validation │
│ - User authentication (JWT) │
│ - License key generation │
│ - Feature management │
│ - Global settings │
│ - Audit logging │
└─────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ PostgreSQL Database │
│ - users │
│ - licenses │
│ - license_activations │
│ - global_settings │
│ - audit_logs │
└─────────────────────────────────────┘
Key Features
Licensing System:
- Three-tier license model (Basic, Pro, Enterprise)
- Machine-based activation (max 3 devices per license)
- Online validation with offline grace period (7 days)
- License expiration and trial support
- Anti-tamper signature verification (HMAC-SHA256)
- Revocation support
User Management:
- User authentication with JWT
- User profile with associated licenses
- Role-based access control (USER, ADMIN)
- Password hashing (BCrypt)
Protocol Handling:
- Custom protocol:
hiveops:// - Domain-based:
*.hiveops.com - Platform-specific registration (Windows registry, Linux MIME types)
- Deep linking support
Security:
- Encrypted license storage (electron-store)
- JWT authentication (Spring Security)
- HTTPS-only API communication
- SQL injection prevention (parameterized queries)
- XSS protection (CSP headers)
- Audit logging for all actions
Total Effort Estimate
- Backend Development: 3 weeks
- Client Development: 3 weeks
- Integration & Testing: 1 week
- Deployment & Documentation: 1 week
- Total: 8 weeks (2 months)
Success Metrics
- ✅ License activation success rate > 95%
- ✅ API response time < 200ms (p95)
- ✅ Zero license bypass incidents
- ✅ Offline operation works seamlessly
- ✅ Protocol handler registration success > 90%
- ✅ User satisfaction score > 4/5