Web Package
August 3, 2026 · View on GitHub
Package: web
Path: /web/
Purpose: HTTP server, API endpoints, JWT authentication, and health monitoring
Overview
The web package provides a comprehensive HTTP interface for Ofelia, including RESTful API endpoints, JWT-based authentication, health checks, and security middleware. It exposes job management functionality through a web UI and API, with built-in rate limiting, CSRF protection, and secure authentication.
Key Components
1. Server
HTTP server with RESTful API for job management and monitoring.
type Server struct {
addr string
scheduler *core.Scheduler
config interface{}
srv *http.Server
origins map[string]string
client *dockerclient.Client
}
Features:
- RESTful API for job management
- Web UI with embedded static files
- Security middleware integration
- Rate limiting per IP (100 req/min)
- Graceful shutdown support
- Health check endpoints
Creation:
server := web.NewServer(":8080", scheduler, config, dockerClient)
2. Authentication System
JWT Authentication
Industry-standard JWT-based authentication with secure token management.
type JWTManager struct {
secretKey []byte
tokenExpiry time.Duration
}
type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
Features:
- HS256 signing algorithm
- Configurable expiry (default: hours)
- Token refresh capability
- HTTP middleware integration
- Cookie and header token support
Usage:
// Create JWT manager
jwtManager, err := web.NewJWTManager(secretKey, 24) // 24 hour expiry
if err != nil {
log.Fatal(err)
}
// Generate token
token, err := jwtManager.GenerateToken("admin")
// Validate token
claims, err := jwtManager.ValidateToken(token)
// Refresh token
newToken, err := jwtManager.RefreshToken(oldToken)
// Apply middleware
protectedHandler := jwtManager.Middleware(apiHandler)
Security Requirements:
- Secret key must be ≥32 characters
- Tokens include standard JWT claims (exp, iat, nbf, iss, sub)
- Automatic signing method validation
Secure Authentication
Enhanced authentication with bcrypt, rate limiting, and CSRF protection.
type SecureAuthConfig struct {
Enabled bool
Username string
PasswordHash string // bcrypt hash
SecretKey string
TokenExpiry int // hours
MaxAttempts int // per minute
}
Features:
- Bcrypt password hashing (cost 12)
- Constant-time username comparison
- Rate limiting per IP
- CSRF token protection
- Timing attack prevention
- Secure HTTP-only cookies
Password Hashing:
// Generate bcrypt hash (cost 12)
hash, err := web.HashPassword("mySecurePassword")
// Store hash in config
config.PasswordHash = hash
Rate Limiting:
// Create rate limiter: 5 attempts per minute
rateLimiter := web.NewRateLimiter(5, 5)
// Check if allowed
if !rateLimiter.Allow(clientIP) {
return errors.New("too many attempts")
}
CSRF Protection:
// Generate CSRF token
csrfToken, err := tokenManager.GenerateCSRFToken()
// Validate CSRF token (one-time use)
valid := tokenManager.ValidateCSRFToken(token)
3. Health Checks
Comprehensive health monitoring with Docker, scheduler, and system checks.
type HealthChecker struct {
startTime time.Time
dockerClient *docker.Client
version string
checks map[string]HealthCheck
checkInterval time.Duration // default: 30s
}
Health Status Levels:
const (
HealthStatusHealthy = "healthy"
HealthStatusDegraded = "degraded"
HealthStatusUnhealthy = "unhealthy"
)
Checks Performed:
- Docker Connectivity: Ping Docker daemon, get container count
- Scheduler Status:
healthywhile every configured job is scheduled.degraded— naming each one — as soon as the scheduler refuses a job, since an unparsable schedule or a duplicate name means that job never fires. A checker constructed without a scheduler is alsodegraded, rather than claiming health it cannot establish. - System Resources: Monitor memory usage (healthy <75%, degraded <90%, unhealthy ≥90%)
Usage:
// Create health checker. The scheduler may be nil, at the cost of the check
// above; the version is what /health reports.
healthChecker := web.NewHealthChecker(dockerClient, scheduler, cli.Version)
// Register health endpoints
server.RegisterHealthEndpoints(healthChecker)
// The constructor starts a background loop that re-runs every check every 30s.
// Stop ends it; the daemon calls this from a shutdown hook. Safe to call more
// than once.
defer healthChecker.Stop()
// Health endpoints available:
// GET /health - Detailed health information (always 200 OK)
// GET /healthz - Simple health check alias
// GET /ready - Readiness check (503 if unhealthy)
// GET /live - Liveness check (always 200 OK)
Health Response:
{
"status": "healthy",
"timestamp": "2025-01-15T10:30:00Z",
"uptimeSeconds": 3600.5,
"version": "v0.28.1",
"checks": {
"docker": {
"name": "docker",
"status": "healthy",
"message": "Docker 24.0.7 running with 5 containers",
"lastChecked": "2025-01-15T10:30:00Z",
"durationMs": 8332586
},
"scheduler": {
"name": "scheduler",
"status": "healthy",
"message": "Scheduler is operational",
"lastChecked": "2025-01-15T10:30:00Z",
"durationMs": 3180
},
"system": {
"name": "system",
"status": "healthy",
"message": "System resources normal",
"lastChecked": "2025-01-15T10:30:00Z",
"durationMs": 2
}
},
"system": {
"goVersion": "go1.23.5",
"goroutines": 12,
"cpus": 8,
"memoryAllocBytes": 45678900,
"memoryTotalBytes": 67890123,
"gcRuns": 45
}
}
4. Security Middleware
HTTP middleware for security headers and rate limiting.
Security Headers:
func securityHeaders(next http.Handler) http.Handler
Headers Applied:
X-Content-Type-Options: nosniff- Prevent MIME sniffingX-Frame-Options: DENY- Prevent clickjackingX-XSS-Protection: 1; mode=block- XSS protectionReferrer-Policy: strict-origin-when-cross-origin- Referrer controlContent-Security-Policy- CSP for XSS preventionStrict-Transport-Security- HSTS (when using HTTPS)
Rate Limiting:
type rateLimiter struct {
requests map[string][]time.Time
limit int // requests allowed
window time.Duration // time window
}
// Default: 100 requests per minute per IP
rl := newRateLimiter(100, time.Minute)
Features:
- Per-IP rate limiting
- Sliding window algorithm
- Automatic cleanup of old entries
- X-Forwarded-For support
5. API Endpoints
Job Management
| Endpoint | Method | Description |
|---|---|---|
/api/jobs | GET | List all active jobs |
/api/jobs/removed | GET | List removed jobs |
/api/jobs/disabled | GET | List disabled jobs |
/api/jobs/run | POST | Trigger job execution |
/api/jobs/disable | POST | Disable a job |
/api/jobs/enable | POST | Enable a job |
/api/jobs/create | POST | Create new job |
/api/jobs/update | POST | Update job configuration |
/api/jobs/delete | POST | Delete a job |
/api/jobs/{name}/history | GET | Get job execution history |
/api/config | GET | Get server configuration (jobs stripped) |
Job API Types
Job Response:
type apiJob struct {
Name string `json:"name"`
Type string `json:"type"` // "run", "exec", "local", "service", "compose"
Schedule string `json:"schedule"`
Command string `json:"command"`
LastRun *apiExecution `json:"lastRun,omitempty"`
Origin string `json:"origin"` // "config", "docker", "api"
Config json.RawMessage `json:"config"`
}
Execution Response:
type apiExecution struct {
Date time.Time `json:"date"`
Duration time.Duration `json:"duration"`
Failed bool `json:"failed"`
Skipped bool `json:"skipped"`
Error string `json:"error,omitempty"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
}
API Usage Examples
List All Jobs
GET /api/jobs
Response:
[
{
"name": "backup-db",
"type": "exec",
"schedule": "@daily",
"command": "pg_dump mydb",
"lastRun": {
"date": "2025-01-15T02:00:00Z",
"duration": 45200000000,
"failed": false,
"skipped": false,
"stdout": "Backup completed successfully",
"stderr": ""
},
"origin": "config"
}
]
Run Job Manually
POST /api/jobs/run
Content-Type: application/json
{
"name": "backup-db"
}
Response: 204 No Content on success
Create New Job
POST /api/jobs/create
Content-Type: application/json
{
"name": "new-job",
"type": "local",
"schedule": "0 */6 * * *",
"command": "/backup/script.sh"
}
Response: 201 Created on success
Get Job History
GET /api/jobs/backup-db/history
Response:
[
{
"date": "2025-01-15T02:00:00Z",
"duration": 45200000000,
"failed": false,
"skipped": false,
"stdout": "Backup completed",
"stderr": ""
},
{
"date": "2025-01-14T02:00:00Z",
"duration": 43100000000,
"failed": false,
"skipped": false,
"stdout": "Backup completed",
"stderr": ""
}
]
Authentication Flow
JWT Authentication
# 1. Generate token
POST /api/login
Content-Type: application/json
{
"username": "admin",
"password": "secure123"
}
# Response:
{
"token": "eyJhbGc...",
"expires_in": 86400
}
# 2. Use token in requests
GET /api/jobs
Authorization: Bearer eyJhbGc...
# 3. Refresh token before expiry
POST /api/refresh
Authorization: Bearer eyJhbGc...
# Response:
{
"token": "eyJhbGc...", // New token
"expires_in": 86400
}
Secure Authentication with CSRF
# 1. Login with CSRF protection
POST /api/login
Content-Type: application/json
X-CSRF-Token: abc123...
{
"username": "admin",
"password": "secure123"
}
# Response:
{
"token": "auth_token_here",
"csrf_token": "new_csrf_token",
"expires_in": 86400
}
# Cookie set: auth_token (HttpOnly, Secure, SameSite=Strict)
Server Configuration
Basic Setup
import (
"github.com/netresearch/ofelia/web"
"github.com/netresearch/ofelia/core"
)
func main() {
// Create scheduler
scheduler := core.NewScheduler()
// Create server
server := web.NewServer(":8080", scheduler, config, dockerClient)
// Create health checker
healthChecker := web.NewHealthChecker(dockerClient, scheduler, cli.Version)
defer healthChecker.Stop()
server.RegisterHealthEndpoints(healthChecker)
// Start server
if err := server.Start(); err != nil {
log.Fatal(err)
}
// Graceful shutdown
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
log.Printf("Server shutdown error: %v", err)
}
}
With JWT Authentication
// Create JWT manager
jwtManager, err := web.NewJWTManager(os.Getenv("OFELIA_JWT_SECRET"), 24)
if err != nil {
log.Fatal(err)
}
// Create server with JWT middleware
server := web.NewServer(":8080", scheduler, config, dockerClient)
// Apply JWT authentication to API routes
// (Implementation depends on routing strategy)
With Secure Authentication
// Create secure auth config
authConfig := &web.SecureAuthConfig{
Enabled: true,
Username: "admin",
PasswordHash: hashedPassword, // bcrypt hash
SecretKey: os.Getenv("OFELIA_SECRET_KEY"),
TokenExpiry: 24,
MaxAttempts: 5,
}
// Create token manager and rate limiter
tokenManager, _ := web.NewSecureTokenManager(authConfig.SecretKey, authConfig.TokenExpiry)
rateLimiter := web.NewRateLimiter(authConfig.MaxAttempts, authConfig.MaxAttempts)
// Create login handler
loginHandler := web.NewSecureLoginHandler(authConfig, tokenManager, rateLimiter)
Security Considerations
Password Security
- Bcrypt hashing: Cost factor 12 for security/performance balance
- Constant-time comparison: Prevents timing attacks on username
- Rate limiting: Prevent brute force (default: 5 attempts/minute)
- Delay on failure: 100ms delay to slow brute force
Token Security
- JWT secret key: Must be ≥32 characters for HS256
- Token expiry: Configurable (default: 24 hours)
- CSRF protection: One-time use tokens for state-changing operations
- Secure cookies: HttpOnly, Secure (HTTPS), SameSite=Strict
Network Security
- HTTPS enforcement: HSTS header when TLS detected
- Security headers: XSS protection, frame denial, CSP
- Rate limiting: 100 requests/minute per IP (configurable)
- Request timeouts: ReadHeader 5s, Write 60s, Idle 120s
Input Validation
- Method validation: Enforce POST for state changes
- Content-type checks: Validate JSON payloads
- Origin tracking: Track job creation source (config, docker, API)
Performance Considerations
Server Timeouts
server.srv = &http.Server{
Addr: ":8080",
ReadHeaderTimeout: 5 * time.Second, // Prevent slow header attacks
WriteTimeout: 60 * time.Second, // Long-running jobs need time
IdleTimeout: 120 * time.Second, // Keep-alive timeout
}
Rate Limiting
- Default: 100 requests/minute per IP
- Cleanup: Periodic cleanup every window duration
- Memory: O(n) where n = unique IPs in window
Health Checks
- Interval: 30 seconds (configurable)
- Overhead: <15ms per check cycle
- Docker ping: ~10ms
- System stats: ~2ms
Integration Points
Core Integration
- Scheduler: Job management operations
- Jobs: Job execution and history
- Docker Client: Container operations
Metrics Integration
- Prometheus Metrics: HTTP request metrics
- Endpoint:
/metricsfor Prometheus scraping
Logging Integration
- Structured Logging: Request/response logging
- Correlation IDs: Track requests across components
Testing
Health Check Testing
# Liveness probe (always succeeds if running)
curl http://localhost:8080/live
# Response: OK
# Readiness probe (checks dependencies)
curl http://localhost:8080/ready
# Response: {"status":"healthy",...}
# Detailed health check
curl http://localhost:8080/health
# Response: Full health report with all checks
API Testing
# Test rate limiting
for i in {1..150}; do
curl http://localhost:8080/api/jobs &
done
# Expected: 100 succeed, 50 fail with 429 Too Many Requests
Security Testing
# Test security headers
curl -I http://localhost:8080/
# Expected: X-Content-Type-Options, X-Frame-Options, etc.
# Test JWT authentication
curl http://localhost:8080/api/jobs
# Expected: 401 Unauthorized without token
curl -H "Authorization: Bearer <token>" http://localhost:8080/api/jobs
# Expected: 200 OK with valid token
Troubleshooting
Authentication Issues
Error: "JWT secret key must be at least 32 characters long"
Solution: Set OFELIA_JWT_SECRET environment variable with ≥32 chars
Error: "Invalid or expired token"
Solution: Generate new token via /api/login or refresh existing token
Error: "Too many login attempts"
Solution: Wait for rate limit window to reset (default: 1 minute)
Health Check Issues
Error: Docker check shows "unhealthy"
Solution: Verify Docker daemon is running and accessible
Error: System check shows "degraded" - memory usage high
Solution: Check memory usage, restart if >90% allocation
Server Issues
Error: "Address already in use"
Solution: Change port or stop conflicting process
Error: Rate limit exceeded
Solution: Reduce request rate or increase limit in server configuration
Related Documentation
- Core Package - Job execution and scheduling
- Metrics Package - HTTP metrics collection
- Logging Package - Request logging
- API Documentation - Complete API reference
- Security Considerations - Security best practices
- PROJECT_INDEX - Overall system architecture