Build Doctrine: Everything is an event. Execution is stateless. Truth is reconstructable.
This system is built following a strict A→Z build doctrine to ensure zero rework and production-grade reliability:
- Event Sourcing: All state changes are immutable events (append-only)
- Stateless Workers: Workers process jobs independently, no shared state
- Idempotency: Same job cannot run twice (deduplication guard)
- Traceability: Correlation IDs propagate through entire system
- Pluggable Executors: Support for SSH, Docker, Kubernetes
- Projections: Read models derived from events for fast queries
API → Event Store → Queue → Worker → Executor
↓
Observability (Logs, Traces)
↓
Projections (Read Models)
↓
UI
- Docker Compose with PostgreSQL, Redis, API, Worker
- Immutable event store
- Job model with idempotency
- BullMQ queue
- Pluggable executors (SSH)
- Event-driven job lifecycle
- JWT authentication
- RBAC (admin/operator/viewer)
- Correlation ID propagation
- Structured logging
- Retry engine with exponential backoff
- Structured JSON logs
- Projections (read models)
- React dashboard
- Docker Compose
- Kubernetes Helm charts
- Vault integration
- Audit logging
- Testing & validation
# Install dependencies
pnpm install
# Start services
docker-compose up -d
# Run migrations
docker-compose exec api node scripts/migrate.js
# Create job
curl -X POST http://localhost:3000/jobs \
-H "Content-Type: application/json" \
-d '{
"idempotencyKey": "job-1",
"target": "user@host:22",
"executor": "ssh",
"command": "echo hello"
}'
# Check status
curl http://localhost:3000/jobs/{jobId}POST /login- Get JWT token
POST /jobs- Create job (with idempotency)GET /jobs/:jobId- Get job status (from projection)GET /jobs/:jobId/events- Get job event history
JOB_CREATED- Job createdJOB_STARTED- Execution startedJOB_SUCCESS- Completed successfullyJOB_FAILED- Execution failedJOB_RETRY- Scheduled for retryJOB_FINAL_FAILED- Max retries exceeded
server/
├── api.ts # Express API server
├── worker.ts # Worker entry point
├── db/
│ ├── schema.sql # Event store schema
│ ├── events.ts # Event store service
│ └── jobs.ts # Job store service
├── queue/
│ └── queue.ts # BullMQ queue
├── executors/
│ ├── executor.ts # Executor interface
│ └── ssh.executor.ts # SSH executor
├── worker/
│ └── processor.ts # Job processor
├── auth/
│ └── jwt.ts # JWT authentication
├── logging/
│ └── logger.ts # Structured logging
└── projections/
└── builder.ts # Projection builder
# Watch mode
pnpm dev
# Worker
pnpm worker
# Tests
pnpm test
# Lint
pnpm lint# Build Docker images
docker build -f Dockerfile.api -t opsforge-api:latest .
docker build -f Dockerfile.worker -t opsforge-worker:latest .
# Deploy to Kubernetes
helm install opsforge ./infra/helm- Complete audit trail
- Full state reconstruction
- Debuggable failures
- Compliance-ready
- Horizontal scaling
- Crash recovery
- No shared state
- Independent processing
- Safe retries
- Duplicate prevention
- Exactly-once semantics
- Request tracing
- Debugging support
- Observability
- ❌ Skipping event model → system collapses later
- ❌ Mixing execution into API → kills scalability
- ❌ No idempotency → duplicate chaos
- ❌ No correlation → impossible debugging
- ❌ Adding UI too early → wrong architecture
MIT