# 🚀 Deployment Guide > **Production deployment and operations** for Ario's web application, desktop app, and Progressive Web App across multiple platforms and environments. ## Table of Contents - [[#Deployment Options]] - [[#Production Build Process]] - [[#Environment Configuration]] - [[#Platform-Specific Deployment]] - [[#Monitoring & Maintenance]] - [[#Security Considerations]] ## Deployment Options ### **Web Application (SPA)** - **Static Hosting** - Deploy to CDN or static hosting service - **Traditional Web Server** - Nginx, Apache, or similar - **Cloud Platforms** - Vercel, Netlify, Cloudflare Pages - **Container Deployment** - Docker with web server ### **Progressive Web App (PWA)** - **Service Worker** - Offline capabilities and caching - **App Manifest** - Installation and app-like experience - **Push Notifications** - Real-time updates (future feature) - **Background Sync** - Offline data synchronization ### **Desktop Application** - **Electron** - Cross-platform desktop packaging - **Platform-Specific** - Windows MSI, macOS DMG, Linux AppImage/Snap - **Auto-Update** - Seamless application updates - **Code Signing** - Security and trust verification ### **Self-Hosted** - **Full Control** - Complete control over infrastructure - **Privacy** - No third-party hosting dependencies - **Custom Configuration** - Tailored environment variables and settings - **Local Development** - Internal network deployment ## Production Build Process ### **Build Commands** ```bash # Standard web build npm run build # Build with analysis npm run analyze # Electron desktop build npm run electron:build # Platform-specific builds npm run build:win # Windows npm run build:mac # macOS npm run build:linux # Linux ``` ### **Build Configuration** #### **Vite Production Config** ```typescript // vite.config.ts - Production optimizations export default defineConfig({ build: { target: 'es2020', minify: 'terser', sourcemap: false, // Disable in production chunkSizeWarningLimit: 600, rollupOptions: { output: { manualChunks: { 'vue-vendor': ['vue', 'vue-router', 'pinia'], 'ui-vendor': ['@headlessui/vue', '@heroicons/vue'], 'nostr-vendor': ['nostr-tools'], 'crypto-vendor': ['crypto-js'] } } } } }) ``` #### **Build Optimization** - **Tree Shaking** - Remove unused code - **Code Splitting** - Lazy load modules and routes - **Asset Optimization** - Compress images and fonts - **Bundle Analysis** - Monitor bundle size and dependencies ### **Build Output Structure** ``` dist/ ├── index.html # Entry point ├── assets/ # Static assets with hashed names │ ├── index-[hash].js # Main application bundle │ ├── vendor-[hash].js # Vendor dependencies │ └── [module]-[hash].js # Module-specific bundles ├── icons/ # PWA icons ├── manifest.json # PWA manifest └── sw.js # Service worker ``` ## Environment Configuration ### **Environment Variables** #### **Production Environment (.env.production)** ```bash # Application Configuration VITE_APP_NAME="Ario" VITE_APP_VERSION="1.0.0" VITE_BASE_URL="/" # Nostr Configuration VITE_NOSTR_RELAYS='["wss://relay.damus.io","wss://nos.lol","wss://relay.snort.social"]' VITE_ADMIN_PUBKEYS='["admin_pubkey_1","admin_pubkey_2"]' # Lightning Configuration (if using LNbits) VITE_LNBITS_URL="https://your-lnbits-instance.com" VITE_LNBITS_ADMIN_KEY="" # Only for invoice creation # Security & Performance VITE_DEBUG=false VITE_ENABLE_PWA=true VITE_ENABLE_ANALYTICS=true # Optional Features VITE_ENABLE_NOTIFICATIONS=true VITE_MAX_RELAY_CONNECTIONS=10 VITE_EVENT_CACHE_SIZE=1000 ``` #### **Configuration Validation** ```typescript // src/config/production.ts export const productionConfig = { app: { name: import.meta.env.VITE_APP_NAME || 'Ario', version: import.meta.env.VITE_APP_VERSION || '1.0.0', baseUrl: import.meta.env.VITE_BASE_URL || '/', }, nostr: { relays: JSON.parse(import.meta.env.VITE_NOSTR_RELAYS || '[]'), adminPubkeys: JSON.parse(import.meta.env.VITE_ADMIN_PUBKEYS || '[]'), maxConnections: Number(import.meta.env.VITE_MAX_RELAY_CONNECTIONS) || 10, }, features: { debug: import.meta.env.VITE_DEBUG === 'true', pwa: import.meta.env.VITE_ENABLE_PWA === 'true', notifications: import.meta.env.VITE_ENABLE_NOTIFICATIONS === 'true', } } // Validate required configuration if (!productionConfig.nostr.relays.length) { throw new Error('VITE_NOSTR_RELAYS must be configured') } ``` ### **Security Configuration** #### **Content Security Policy (CSP)** ```html ``` #### **Security Headers (nginx example)** ```nginx # nginx.conf security headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Referrer-Policy "strict-origin-when-cross-origin"; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()"; ``` ## Platform-Specific Deployment ### **Web Hosting - Vercel** #### **vercel.json Configuration** ```json { "buildCommand": "npm run build", "outputDirectory": "dist", "framework": "vite", "rewrites": [ { "source": "/((?!api/).*)", "destination": "/index.html" } ], "headers": [ { "source": "/(.*)", "headers": [ { "key": "X-Frame-Options", "value": "DENY" }, { "key": "X-Content-Type-Options", "value": "nosniff" } ] } ] } ``` #### **Deployment Steps** ```bash # 1. Install Vercel CLI npm i -g vercel # 2. Deploy to Vercel vercel --prod # 3. Set environment variables in Vercel dashboard # - VITE_NOSTR_RELAYS # - VITE_ADMIN_PUBKEYS # - Other configuration variables ``` ### **Web Hosting - Netlify** #### **netlify.toml Configuration** ```toml [build] command = "npm run build" publish = "dist" [[redirects]] from = "/*" to = "/index.html" status = 200 [build.environment] NODE_VERSION = "18" [[headers]] for = "/*" [headers.values] X-Frame-Options = "DENY" X-XSS-Protection = "1; mode=block" X-Content-Type-Options = "nosniff" ``` ### **Self-Hosted - Docker** #### **Dockerfile** ```dockerfile # Multi-stage build for production FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . RUN npm run build # Production stage FROM nginx:alpine # Copy built application COPY --from=builder /app/dist /usr/share/nginx/html # Copy nginx configuration COPY docker/nginx.conf /etc/nginx/nginx.conf # Expose port EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` #### **Docker Compose** ```yaml # docker-compose.yml version: '3.8' services: ario-web: build: . ports: - "80:80" environment: - VITE_NOSTR_RELAYS=["wss://relay.damus.io"] - VITE_ADMIN_PUBKEYS=["your_admin_pubkey"] volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro restart: unless-stopped ``` ### **Desktop Application** #### **Electron Forge Configuration** ```javascript // forge.config.js module.exports = { packagerConfig: { name: 'Ario', executableName: 'ario', icon: './src/assets/icon', asar: true, // Code signing (production) osxSign: { identity: process.env.APPLE_IDENTITY, 'hardened-runtime': true, entitlements: './entitlements.plist', 'entitlements-inherit': './entitlements.plist' }, osxNotarize: { tool: 'notarytool', appleId: process.env.APPLE_ID, appleIdPassword: process.env.APPLE_PASSWORD, teamId: process.env.APPLE_TEAM_ID } }, makers: [ { name: '@electron-forge/maker-squirrel', config: { name: 'ario', setupIcon: './src/assets/icon.ico' } }, { name: '@electron-forge/maker-dmg', config: { format: 'ULFO', icon: './src/assets/icon.icns' } }, { name: '@electron-forge/maker-deb', config: { options: { maintainer: 'Ario Team', homepage: 'https://ario.app' } } } ], publishers: [ { name: '@electron-forge/publisher-github', config: { repository: { owner: 'your-org', name: 'ario' }, prerelease: false } } ] } ``` #### **Auto-Update Configuration** ```typescript // electron/main.ts - Auto-updater setup import { autoUpdater } from 'electron-updater' if (!app.isPackaged) { autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml') } autoUpdater.checkForUpdatesAndNotify() autoUpdater.on('update-available', () => { dialog.showMessageBox(mainWindow, { type: 'info', title: 'Update Available', message: 'A new version is available. It will be downloaded in the background.', buttons: ['OK'] }) }) ``` ## Monitoring & Maintenance ### **Performance Monitoring** #### **Web Vitals Tracking** ```typescript // src/utils/analytics.ts import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals' export function initPerformanceMonitoring() { if (import.meta.env.VITE_ENABLE_ANALYTICS) { getCLS(sendToAnalytics) getFID(sendToAnalytics) getFCP(sendToAnalytics) getLCP(sendToAnalytics) getTTFB(sendToAnalytics) } } function sendToAnalytics(metric: any) { // Send to your analytics service console.log('Performance metric:', metric) } ``` #### **Application Health Monitoring** ```typescript // src/services/HealthMonitor.ts export class HealthMonitor extends BaseService { private healthStatus = ref({ relayConnections: 0, lastEventReceived: null as Date | null, memoryUsage: 0, errorCount: 0 }) async initialize(): Promise { setInterval(() => this.checkHealth(), 30000) // Every 30 seconds } private checkHealth(): void { const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) this.healthStatus.value = { relayConnections: relayHub.connectedRelays.value.length, lastEventReceived: this.getLastEventTime(), memoryUsage: this.getMemoryUsage(), errorCount: this.getErrorCount() } // Alert if critical issues detected if (this.healthStatus.value.relayConnections === 0) { console.warn('No relay connections available') } } } ``` ### **Error Tracking** #### **Global Error Handler** ```typescript // src/utils/errorHandler.ts export function setupGlobalErrorHandler() { window.addEventListener('error', (event) => { console.error('Global error:', event.error) // Send to error tracking service if (import.meta.env.VITE_ERROR_TRACKING) { sendErrorToService(event.error) } }) window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled promise rejection:', event.reason) sendErrorToService(event.reason) }) } ``` ### **Logging Strategy** #### **Production Logging** ```typescript // src/utils/logger.ts class Logger { private shouldLog(level: LogLevel): boolean { if (import.meta.env.PROD && level === 'debug') return false return true } info(message: string, ...args: unknown[]): void { if (this.shouldLog('info')) { console.log(`[INFO] ${message}`, ...args) } } error(message: string, error?: Error, ...args: unknown[]): void { if (this.shouldLog('error')) { console.error(`[ERROR] ${message}`, error, ...args) // Send to error tracking in production if (import.meta.env.PROD && error) { this.sendToErrorTracking(message, error) } } } private sendToErrorTracking(message: string, error: Error): void { // Implementation for error tracking service } } export const logger = new Logger() ``` ## Security Considerations ### **Client-Side Security** #### **Key Storage Security** ```typescript // src/utils/keyStorage.ts export class SecureKeyStorage { private static readonly STORAGE_KEY = 'ario_encrypted_keys' static async storeEncryptedKey(privateKey: string, passphrase: string): Promise { const encrypted = await this.encrypt(privateKey, passphrase) localStorage.setItem(this.STORAGE_KEY, encrypted) } static async retrieveDecryptedKey(passphrase: string): Promise { const encrypted = localStorage.getItem(this.STORAGE_KEY) if (!encrypted) return null try { return await this.decrypt(encrypted, passphrase) } catch { return null // Invalid passphrase } } private static async encrypt(data: string, passphrase: string): Promise { // Use Web Crypto API for encryption const encoder = new TextEncoder() const key = await window.crypto.subtle.importKey( 'raw', encoder.encode(passphrase), { name: 'PBKDF2' }, false, ['deriveKey'] ) // Implementation details... return encryptedData } } ``` #### **Input Validation** ```typescript // src/utils/validation.ts export const validators = { nostrPublicKey: (pubkey: string): boolean => { return /^[0-9a-f]{64}$/i.test(pubkey) }, nostrPrivateKey: (privkey: string): boolean => { return /^[0-9a-f]{64}$/i.test(privkey) }, lightningInvoice: (invoice: string): boolean => { return /^(lnbc|lntb|lnbcrt)[0-9]+[munp]?[0-9a-z]+$/i.test(invoice) }, sanitizeContent: (content: string): string => { // Sanitize user-generated content return content .replace(/)<[^<]*)*<\/script>/gi, '') .replace(/javascript:/gi, '') .trim() } } ``` ### **Deployment Security** #### **HTTPS Enforcement** ```nginx # nginx.conf - Force HTTPS server { listen 80; server_name ario.app www.ario.app; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name ario.app www.ario.app; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/private.key; # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } } ``` ### **Privacy Protection** #### **Data Minimization** - **No Server Storage** - All user data stored client-side - **Ephemeral Sessions** - No persistent server-side sessions - **Optional Analytics** - Analytics can be disabled by users - **Relay Privacy** - Users can configure their own relays #### **User Privacy Controls** ```typescript // src/services/PrivacyService.ts export class PrivacyService extends BaseService { private settings = ref({ shareAnalytics: false, shareErrorReports: false, allowLocationTracking: false, publicProfile: false }) updatePrivacySettings(updates: Partial): void { this.settings.value = { ...this.settings.value, ...updates } // Apply privacy settings immediately if (!this.settings.value.shareAnalytics) { this.disableAnalytics() } if (!this.settings.value.shareErrorReports) { this.disableErrorReporting() } } } ``` ## See Also ### Configuration Documentation - **[[configuration|⚙️ Environment Configuration]]** - Detailed configuration options - **[[pwa-setup|📱 PWA Configuration]]** - Progressive Web App setup - **[[electron|🖥️ Desktop App Packaging]]** - Electron configuration and distribution ### Operations Documentation - **[[../04-development/index|💻 Development Guide]]** - Development environment setup - **[[../05-api-reference/index|📡 API Reference]]** - External service integrations - **[[../01-architecture/index|🏗️ Architecture Overview]]** - System architecture principles --- **Tags:** #deployment #production #security #monitoring #pwa #electron **Last Updated:** 2025-09-06 **Author:** Development Team