- Reorganize all markdown documentation into structured docs/ folder - Create 7 main documentation categories (00-overview through 06-deployment) - Add comprehensive index files for each category with cross-linking - Implement Obsidian-compatible [[link]] syntax throughout - Move legacy/deprecated documentation to archive folder - Establish documentation standards and maintenance guidelines - Provide complete coverage of modular architecture, services, and deployment - Enable better navigation and discoverability for developers and contributors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
16 KiB
16 KiB
🚀 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
# 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
// 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)
# 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
// 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)
<!-- 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.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
{
"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
# 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
[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
# 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
# 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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.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
// 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 - Detailed configuration options
- pwa-setup - Progressive Web App setup
- electron - Electron configuration and distribution
Operations Documentation
- ../04-development/index - Development environment setup
- ../05-api-reference/index - External service integrations
- ../01-architecture/index - System architecture principles
Tags: #deployment #production #security #monitoring #pwa #electron
Last Updated: 2025-09-06
Author: Development Team