Version: 1.0.0 Last Updated: 2025-10-25
- Build Instructions
- Environment Configuration
- Hosting Options
- SSL/HTTPS Requirements
- Security Headers
- Service Worker Configuration
- Domain Setup
- Performance Optimization
- Monitoring & Analytics
- Troubleshooting Deployment
- Node.js >= 20.0.0
- NPM >= 10.0.0
- Git
# Clone repository
git clone https://github.com/yourusername/trustvault-pwa.git
cd trustvault-pwa
# Install dependencies
npm install
# Start development server (port 3000)
npm run dev
# Run type checking
npm run type-check
# Run tests
npm run test
# Run linter
npm run lint# Build for production
npm run build
# Output directory: dist/
# - dist/assets/ - Optimized JS, CSS chunks
# - dist/index.html - Entry point
# - dist/manifest.json - PWA manifest
# - dist/sw.js - Service worker
# - dist/*.png - PWA icons# Build and preview locally
npm run build
npm run preview
# Opens preview server at http://localhost:4173# Check bundle sizes
ls -lh dist/assets/
# Expected sizes (gzipped):
# - Main chunk: ~150-200 KB
# - React vendor: ~130-150 KB
# - MUI vendor: ~200-250 KB
# - Security vendor: ~50-70 KB
# - Total: <600 KB (target)
# Run Lighthouse audit
npm run lighthouse
# Target scores (all >90):
# - Performance: 90+
# - Accessibility: 90+
# - Best Practices: 90+
# - SEO: 90+
# - PWA: 100TrustVault currently has no backend, so environment variables are minimal.
Optional:
# .env.production (if needed for analytics/monitoring)
VITE_APP_VERSION=1.0.0
VITE_ANALYTICS_ID=G-XXXXXXXXXX # Google Analytics (optional)Important: Never commit .env files with secrets. TrustVault is client-only, so no API keys should be exposed.
Edit vite.config.ts for build customization:
export default defineConfig({
base: '/', // Change if deploying to subdirectory
build: {
outDir: 'dist',
sourcemap: false, // Set true for debugging production issues
minify: 'terser',
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks: {
// Customize code splitting here
}
}
}
}
});Pros: Automatic HTTPS, CDN, Git integration, zero config Free Tier: Unlimited static sites
Deployment:
# Install Vercel CLI
npm install -g vercel
# Deploy
npm run build
vercel --prod
# Or connect GitHub repo for automatic deploymentsVercel Configuration (vercel.json):
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "dist"
}
}
],
"routes": [
{
"src": "/(.*)",
"dest": "/index.html"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
},
{
"key": "Permissions-Policy",
"value": "geolocation=(), microphone=(), camera=()"
},
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
}
]
}
]
}Pros: Git integration, CDN, redirects, free SSL Free Tier: 100 GB bandwidth/month
Deployment:
# Install Netlify CLI
npm install -g netlify-cli
# Deploy
npm run build
netlify deploy --prod
# Or connect GitHub repoNetlify Configuration (netlify.toml):
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Content-Type-Options = "nosniff"
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Content-Security-Policy = "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"Pros: Free, Git-integrated, simple Cons: No custom headers (security limitation)
Deployment (if you must):
# Install gh-pages
npm install -D gh-pages
# Add script to package.json
"scripts": {
"deploy:gh-pages": "npm run build && gh-pages -d dist"
}
# Deploy
npm run deploy:gh-pagesGitHub Pages Configuration:
- Repository Settings → Pages → Source: gh-pages branch
- Custom domain: Add CNAME file to public/ directory
Pros: Full control, custom headers, no vendor lock-in Cons: Requires server management, SSL setup
Nginx Configuration (/etc/nginx/sites-available/trustvault):
server {
listen 443 ssl http2;
server_name trustvault.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/trustvault.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/trustvault.yourdomain.com/privkey.pem;
root /var/www/trustvault/dist;
index index.html;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
# SPA routing
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Service worker (no cache)
location = /sw.js {
add_header Cache-Control "no-cache";
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name trustvault.yourdomain.com;
return 301 https://$server_name$request_uri;
}SSL Certificate (Let's Encrypt):
# Install certbot
sudo apt install certbot python3-certbot-nginx
# Get certificate
sudo certbot --nginx -d trustvault.yourdomain.com
# Auto-renewal (cron)
sudo certbot renew --dry-runTrustVault requires HTTPS for:
- Service Worker registration (PWA feature)
- WebCrypto API (encryption)
- WebAuthn (biometric authentication)
- Clipboard API (copy/paste)
- Security best practices
Exception: localhost (development only)
Free Options:
- Let's Encrypt (self-hosted)
- Cloudflare (proxy)
- Vercel/Netlify (automatic)
Paid Options:
- DigiCert, GlobalSign (for organizations)
- Certificate valid and not expired
- Redirect HTTP → HTTPS (301)
- HSTS header enabled
- TLS 1.2+ only (disable TLS 1.0/1.1)
- Strong cipher suites
- OCSP stapling enabled
Test SSL:
- https://www.ssllabs.com/ssltest/
- Target: A or A+ rating
Already configured in vite.config.ts for development:
headers: {
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; ..."
}For production hosting, configure in:
- Vercel:
vercel.json - Netlify:
netlify.tomlor_headersfile - Nginx:
nginx.conf - Apache:
.htaccess
Current CSP:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: blob:;
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
Explanation:
default-src 'self'- Only load resources from same origin'wasm-unsafe-eval'- Required for Argon2 WASM module'unsafe-inline'- Required for Material-UI styles (can't be avoided)fonts.googleapis.com- Material-UI fontsdata: blob:- For PWA icons and generated content
Customization: If you add external services (analytics, CDN), update CSP:
connect-src 'self' https://analytics.google.com;Online Tools:
- https://securityheaders.com/
- Target: A or A+ rating
Common Issues:
- Missing CSP → Add to hosting config
'unsafe-inline'flagged → Material-UI limitation, acceptable- HSTS not set → Add:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Current configuration (vite.config.ts):
VitePWA({
registerType: 'autoUpdate',
devOptions: { enabled: true },
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2,wasm}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\./,
handler: 'CacheFirst',
options: {
cacheName: 'fonts',
expiration: { maxAgeSeconds: 60 * 60 * 24 * 365 }
}
}
]
}
})Auto-Update (Current):
- New version automatically installs
- User prompted to reload
- UpdateAvailableSnackbar shows notification
Manual Update (Alternative):
Change registerType: 'prompt' to give user control
- Installation - Assets cached on first visit
- Activation - SW takes control of page
- Fetch - Intercepts network requests, serves from cache
- Update - New version detected, prompts user
Chrome DevTools:
- Open DevTools → Application tab
- Service Workers section
- Check status, update, unregister
Common Issues:
- SW not registering → Check HTTPS
- Old SW stuck → Unregister and hard refresh
- Updates not applying → Check
skipWaitingsetting
For Custom Domain:
Type Name Value TTL
A trustvault <your-server-ip> 3600
CNAME www.trustvault trustvault.yourdomain.com. 3600
For Vercel/Netlify:
Follow platform-specific instructions:
- Vercel: Dashboard → Domain Settings → Add Domain
- Netlify: Dashboard → Domain Management → Add Domain
Root Domain (Recommended):
https://trustvault.com- Better for PWA install prompts
- Cleaner branding
Subdomain:
https://vault.example.com- Good for adding to existing site
After DNS setup:
- Wait for propagation (up to 48 hours, usually <1 hour)
- Check with:
nslookup trustvault.yourdomain.com - Test HTTPS:
curl -I https://trustvault.yourdomain.com
Current Strategy:
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'mui-vendor': ['@mui/material', '@mui/icons-material'],
'security-vendor': ['@noble/hashes'],
'storage-vendor': ['dexie']
}Benefits:
- Separate vendor chunks for better caching
- Lazy-loaded routes (already implemented)
- Total bundle size reduced by ~40%
Routes:
const DashboardPage = lazy(() => import('./pages/DashboardPage'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));Heavy Components:
- Password Generator (loaded on demand)
- TOTP components (only if credential has TOTP)
- Biometric components (only if available)
Images:
- PWA icons: Optimized PNGs at required sizes
- Use WebP with PNG fallback for screenshots
Fonts:
- Material-UI uses Google Fonts (cached by SW)
- Subset fonts if possible
CSS:
- Minified in production
- Critical CSS inlined (Vite default)
Static Assets:
CacheFirst- Fonts, images (1 year cache)StaleWhileRevalidate- JS, CSS chunks
Dynamic Content:
- All data in IndexedDB (offline-first)
- No API calls (fully client-side)
Target Scores:
- Performance: 90+
- Accessibility: 90+
- Best Practices: 90+
- SEO: 90+
- PWA: 100
Common Improvements:
- Enable compression (gzip/brotli)
- Use HTTP/2
- Minimize main-thread work
- Reduce JavaScript execution time
Sentry Integration:
npm install @sentry/react// src/main.tsx
import * as Sentry from '@sentry/react';
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
environment: import.meta.env.MODE,
tracesSampleRate: 0.1,
beforeSend(event) {
// Filter sensitive data
delete event.user;
delete event.request?.data;
return event;
}
});Google Analytics 4:
// src/utils/analytics.ts
declare global {
interface Window {
gtag?: (...args: any[]) => void;
}
}
export function trackPageView(path: string) {
window.gtag?.('config', import.meta.env.VITE_GA_ID, {
page_path: path,
anonymize_ip: true
});
}
export function trackEvent(category: string, action: string) {
window.gtag?.('event', action, { event_category: category });
}Events to Track:
- User signups (without email)
- Credential created/updated/deleted counts
- PWA installs
- Export/import operations
- Biometric enabled
What NOT to Track:
- Passwords (obviously)
- Email addresses
- Credential titles/usernames
- Any decrypted data
Self-Hosted:
- Uptime Kuma (open source)
- Statping
SaaS:
- UptimeRobot (free tier)
- Pingdom
- StatusCake
What to Monitor:
- HTTPS endpoint reachability
- SSL certificate expiration
- Response time (<500ms target)
- Service worker updates
Error: Cannot find module '@noble/hashes'
Solution: npm install and ensure all dependencies installed
Error: TypeScript errors in build
Solution: Run npm run type-check first, fix all errors
Error: WASM loading failed
Solution: Ensure vite-plugin-wasm installed and configured
Error: SW not registering in production Solution: Ensure HTTPS enabled, check browser console
Error: Update not applying Solution: Unregister SW in DevTools, hard refresh
Error: Offline mode not working Solution: Check workbox caching patterns, ensure files cached
Error: 404 on direct URL access Solution: Configure SPA routing (see hosting-specific configs)
Error: Assets not loading
Solution: Check base path in vite.config.ts
Error: Lighthouse score <90 Solution:
- Enable compression
- Check bundle sizes (
npm run build) - Lazy load more components
- Optimize images
Error: Slow initial load Solution:
- Code split vendors
- Inline critical CSS
- Use CDN for hosting
- Enable HTTP/2
Error: CSP blocking resources Solution: Review CSP, add necessary domains
Error: Mixed content warnings Solution: Ensure all resources loaded via HTTPS
- All tests passing (
npm run test) - Type check passing (
npm run type-check) - Lint passing (
npm run lint) - Build succeeds (
npm run build) - Bundle size acceptable (<600 KB)
- Lighthouse scores >90
- HTTPS configured
- SSL certificate valid
- Security headers configured
- CSP tested and working
- No secrets in code
- Environment variables secured
- Service worker registering
- Offline mode working
- Install prompt appearing
- Icons valid and correct sizes
- Manifest.json valid
- Lighthouse Performance >90
- First Contentful Paint <1.8s
- Time to Interactive <3.8s
- Compression enabled
- Assets cached properly
- Error tracking configured (if used)
- Analytics configured (if used)
- Uptime monitoring set up
- SSL expiration monitoring
- Test on real devices (mobile, tablet, desktop)
- Test on multiple browsers (Chrome, Safari, Firefox, Edge)
- Verify PWA installation
- Test offline functionality
- Check security headers (securityheaders.com)
- Run Lighthouse audit on live URL
- Monitor error logs for first 24 hours
Create .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test:run
- name: Type check
run: npm run type-check
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'Last Updated: 2025-10-25 Version: 1.0.0