# 🚀 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
<!-- In index.html for production -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  connect-src 'self' wss: ws: https:;
  img-src 'self' data: https:;
  font-src 'self' data:;
  manifest-src 'self';
">
```

#### **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<void> {
    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<void> {
    const encrypted = await this.encrypt(privateKey, passphrase)
    localStorage.setItem(this.STORAGE_KEY, encrypted)
  }
  
  static async retrieveDecryptedKey(passphrase: string): Promise<string | null> {
    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<string> {
    // 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\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/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<PrivacySettings>): 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