PatchMon Architecture Documentation
Table of Contents
- System Overview
- Nginx Reverse Proxy
- BullMQ Queue System
- Database Schema
- API Credential Scoping
- WebSocket Communication
- Agent Communication Flow
- Authentication & Authorization
System Overview
graph TB
subgraph "Client Layer"
Browser[Web Browser]
Agent[PatchMon Agent<br/>Go Binary]
end
subgraph "Reverse Proxy Layer"
Nginx[Nginx<br/>Port 80/443]
end
subgraph "Application Layer"
Frontend[React Frontend<br/>Vite + Tailwind]
Backend[Node.js Backend<br/>Express Server<br/>Port 3001]
end
subgraph "Queue System"
Redis[(Redis<br/>Queue Storage)]
BullMQ[BullMQ Queues]
Workers[BullMQ Workers]
end
subgraph "Data Layer"
PostgreSQL[(PostgreSQL<br/>Primary Database)]
end
subgraph "External Services"
OIDC[OIDC Provider<br/>Optional]
DNS[DNS Server<br/>Version Check<br/>server.vcheck.patchmon.net]
end
Browser -->|HTTPS/HTTP| Nginx
Agent -->|HTTPS/HTTP<br/>WebSocket| Nginx
Nginx -->|"Proxy /api/*"| Backend
Nginx -->|"Proxy /bullboard"| Backend
Nginx -->|Serve Static| Frontend
Backend -->|Read/Write| PostgreSQL
Backend -->|Queue Jobs| BullMQ
BullMQ -->|Store Jobs| Redis
Workers -->|Process Jobs| BullMQ
Workers -->|Read/Write| PostgreSQL
Backend -->|WebSocket| Agent
Backend -->|OAuth| OIDC
Backend -->|DNS TXT Query| DNS
Nginx Reverse Proxy
Request Routing Flow
graph LR
Client[Client Request] --> Nginx{Nginx<br/>Port 80/443}
Nginx -->|"Location: /"| Frontend[Frontend<br/>Static Files<br/>/usr/share/nginx/html]
Nginx -->|"Location: /api/*"| Backend[Backend API<br/>http://backend:3001]
Nginx -->|"Location: /bullboard"| Backend[Bull Board UI<br/>http://backend:3001/bullboard]
Nginx -->|"Location: /assets/*"| Assets[Custom Branding<br/>/usr/share/nginx/html/assets/]
Nginx -->|"Location: /health"| Health[Health Check<br/>200 OK]
Backend -->|WebSocket Upgrade| WS[WebSocket Handler]
Nginx Configuration Details
Key Features:
- Port 3000 (Docker) or 80/443 (Production)
- SSL/TLS support with Let's Encrypt
- WebSocket upgrade support for real-time communication
- Security headers (HSTS, X-Frame-Options, CSP)
- Cookie passthrough for authentication
- IP forwarding (X-Real-IP, X-Forwarded-For)
- Large file uploads (10MB limit for API routes)
Location Blocks:
/- Frontend SPA (try_files for client-side routing)/api/*- Backend API proxy with WebSocket support/bullboard- Bull Board queue monitoring UI/assets/*- Custom branding assets (logos, favicons)/health- Health check endpoint
BullMQ Queue System
Queue Architecture
graph TB
subgraph "Queue Manager"
QM[QueueManager<br/>Singleton]
end
subgraph "Queues (BullMQ)"
Q1[version-update-check]
Q2[session-cleanup]
Q3[orphaned-repo-cleanup]
Q4[orphaned-package-cleanup]
Q5[docker-inventory-cleanup]
Q6[docker-image-update-check]
Q7[metrics-reporting]
Q8[system-statistics]
Q9[social-media-stats]
Q10[agent-commands]
Q11[alert-cleanup]
Q12[host-status-monitor]
end
subgraph "Workers"
W1[VersionUpdateCheck Worker]
W2[SessionCleanup Worker]
W3[OrphanedRepoCleanup Worker]
W4[OrphanedPackageCleanup Worker]
W5[DockerInventoryCleanup Worker]
W6[DockerImageUpdateCheck Worker]
W7[MetricsReporting Worker]
W8[SystemStatistics Worker]
W9[SocialMediaStats Worker]
W10[AgentCommands Worker]
W11[AlertCleanup Worker]
W12[HostStatusMonitor Worker]
end
subgraph "Storage Layer"
Redis[(Redis<br/>Job Storage)]
end
subgraph "Database"
DB[(PostgreSQL)]
end
QM --> Q1
QM --> Q2
QM --> Q3
QM --> Q4
QM --> Q5
QM --> Q6
QM --> Q7
QM --> Q8
QM --> Q9
QM --> Q10
QM --> Q11
QM --> Q12
Q1 --> Redis
Q2 --> Redis
Q3 --> Redis
Q4 --> Redis
Q5 --> Redis
Q6 --> Redis
Q7 --> Redis
Q8 --> Redis
Q9 --> Redis
Q10 --> Redis
Q11 --> Redis
Q12 --> Redis
W1 --> Q1
W2 --> Q2
W3 --> Q3
W4 --> Q4
W5 --> Q5
W6 --> Q6
W7 --> Q7
W8 --> Q8
W9 --> Q9
W10 --> Q10
W11 --> Q11
W12 --> Q12
W1 --> DB
W2 --> DB
W3 --> DB
W4 --> DB
W5 --> DB
W6 --> DB
W7 --> DB
W8 --> DB
W9 --> DB
W10 --> DB
W11 --> DB
W12 --> DB
W10 -->|WebSocket Commands| AgentWS[Agent WebSocket]
Queue Details
| Queue Name | Purpose | Schedule | Worker |
|---|---|---|---|
version-update-check |
Check for PatchMon updates | Daily | VersionUpdateCheck |
session-cleanup |
Clean expired user sessions | Hourly | SessionCleanup |
orphaned-repo-cleanup |
Remove unused repositories | Daily | OrphanedRepoCleanup |
orphaned-package-cleanup |
Remove unused packages | Daily | OrphanedPackageCleanup |
docker-inventory-cleanup |
Clean Docker inventory | Daily | DockerInventoryCleanup |
docker-image-update-check |
Check Docker image updates | Daily | DockerImageUpdateCheck |
metrics-reporting |
Report anonymous metrics | Daily | MetricsReporting |
system-statistics |
Generate system statistics | Hourly | SystemStatistics |
social-media-stats |
Fetch social media stats | On boot | SocialMediaStats |
agent-commands |
Send commands to agents | On demand | AgentCommands |
alert-cleanup |
Clean old alerts | Daily | AlertCleanup |
host-status-monitor |
Monitor host status | Every 5 min | HostStatusMonitor |
Worker Configuration
- Concurrency: 1 job at a time per worker
- Retry Policy: 3 attempts with exponential backoff
- Job Retention: 50 completed, 20 failed jobs
- Connection: Shared Redis connection pool
Version Check Mechanism
The version update check uses DNS TXT records instead of GitHub API to avoid rate limiting and provide a lightweight, fast version checking mechanism.
DNS Domains:
- Server version:
server.vcheck.patchmon.net - Agent version:
agent.vcheck.patchmon.net
How it works:
- Worker queries DNS TXT record for the appropriate domain
- DNS returns version string (e.g., "1.4.0") in TXT record
- Version is validated against semantic versioning format
- Compared with current version from
package.json(server) or agent binary - Update alerts created if newer version available
Benefits:
- No API rate limits
- Fast DNS lookups (cached by DNS resolvers)
- No authentication required
- Works even if GitHub API is unavailable
Implementation:
- Uses Node.js
dns.resolveTxt()for DNS queries - Validates version format:
^\d+\.\d+\.\d+ - Falls back to cached version from database if DNS lookup fails
Database Schema
Core Entity Relationships
erDiagram
users ||--o{ user_sessions : has
users ||--o{ dashboard_preferences : has
users ||--o{ auto_enrollment_tokens : creates
users ||--o{ alerts : assigned_to
users ||--o{ alerts : resolved_by
users ||--o{ alert_history : creates
hosts ||--o{ host_packages : has
hosts ||--o{ host_repositories : has
hosts ||--o{ host_group_memberships : belongs_to
hosts ||--o{ update_history : has
hosts ||--o{ job_history : has
hosts ||--o{ docker_containers : has
hosts ||--o{ docker_volumes : has
hosts ||--o{ docker_networks : has
hosts ||--o{ compliance_scans : has
packages ||--o{ host_packages : "installed on"
repositories ||--o{ host_repositories : "used by"
host_groups ||--o{ host_group_memberships : contains
host_groups ||--o{ auto_enrollment_tokens : "default group"
compliance_profiles ||--o{ compliance_scans : used_in
compliance_profiles ||--o{ compliance_rules : contains
compliance_scans ||--o{ compliance_results : produces
compliance_rules ||--o{ compliance_results : evaluated_in
docker_images ||--o{ docker_containers : used_by
docker_images ||--o{ docker_image_updates : has
alerts ||--o{ alert_history : has
alert_config ||--o| users : "auto_assign_to"
Key Tables
Core Tables
users
- Authentication (password_hash, tfa_secret)
- OIDC integration (oidc_sub, oidc_provider)
- Preferences (theme, dashboard layout)
- Role-based permissions
hosts
- Unique API credentials (api_id, api_key) - scoped per host
- System information (OS, hardware, network)
- Status tracking (status, last_update)
- Feature flags (docker_enabled, compliance_enabled)
packages
- Package metadata (name, description, category)
- Version tracking (latest_version)
host_packages
- Package instances on hosts
- Update status (needs_update, is_security_update)
- Version tracking (current_version, available_version)
repositories
- Repository definitions (URL, distribution, components)
- Security status (is_secure, is_active)
Docker Tables
docker_images
- Image metadata (repository, tag, digest)
- Source tracking (docker-hub, etc.)
docker_containers
- Container instances per host
- Status and state tracking
docker_image_updates
- Available updates per image
- Security update flags
Compliance Tables
compliance_profiles
- Scan profiles (OpenSCAP, Docker Bench)
- OS family and version mapping
compliance_scans
- Scan execution records
- Results summary (passed, failed, score)
compliance_rules
- Rule definitions per profile
- Severity and remediation info
compliance_results
- Individual rule results per scan
- Findings and remediation details
Alert System Tables
alerts
- Alert records (type, severity, status)
- Assignment and resolution tracking
alert_history
- Audit trail of alert actions
- User actions (created, resolved, silenced)
alert_config
- Per-alert-type configuration
- Auto-assignment rules
- Retention policies
Job Tracking
job_history
- BullMQ job execution records
- Status tracking (active, completed, failed)
- Linked to hosts via api_id
API Credential Scoping
Credential Types
graph TB
subgraph "Credential Types"
HostCreds[Host API Credentials<br/>api_id + api_key<br/>Per-host scoped]
TokenCreds[Auto Enrollment Tokens<br/>token_key + token_secret<br/>Scoped with permissions]
end
subgraph "Host Credentials"
HostCreds -->|Stored in| HostTable["hosts table<br/>api_id: unique<br/>api_key: hashed"]
HostCreds -->|Used by| Agent[Agent Authentication<br/>X-API-ID + X-API-KEY headers]
HostCreds -->|Validated by| HostAuth[validateApiCredentials<br/>middleware]
end
subgraph "Token Credentials"
TokenCreds -->|Stored in| TokenTable["auto_enrollment_tokens table<br/>token_key: unique<br/>token_secret: bcrypt hashed"]
TokenCreds -->|Scopes| Scopes["JSON scopes object<br/>resource: action array"]
TokenCreds -->|IP Restrictions| IPRanges[allowed_ip_ranges<br/>CIDR blocks]
TokenCreds -->|Used by| API[API Integration<br/>Basic Auth]
TokenCreds -->|Validated by| TokenAuth[authenticateApiToken<br/>+ requireApiScope]
end
Authentication Flow
Host Authentication (Agent)
sequenceDiagram
participant Agent
participant Nginx
participant Backend
participant DB
Agent->>Nginx: POST /api/v1/hosts/update<br/>Headers: X-API-ID, X-API-KEY
Nginx->>Backend: Proxy request
Backend->>Backend: validateApiCredentials middleware
Backend->>DB: Find host by api_id
DB-->>Backend: Host record
Backend->>Backend: Verify api_key (bcrypt)
alt Valid credentials
Backend->>Backend: Attach hostRecord to req
Backend->>Backend: Process request
Backend-->>Nginx: 200 OK + Response
Nginx-->>Agent: Response
else Invalid credentials
Backend-->>Nginx: 401 Unauthorized
Nginx-->>Agent: 401 Error
end
Token Authentication (API Integration)
sequenceDiagram
participant Client
participant Nginx
participant Backend
participant DB
Client->>Nginx: POST /api/v1/hosts<br/>Authorization: Basic base64(token_key:token_secret)
Nginx->>Backend: Proxy request
Backend->>Backend: authenticateApiToken middleware
Backend->>DB: Find token by token_key
DB-->>Backend: Token record
Backend->>Backend: Verify token_secret (bcrypt)
Backend->>Backend: Check token.is_active
Backend->>Backend: Check token.expires_at
Backend->>Backend: Check IP restrictions (CIDR)
alt Valid token
Backend->>Backend: requireApiScope middleware
Backend->>Backend: Validate scope permissions
alt Has permission
Backend->>Backend: Attach apiToken to req
Backend->>Backend: Process request
Backend-->>Nginx: 200 OK + Response
Nginx-->>Client: Response
else No permission
Backend-->>Nginx: 403 Forbidden
Nginx-->>Client: 403 Error
end
else Invalid token
Backend-->>Nginx: 401 Unauthorized
Nginx-->>Client: 401 Error
end
Scope Structure
Token scopes are stored as JSON in auto_enrollment_tokens.scopes:
{
"host": ["get", "post", "put", "patch", "delete"],
"package": ["get", "post"],
"compliance": ["get", "post"]
}
Scope Validation:
- Only applies to tokens with
metadata.integration_type === "api" - Validates
scopes[resource].includes(action) - Returns 403 if scope not found
WebSocket Communication
WebSocket Architecture
graph TB
subgraph "Client Layer"
Browser[Web Browser]
Agent[PatchMon Agent]
end
subgraph "Nginx"
NginxProxy[Nginx<br/>WebSocket Upgrade]
end
subgraph "Backend WebSocket Handlers"
AgentWS["Agent WebSocket<br/>/ws/agent"]
SSHWS["SSH Terminal WebSocket<br/>/ws/ssh/:hostId"]
BullWS[Bull Board WebSocket<br/>/bullboard]
end
subgraph "WebSocket Server"
WSS[WebSocket.Server<br/>noServer: true]
end
subgraph "HTTP Server"
HTTPServer[Express HTTP Server]
end
Browser -->|WebSocket Upgrade| NginxProxy
Agent -->|WebSocket Upgrade| NginxProxy
NginxProxy -->|Upgrade Request| HTTPServer
HTTPServer -->|Upgrade Event| WSS
WSS -->|Route by pathname| AgentWS
WSS -->|Route by pathname| SSHWS
WSS -->|Route by pathname| BullWS
Agent WebSocket Flow
sequenceDiagram
participant Agent
participant Nginx
participant Backend
participant WSS
participant DB
Agent->>Nginx: WebSocket Upgrade<br/>/ws/agent?api_id=xxx&api_key=yyy
Nginx->>Backend: Upgrade request
Backend->>Backend: HTTP upgrade event handler
Backend->>DB: Validate api_id + api_key
alt Valid credentials
Backend->>WSS: Accept WebSocket connection
WSS->>Agent: Connection established
Agent->>WSS: Ping every 30s
WSS->>Agent: Pong response
Note over WSS,Agent: Bidirectional communication
WSS->>Agent: Command: report_now
Agent->>WSS: Response: update_data
WSS->>Agent: Command: update_agent
Agent->>WSS: Response: update_status
WSS->>Agent: Command: compliance_scan
Agent->>WSS: Progress: compliance_scan_progress
WSS->>Agent: Command: docker_inventory_refresh
Agent->>WSS: Event: docker_status
else Invalid credentials
Backend->>Agent: Connection rejected
end
SSH Terminal WebSocket Flow
sequenceDiagram
participant Browser
participant Nginx
participant Backend
participant SSHWS
participant AgentWS
participant Agent
participant SSHHost
Browser->>Nginx: WebSocket Upgrade<br/>/ws/ssh/:hostId<br/>Cookie: session_token
Nginx->>Backend: Upgrade request
Backend->>Backend: Authenticate user session
alt Authenticated
Backend->>SSHWS: Accept WebSocket connection
SSHWS->>Browser: Connection established
Browser->>SSHWS: {type: "connect", connection_mode: "proxy"}
SSHWS->>SSHWS: Generate proxy session ID
SSHWS->>AgentWS: Forward to agent WebSocket
AgentWS->>Agent: SSH proxy request
Agent->>SSHHost: SSH connection
Agent->>AgentWS: SSH data stream
AgentWS->>SSHWS: Forward SSH data
SSHWS->>Browser: Display terminal
Browser->>SSHWS: Terminal input
SSHWS->>AgentWS: Forward input
AgentWS->>Agent: Forward to SSH
Agent->>SSHHost: Send input
end
WebSocket Message Types
Agent → Backend
docker_status- Docker container status eventscompliance_scan_progress- Compliance scan progress updatesssh_proxy_data- SSH terminal data (proxy mode)ssh_proxy_connected- SSH connection establishedssh_proxy_error- SSH connection errorssh_proxy_closed- SSH connection closed
Backend → Agent
report_now- Force immediate update reportupdate_agent- Update agent binarysettings_update- Update agent settings (update_interval)refresh_integration_status- Refresh integration statusdocker_inventory_refresh- Refresh Docker inventorycompliance_scan- Start compliance scanremediate_rule- Remediate specific compliance rulessh_proxy_request- SSH proxy connection request
Agent Communication Flow
Agent Registration & Enrollment
sequenceDiagram
participant Agent
participant Nginx
participant Backend
participant DB
Note over Agent: Agent installed on host
Agent->>Nginx: POST /api/v1/hosts/enroll<br/>Authorization: Basic (token_key:token_secret)
Nginx->>Backend: Proxy request
Backend->>Backend: authenticateApiToken middleware
Backend->>DB: Validate enrollment token
Backend->>DB: Check IP restrictions
Backend->>DB: Check max_hosts_per_day
Backend->>DB: Create host record<br/>Generate api_id + api_key
DB-->>Backend: Host created
Backend->>DB: Assign to default host group
Backend-->>Nginx: 201 Created<br/>{api_id, api_key}
Nginx-->>Agent: Credentials
Agent->>Agent: Save credentials to<br/>/etc/patchmon/credentials.yaml
Agent Update Reporting
sequenceDiagram
participant Agent
participant Nginx
participant Backend
participant DB
participant BullMQ
Note over Agent: Scheduled update check<br/>(every update_interval minutes)
Agent->>Nginx: POST /api/v1/hosts/update<br/>Headers: X-API-ID, X-API-KEY<br/>Body: {packages, repositories, ...}
Nginx->>Backend: Proxy request
Backend->>Backend: validateApiCredentials
Backend->>DB: Update host record
Backend->>DB: Upsert packages
Backend->>DB: Upsert repositories
Backend->>DB: Create update_history record
Backend->>BullMQ: Queue system-statistics job
Backend-->>Nginx: 200 OK
Nginx-->>Agent: Response
Agent Command Execution
sequenceDiagram
participant User
participant Frontend
participant Backend
participant BullMQ
participant Worker
participant AgentWS
participant Agent
User->>Frontend: Click "Update Agent" button
Frontend->>Backend: POST /api/v1/hosts/:id/update-agent
Backend->>Backend: Authenticate user (session)
Backend->>Backend: Check permissions
Backend->>BullMQ: Add job to agent-commands queue<br/>{api_id, type: "update_agent"}
Backend-->>Frontend: 202 Accepted
Frontend-->>User: "Update queued"
BullMQ->>Worker: Process job
Worker->>Backend: Check agent WebSocket connection
Worker->>AgentWS: Send command via WebSocket
AgentWS->>Agent: WebSocket message<br/>{type: "update_agent"}
Agent->>Agent: Download new binary
Agent->>Agent: Restart with new binary
Agent->>AgentWS: Update status
AgentWS->>Worker: Command completed
Worker->>DB: Update job_history status
Authentication & Authorization
User Authentication Flow
graph TB
subgraph "Authentication Methods"
LocalAuth[Local Auth<br/>Username + Password]
TFA[Two-Factor Auth<br/>TOTP]
OIDC[OIDC/OAuth2<br/>Optional]
end
subgraph "Session Management"
Session[User Session<br/>httpOnly cookie]
RefreshToken[Refresh Token<br/>Database stored]
AccessToken[Access Token<br/>JWT in cookie]
end
subgraph "Authorization"
Roles[Role-Based<br/>admin, user, viewer]
Permissions[Permission Matrix<br/>role_permissions table]
end
LocalAuth -->|+ TFA| TFA
LocalAuth -->|+ OIDC| OIDC
TFA --> Session
OIDC --> Session
Session --> RefreshToken
RefreshToken --> AccessToken
AccessToken --> Roles
Roles --> Permissions
Permission Matrix
| Permission | Admin | User | Viewer |
|---|---|---|---|
| View Dashboard | ✅ | ✅ | ✅ |
| View Hosts | ✅ | ✅ | ✅ |
| Manage Hosts | ✅ | ❌ | ❌ |
| View Packages | ✅ | ✅ | ✅ |
| Manage Packages | ✅ | ❌ | ❌ |
| View Users | ✅ | ❌ | ❌ |
| Manage Users | ✅ | ❌ | ❌ |
| Manage Superusers | ✅ | ❌ | ❌ |
| View Reports | ✅ | ✅ | ✅ |
| Export Data | ✅ | ❌ | ❌ |
| Manage Settings | ✅ | ❌ | ❌ |
Security Features
-
Password Security
- Bcrypt hashing (10 rounds)
- Password strength requirements
-
Session Security
- HttpOnly cookies (XSS protection)
- Secure flag (HTTPS only)
- SameSite protection (CSRF mitigation)
- Device fingerprinting
- IP address tracking
-
Two-Factor Authentication
- TOTP (Time-based One-Time Password)
- Backup codes
- Remember device option
-
API Security
- Rate limiting
- IP restrictions (CIDR blocks)
- Token expiration
- Scope-based permissions
- Brute force protection
-
WebSocket Security
- Authentication before upgrade
- API credential validation
- Message size limits (64KB)
- Connection timeout handling
Data Flow Examples
Package Update Detection Flow
sequenceDiagram
participant Agent
participant Backend
participant DB
participant BullMQ
participant Worker
participant Frontend
Note over Agent: Agent checks for updates
Agent->>Backend: POST /api/v1/hosts/update<br/>packages: [{name, current_version, available_version}]
Backend->>DB: Upsert packages table
Backend->>DB: Upsert host_packages table<br/>Set needs_update, is_security_update flags
Backend->>DB: Create update_history record
Backend-->>Agent: 200 OK
Backend->>BullMQ: Queue system-statistics job
BullMQ->>Worker: Process job
Worker->>DB: Aggregate statistics
Worker->>DB: Create system_statistics record
Frontend->>Backend: GET /api/v1/hosts/:id/packages
Backend->>DB: Query host_packages with filters
DB-->>Backend: Package list
Backend-->>Frontend: Packages with update status
Compliance Scan Flow
sequenceDiagram
participant User
participant Frontend
participant Backend
participant BullMQ
participant Worker
participant AgentWS
participant Agent
participant OpenSCAP
User->>Frontend: Start compliance scan
Frontend->>Backend: POST /api/v1/compliance/scans/start
Backend->>Backend: Authenticate user
Backend->>BullMQ: Queue agent-commands job<br/>{type: "compliance_scan", profile_id}
Backend-->>Frontend: 202 Accepted
BullMQ->>Worker: Process job
Worker->>AgentWS: Send WebSocket command
AgentWS->>Agent: {type: "compliance_scan", profile_id}
Agent->>OpenSCAP: Run oscap scan
OpenSCAP-->>Agent: Scan results (ARF/XML)
Agent->>AgentWS: Progress updates<br/>{type: "compliance_scan_progress"}
AgentWS->>Backend: Forward progress
Backend->>Frontend: WebSocket broadcast
Agent->>Backend: POST /api/v1/compliance/scans<br/>Scan results
Backend->>Backend: Parse results
Backend->>DB: Create compliance_scans record
Backend->>DB: Create compliance_results records
Backend-->>Agent: 200 OK
Frontend->>Backend: GET /api/v1/compliance/scans/:id
Backend->>DB: Query scan results
DB-->>Backend: Scan details
Backend-->>Frontend: Results with pass/fail status
Component Details
Backend Structure
backend/
├── src/
│ ├── server.js # Main Express server
│ ├── middleware/
│ │ ├── auth.js # User authentication
│ │ ├── apiAuth.js # API token authentication
│ │ └── apiScope.js # API scope validation
│ ├── routes/ # API route handlers
│ ├── services/
│ │ ├── automation/ # BullMQ queue management
│ │ ├── agentWs.js # Agent WebSocket handler
│ │ └── sshTerminalWs.js # SSH terminal WebSocket
│ └── utils/ # Utilities
└── prisma/
└── schema.prisma # Database schema
Frontend Structure
frontend/
├── src/
│ ├── components/ # React components
│ ├── pages/ # Page components
│ ├── hooks/ # Custom React hooks
│ ├── utils/
│ │ └── api.js # API client (axios)
│ └── App.jsx # Main app component
└── dist/ # Built static files
Agent Structure
agent-source-code/
├── cmd/patchmon-agent/
│ └── commands/
│ └── serve.go # WebSocket server
├── internal/
│ ├── client/ # HTTP client
│ ├── config/ # Configuration management
│ ├── integrations/ # Docker, compliance integrations
│ └── packages/ # Package manager interfaces
└── pkg/models/ # Data models
Deployment Architecture
Docker Compose Setup
graph TB
subgraph "Docker Network"
NginxContainer[Nginx Container<br/>Port 3000]
BackendContainer[Backend Container<br/>Port 3001]
FrontendContainer[Frontend Container<br/>Static Files]
PostgresContainer[PostgreSQL Container<br/>Port 5432]
RedisContainer[Redis Container<br/>Port 6379]
end
NginxContainer -->|Proxy| BackendContainer
NginxContainer -->|Serve| FrontendContainer
BackendContainer -->|Connect| PostgresContainer
BackendContainer -->|Connect| RedisContainer
Production Setup
graph TB
subgraph "Server"
Nginx[Nginx<br/>System Service<br/>Port 80/443]
Backend[Node.js Backend<br/>PM2/Systemd<br/>Port 3001]
Frontend[Static Files<br/>/opt/patchmon/frontend/dist]
Postgres[PostgreSQL<br/>System Service<br/>Port 5432]
Redis[Redis<br/>System Service<br/>Port 6379]
end
Internet -->|HTTPS| Nginx
Nginx -->|Proxy| Backend
Nginx -->|Serve| Frontend
Backend -->|Connect| Postgres
Backend -->|Connect| Redis
Performance Considerations
-
Database Connection Pooling
- Prisma connection pool (default: 10 connections)
- Optimized for multiple worker instances
-
Redis Connection Optimization
- Shared connection pool for BullMQ
- Connection reuse across queues and workers
-
WebSocket Connection Management
- Connection tracking by api_id
- Automatic reconnection handling
- Ping/pong keepalive (30s interval)
-
Job Processing
- Low concurrency (1 job/worker) to reduce load
- Exponential backoff for retries
- Job retention limits (50 completed, 20 failed)
-
Caching
- Integration state cache (60s TTL)
- Static asset caching (1 year)
- API response caching where appropriate
Security Architecture
Defense in Depth
-
Network Layer
- Nginx reverse proxy
- SSL/TLS encryption
- Security headers
- Rate limiting
-
Application Layer
- Input validation
- SQL injection prevention (Prisma ORM)
- XSS protection (React escaping)
- CSRF protection (SameSite cookies)
-
Authentication Layer
- Password hashing (bcrypt)
- Token encryption
- Session management
- 2FA support
-
Authorization Layer
- Role-based access control
- API scope validation
- IP restrictions
- Resource-level permissions
-
Data Layer
- Encrypted credentials storage
- Audit logging
- Data retention policies
- Secure credential transmission
Monitoring & Observability
Bull Board
- URL:
/bullboard(secured, requires authentication) - Features:
- Queue status (waiting, active, completed, failed)
- Job details and logs
- Worker status
- Retry management
Health Checks
- Endpoint:
/health - Checks:
- Database connectivity
- Redis connectivity
- Service status
Logging
- Winston logger (configurable)
- Log levels: error, warn, info, debug
- File and console outputs
- Structured JSON logging
Conclusion
This architecture provides:
- Scalability: Queue-based job processing, connection pooling
- Security: Multi-layer security, credential scoping, audit trails
- Reliability: Retry mechanisms, health checks, graceful shutdown
- Maintainability: Clear separation of concerns, modular design
- Observability: Bull Board monitoring, comprehensive logging
For specific implementation details, refer to the source code in the respective directories.