-
Notifications
You must be signed in to change notification settings - Fork 0
Component Integration
This guide covers integrating all TMI components to create a complete working system.
A fully operational TMI deployment consists of:
- TMI Web Application (Frontend)
- TMI Server (API Backend)
- PostgreSQL (Database)
- Redis (Cache)
- OAuth Providers (Authentication)
- Load Balancer/Reverse Proxy (Optional, recommended for production)
┌─────────────────┐
│ User Browser │
└────────┬────────┘
│ HTTPS
↓
┌─────────────────┐
│ Load Balancer │ (Optional)
│ / Reverse │
│ Proxy │
└────┬──────┬─────┘
│ │
│ └─────────────┐
│ HTTPS │ HTTPS
↓ ↓
┌──────────────┐ ┌────────────────┐
│ TMI Web App │ │ TMI Server │
│ (Static) │ │ (API/WebSoc...│
└──────────────┘ └───┬────────┬───┘
│ │
│ TCP │ TCP
↓ ↓
┌──────────┐ ┌──────────┐
│PostgreSQL│ │ Redis │
└──────────┘ └──────────┘
↓
┌──────────────┐
│OAuth Provider│
│(Google/GitHub│
│ /Microsoft) │
└──────────────┘
Before integration, ensure you have:
- TMI server deployed and running
- TMI web application built and deployed
- PostgreSQL configured with migrations run
- Redis configured and running
- OAuth providers configured
- DNS records configured (for production)
- SSL/TLS certificates (for production)
The web application needs to know where to find the API server.
Edit src/environments/environment.prod.ts:
export const environment = {
production: true,
apiUrl: 'https://api.tmi.example.com', // Your TMI API server URL
logLevel: 'ERROR',
authTokenExpiryMinutes: 60,
operatorName: 'TMI Operator',
operatorContact: 'support@example.com',
};Important: The apiUrl must be accessible from users' browsers. Options:
-
Separate subdomain (recommended):
- Web app:
https://tmi.example.com - API:
https://api.tmi.example.com
- Web app:
-
Same domain, different path:
- Web app:
https://tmi.example.com - API:
https://tmi.example.com/api - Requires reverse proxy to route
/apito TMI server
- Web app:
-
Development:
- Web app:
http://localhost:4200 - API:
http://localhost:8080
- Web app:
If web app and API are on different domains, configure CORS in TMI server.
Environment Variable:
WEBSOCKET_ALLOWED_ORIGINS="https://tmi.example.com,https://www.tmi.example.com"Configuration File (config-production.yml):
server:
cors:
allowed_origins:
- "https://tmi.example.com"
- "https://www.tmi.example.com"
websocket:
allowed_origins:
- "https://tmi.example.com"
- "https://www.tmi.example.com"OAuth requires coordination between web app, TMI server, and OAuth providers.
Configure redirect URIs in OAuth provider dashboards:
Web Application Callback: https://tmi.example.com/oauth2/callback
This is where users land after authenticating with the provider.
Configure the OAuth callback URL that TMI server uses:
Environment Variable:
OAUTH_CALLBACK_URL="https://api.tmi.example.com/oauth2/callback"Configuration File:
auth:
oauth:
callback_url: "https://api.tmi.example.com/oauth2/callback"Important: The web app handles the user-facing callback, then sends the authorization code to the TMI server's token exchange endpoint.
The complete OAuth flow:
- User clicks login → Web app redirects to OAuth provider
- User authenticates → Provider redirects to web app callback
- Web app receives code → Sends code to TMI server
- TMI server exchanges code → Gets user info from provider
- TMI server issues JWT → Returns tokens to web app
- Web app stores tokens → Uses for subsequent API requests
Ensure TMI server can connect to PostgreSQL and Redis.
Test connection from TMI server:
# From TMI server host
psql -h postgres-host -U tmi_user -d tmi -c "SELECT version();"Configure TMI server:
POSTGRES_HOST=postgres.example.com
POSTGRES_PORT=5432
POSTGRES_USER=tmi_user
POSTGRES_PASSWORD=secure_password
POSTGRES_DATABASE=tmi
POSTGRES_SSL_MODE=requireVerify migrations:
# Check database schema
go run cmd/check-db/main.go
# Should show all tables presentTest connection from TMI server:
# From TMI server host
redis-cli -h redis-host -p 6379 -a redis_password ping
# Expected: PONGConfigure TMI server:
REDIS_HOST=redis.example.com
REDIS_PORT=6379
REDIS_PASSWORD=redis_password
REDIS_DB=0Ensure TMI server can reach databases:
# Test PostgreSQL connectivity
telnet postgres.example.com 5432
# Test Redis connectivity
telnet redis.example.com 6379Firewall rules:
- PostgreSQL: Allow only from TMI server IPs
- Redis: Allow only from TMI server IPs
- Do not expose databases to internet
A reverse proxy provides SSL termination, load balancing, and routing.
Create /etc/nginx/sites-available/tmi:
# Upstream servers
upstream tmi_api {
server localhost:8080;
# Add more servers for load balancing
# server 10.0.0.11:8080;
# server 10.0.0.12:8080;
keepalive 32;
}
# Web application
server {
listen 443 ssl http2;
server_name tmi.example.com;
ssl_certificate /etc/letsencrypt/live/tmi.example.com/fullchain.pem;
ssl_private_key /etc/letsencrypt/live/tmi.example.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Web application (static files)
root /var/www/tmi-ux;
index index.html;
# Enable gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
# Static assets with long cache
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Angular routing
location / {
try_files $uri $uri/ /index.html;
}
}
# API server
server {
listen 443 ssl http2;
server_name api.tmi.example.com;
ssl_certificate /etc/letsencrypt/live/api.tmi.example.com/fullchain.pem;
ssl_private_key /etc/letsencrypt/live/api.tmi.example.com/privkey.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=20 nodelay;
location / {
proxy_pass http://tmi_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts for WebSocket
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name tmi.example.com api.tmi.example.com;
return 301 https://$server_name$request_uri;
}Enable configuration:
sudo ln -s /etc/nginx/sites-available/tmi /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxFor same-domain deployment:
server {
listen 443 ssl http2;
server_name tmi.example.com;
ssl_certificate /etc/letsencrypt/live/tmi.example.com/fullchain.pem;
ssl_private_key /etc/letsencrypt/live/tmi.example.com/privkey.pem;
# API routes
location /api/ {
rewrite ^/api/(.*) /$1 break;
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Web application
root /var/www/tmi-ux;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}Update web app configuration:
apiUrl: 'https://tmi.example.com/api'# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Get certificate for web app
sudo certbot --nginx -d tmi.example.com -d www.tmi.example.com
# Get certificate for API
sudo certbot --nginx -d api.tmi.example.com
# Test renewal
sudo certbot renew --dry-runCertbot automatically:
- Obtains certificates
- Configures nginx
- Sets up auto-renewal
If using custom certificates:
# Copy certificates
sudo cp tmi.example.com.crt /etc/ssl/certs/
sudo cp tmi.example.com.key /etc/ssl/private/
sudo chmod 600 /etc/ssl/private/tmi.example.com.keyConfigure in nginx (shown in Step 4).
Configure DNS records for your domains:
tmi.example.com A 203.0.113.10
api.tmi.example.com A 203.0.113.10
Or use separate IPs:
tmi.example.com A 203.0.113.10 (web app server)
api.tmi.example.com A 203.0.113.11 (API server)
tmi.example.com A 203.0.113.10
www.tmi.example.com CNAME tmi.example.com
api.tmi.example.com CNAME tmi.example.com
# Check DNS resolution
dig tmi.example.com
dig api.tmi.example.com
# Or with nslookup
nslookup tmi.example.com
nslookup api.tmi.example.com# Health check
curl https://api.tmi.example.com/version
# Expected response:
{
"version": "1.0.0",
"build": "2025-11-12",
"go_version": "1.24"
}
# Check OAuth providers
curl https://api.tmi.example.com/oauth2/providers
# Expected response:
{
"providers": [
{"id": "google", "name": "Google", "icon": "google"},
{"id": "github", "name": "GitHub", "icon": "github"}
]
}# Check if web app loads
curl -I https://tmi.example.com
# Should return 200 OK
# Check static assets
curl -I https://tmi.example.com/main.js
# Should return 200 OK with long cache headers-
Open web app in browser:
https://tmi.example.com - Click login button
- Select OAuth provider (Google/GitHub/Microsoft)
- Complete authentication
- Verify redirect to application
- Check browser console for errors
- Verify JWT token received
After logging in:
// In browser console
fetch('https://api.tmi.example.com/threat_models', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('tmi_access_token')
}
})
.then(r => r.json())
.then(console.log)Should return threat models (empty array if none created yet).
// In browser console (after login)
const ws = new WebSocket('wss://api.tmi.example.com/ws/diagrams/test-id');
ws.onopen = () => console.log('WebSocket connected');
ws.onerror = (error) => console.error('WebSocket error:', error);
ws.onclose = () => console.log('WebSocket closed');Note: This will fail with 401 if not properly authenticated, but confirms WebSocket endpoint is accessible.
Symptom: Browser console shows CORS errors when calling API.
Solution:
- Verify
WEBSOCKET_ALLOWED_ORIGINSincludes web app domain - Check API server CORS configuration
- Ensure preflight requests are handled
Test CORS:
curl -H "Origin: https://tmi.example.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Authorization" \
-X OPTIONS \
https://api.tmi.example.com/threat_modelsSymptom: After OAuth login, app redirects back to provider repeatedly.
Causes:
- Redirect URI mismatch in OAuth provider configuration
- State parameter validation failing
- Token exchange failing
Debug:
- Check browser network tab for failed requests
- Verify OAuth redirect URI matches exactly
- Check TMI server logs for token exchange errors
- Verify OAuth client credentials are correct
Symptom: Real-time collaboration doesn't work.
Solutions:
- Check reverse proxy WebSocket configuration
- Verify
UpgradeandConnectionheaders are forwarded - Check WebSocket allowed origins
- Increase proxy timeouts for long-lived connections
Test WebSocket:
# Test WebSocket endpoint (requires wscat)
npm install -g wscat
wscat -c wss://api.tmi.example.com/ws/diagrams/test-id \
-H "Authorization: Bearer YOUR_TOKEN"Symptom: API requests fail with database timeout errors.
Solutions:
- Check database server is running
- Verify firewall allows TMI server to database
- Check connection pool settings
- Verify database credentials
Test database connection:
# From TMI server host
psql -h postgres-host -U tmi_user -d tmi -c "SELECT 1;"Symptom: Web app loads but shows blank page, missing CSS/JS.
Solutions:
- Check nginx static file configuration
- Verify file permissions on /var/www/tmi-ux
- Check browser console for 404 errors
- Verify nginx is serving correct directory
Debug:
# Check files exist
ls -la /var/www/tmi-ux/
# Check nginx error log
sudo tail -f /var/log/nginx/error.log
# Test static file
curl -I https://tmi.example.com/main.jsAfter integration, verify security:
- All traffic uses HTTPS
- HTTP redirects to HTTPS
- Security headers configured (CSP, X-Frame-Options, etc.)
- Database not accessible from internet
- Redis not accessible from internet
- OAuth secrets in environment variables, not config files
- JWT secret is strong and unique
- Rate limiting configured
- CORS properly restricted
- Firewall rules limiting access
- SSL certificates valid and auto-renewing
Configure nginx:
upstream tmi_api {
server localhost:8080;
keepalive 32; # Keep connections alive
}
location / {
proxy_http_version 1.1;
proxy_set_header Connection ""; # Clear connection header
}Configure caching headers:
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Don't cache API responses
location /api/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}Enable compression in nginx:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss;Set up monitoring for integrated system:
Create /usr/local/bin/check-tmi-health.sh:
#!/bin/bash
# Check web app
if ! curl -f -s https://tmi.example.com > /dev/null; then
echo "ERROR: Web app not responding"
exit 1
fi
# Check API
if ! curl -f -s https://api.tmi.example.com/version > /dev/null; then
echo "ERROR: API not responding"
exit 1
fi
# Check database
if ! psql -h postgres-host -U tmi_user -d tmi -c "SELECT 1;" > /dev/null 2>&1; then
echo "ERROR: Database not responding"
exit 1
fi
# Check Redis
if ! redis-cli -h redis-host -a password ping > /dev/null 2>&1; then
echo "ERROR: Redis not responding"
exit 1
fi
echo "OK: All components healthy"
exit 0Consider deploying:
- Prometheus: Metrics collection
- Grafana: Dashboards and visualization
- Loki: Log aggregation
- Alertmanager: Alert routing
Before going live:
- All components tested individually
- Integration tests pass
- OAuth flow works end-to-end
- WebSocket connections successful
- Database migrations complete
- Redis cache functioning
- DNS records configured and propagated
- SSL certificates installed and valid
- Reverse proxy configured and tested
- Firewall rules in place
- Monitoring and alerting active
- Backup procedures tested
- Rollback plan documented
- Can access web app via domain
- Can login with OAuth providers
- Can create threat model
- Can edit diagrams
- Real-time collaboration works
- No errors in browser console
- No errors in server logs
- Post-Deployment - Final verification and testing
- Monitoring and Health - Set up ongoing monitoring
- Security Best Practices - Additional hardening
- Using TMI for Threat Modeling
- Accessing TMI
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Metadata and Extensions
- Planning Your Deployment
- Deploying TMI Server
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Monitoring and Health
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks