clearctx 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1043 @@
1
+ ---
2
+ name: devops
3
+ description: Production-grade DevOps patterns for Docker, CI/CD, deployment strategies, Nginx, health checks, and infrastructure automation
4
+ domain: devops
5
+ keywords: [devops, docker, ci-cd, deployment, nginx, github-actions, health-checks, monitoring]
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # DevOps & Infrastructure Expertise
10
+
11
+ ## Worker Context
12
+
13
+ You are a DevOps specialist worker. Your role is to implement production-grade infrastructure configurations, containerization, CI/CD pipelines, deployment strategies, and monitoring solutions.
14
+
15
+ ### Dockerfile — Multi-Stage Builds & Layer Optimization
16
+
17
+ **CRITICAL:** NEVER use `:latest` tag in production. Pin exact versions.
18
+
19
+ **Layer caching strategy:**
20
+ 1. COPY package manifests FIRST (package.json, package-lock.json)
21
+ 2. RUN dependency installation (npm ci, yarn install --frozen-lockfile)
22
+ 3. COPY application source LAST
23
+
24
+ ```dockerfile
25
+ # GOOD — Optimized multi-stage build
26
+ FROM node:20.11.0-alpine AS builder
27
+ WORKDIR /app
28
+ COPY package*.json ./
29
+ RUN npm ci --only=production && npm cache clean --force
30
+ COPY src ./src
31
+ RUN npm run build
32
+
33
+ FROM node:20.11.0-alpine AS runner
34
+ WORKDIR /app
35
+ RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
36
+ COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
37
+ COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
38
+ COPY --chown=nodejs:nodejs package.json ./
39
+ USER nodejs
40
+ EXPOSE 3000
41
+ ENV NODE_ENV=production
42
+ CMD ["node", "dist/server.js"]
43
+
44
+ # BAD — Single stage, runs as root, uses :latest
45
+ FROM node:latest
46
+ WORKDIR /app
47
+ COPY . .
48
+ RUN npm install
49
+ CMD ["node", "server.js"]
50
+ ```
51
+
52
+ **IMPORTANT:** .dockerignore MUST exclude:
53
+ ```
54
+ node_modules
55
+ .git
56
+ .env
57
+ .env.*
58
+ !.env.example
59
+ *.log
60
+ coverage/
61
+ .DS_Store
62
+ README.md
63
+ ```
64
+
65
+ ### Docker Compose — Service Dependencies & Health Checks
66
+
67
+ **CRITICAL:** Use `condition: service_healthy` NOT `condition: service_started`. Services must be ready, not just running.
68
+
69
+ ```yaml
70
+ # GOOD — Health checks with dependency orchestration
71
+ version: '3.8'
72
+
73
+ services:
74
+ postgres:
75
+ image: postgres:16.1-alpine
76
+ environment:
77
+ POSTGRES_USER: ${DB_USER}
78
+ POSTGRES_PASSWORD: ${DB_PASSWORD}
79
+ POSTGRES_DB: ${DB_NAME}
80
+ volumes:
81
+ - postgres_data:/var/lib/postgresql/data
82
+ healthcheck:
83
+ test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
84
+ interval: 5s
85
+ timeout: 3s
86
+ retries: 5
87
+ networks:
88
+ - backend
89
+
90
+ redis:
91
+ image: redis:7.2.4-alpine
92
+ healthcheck:
93
+ test: ["CMD", "redis-cli", "ping"]
94
+ interval: 5s
95
+ timeout: 3s
96
+ retries: 5
97
+ networks:
98
+ - backend
99
+
100
+ api:
101
+ build:
102
+ context: .
103
+ dockerfile: Dockerfile
104
+ ports:
105
+ - "${API_PORT:-3000}:3000"
106
+ env_file:
107
+ - .env
108
+ depends_on:
109
+ postgres:
110
+ condition: service_healthy
111
+ redis:
112
+ condition: service_healthy
113
+ healthcheck:
114
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
115
+ interval: 10s
116
+ timeout: 5s
117
+ retries: 3
118
+ start_period: 30s
119
+ networks:
120
+ - backend
121
+
122
+ volumes:
123
+ postgres_data:
124
+
125
+ networks:
126
+ backend:
127
+ driver: bridge
128
+
129
+ # BAD — No health checks, vague depends_on
130
+ version: '3.8'
131
+ services:
132
+ db:
133
+ image: postgres
134
+ environment:
135
+ POSTGRES_PASSWORD: password123 # Hardcoded secret!
136
+
137
+ app:
138
+ build: .
139
+ depends_on:
140
+ - db # No condition — app starts before DB is ready
141
+ ports:
142
+ - "3000:3000"
143
+ ```
144
+
145
+ ### CI/CD Pipelines — GitHub Actions Pattern
146
+
147
+ **NEVER:** Hardcode secrets in workflow files. Use `${{ secrets.SECRET_NAME }}`.
148
+
149
+ ```yaml
150
+ # GOOD — Parallel stages with caching and secrets
151
+ name: CI/CD Pipeline
152
+
153
+ on:
154
+ push:
155
+ branches: [main, develop]
156
+ pull_request:
157
+ branches: [main]
158
+
159
+ env:
160
+ NODE_VERSION: '20.11.0'
161
+
162
+ jobs:
163
+ lint:
164
+ runs-on: ubuntu-latest
165
+ steps:
166
+ - uses: actions/checkout@v4
167
+
168
+ - name: Setup Node.js
169
+ uses: actions/setup-node@v4
170
+ with:
171
+ node-version: ${{ env.NODE_VERSION }}
172
+ cache: 'npm'
173
+
174
+ - name: Install dependencies
175
+ run: npm ci
176
+
177
+ - name: Run linter
178
+ run: npm run lint
179
+
180
+ test:
181
+ runs-on: ubuntu-latest
182
+ strategy:
183
+ matrix:
184
+ node-version: [18.x, 20.x]
185
+ steps:
186
+ - uses: actions/checkout@v4
187
+
188
+ - name: Setup Node.js ${{ matrix.node-version }}
189
+ uses: actions/setup-node@v4
190
+ with:
191
+ node-version: ${{ matrix.node-version }}
192
+ cache: 'npm'
193
+
194
+ - name: Install dependencies
195
+ run: npm ci
196
+
197
+ - name: Run tests
198
+ run: npm test -- --coverage
199
+
200
+ - name: Upload coverage
201
+ uses: codecov/codecov-action@v3
202
+ if: matrix.node-version == '20.x'
203
+
204
+ build:
205
+ runs-on: ubuntu-latest
206
+ needs: [lint, test]
207
+ steps:
208
+ - uses: actions/checkout@v4
209
+
210
+ - name: Setup Node.js
211
+ uses: actions/setup-node@v4
212
+ with:
213
+ node-version: ${{ env.NODE_VERSION }}
214
+ cache: 'npm'
215
+
216
+ - name: Install dependencies
217
+ run: npm ci
218
+
219
+ - name: Build application
220
+ run: npm run build
221
+
222
+ - name: Upload build artifacts
223
+ uses: actions/upload-artifact@v4
224
+ with:
225
+ name: dist
226
+ path: dist/
227
+
228
+ deploy:
229
+ runs-on: ubuntu-latest
230
+ needs: build
231
+ if: github.ref == 'refs/heads/main'
232
+ environment: production
233
+ steps:
234
+ - uses: actions/checkout@v4
235
+
236
+ - name: Download build artifacts
237
+ uses: actions/download-artifact@v4
238
+ with:
239
+ name: dist
240
+ path: dist/
241
+
242
+ - name: Deploy to production
243
+ env:
244
+ DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
245
+ DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
246
+ DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
247
+ run: |
248
+ echo "$DEPLOY_KEY" > deploy_key
249
+ chmod 600 deploy_key
250
+ rsync -avz -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \
251
+ dist/ $DEPLOY_USER@$DEPLOY_HOST:/var/www/app/
252
+
253
+ # BAD — Sequential jobs, no caching, secrets exposed
254
+ jobs:
255
+ build-and-deploy:
256
+ runs-on: ubuntu-latest
257
+ steps:
258
+ - uses: actions/checkout@v4
259
+ - run: npm install # Should use npm ci
260
+ - run: npm run lint
261
+ - run: npm test
262
+ - run: npm run build
263
+ - run: |
264
+ sshpass -p 'mypassword' ssh user@server.com # SECRET IN CODE!
265
+ ```
266
+
267
+ ### Environment Management
268
+
269
+ **CRITICAL:** NEVER commit `.env` files with real values. Commit `.env.example` with placeholder values only.
270
+
271
+ ```bash
272
+ # GOOD — .env.example (committed to git)
273
+ NODE_ENV=development
274
+ PORT=3000
275
+ DATABASE_URL=postgresql://user:password@localhost:5432/dbname
276
+ REDIS_URL=redis://localhost:6379
277
+ JWT_SECRET=your-secret-key-here
278
+ API_KEY=your-api-key-here
279
+
280
+ # GOOD — Config validation on startup (src/config.js)
281
+ const requiredEnvVars = [
282
+ 'NODE_ENV',
283
+ 'DATABASE_URL',
284
+ 'REDIS_URL',
285
+ 'JWT_SECRET'
286
+ ];
287
+
288
+ for (const envVar of requiredEnvVars) {
289
+ if (!process.env[envVar]) {
290
+ throw new Error(`Missing required environment variable: ${envVar}`);
291
+ }
292
+ }
293
+
294
+ const config = {
295
+ env: process.env.NODE_ENV,
296
+ port: parseInt(process.env.PORT, 10) || 3000,
297
+ db: {
298
+ url: process.env.DATABASE_URL,
299
+ pool: {
300
+ min: 2,
301
+ max: 10
302
+ }
303
+ },
304
+ redis: {
305
+ url: process.env.REDIS_URL
306
+ },
307
+ jwt: {
308
+ secret: process.env.JWT_SECRET,
309
+ expiresIn: '7d'
310
+ }
311
+ };
312
+
313
+ module.exports = config;
314
+ ```
315
+
316
+ **Environment parity checklist:**
317
+ - [ ] Same Node.js version across dev/staging/prod
318
+ - [ ] Same dependency versions (use package-lock.json)
319
+ - [ ] Same database version
320
+ - [ ] Same environment variable names
321
+ - [ ] Secrets stored in CI/CD vault, not in code
322
+
323
+ ### Deployment Strategies — Decision Tree
324
+
325
+ | Strategy | Use When | Downtime | Rollback Speed | Complexity | Resource Cost |
326
+ |----------|----------|----------|----------------|------------|---------------|
327
+ | **Rolling** | Default for most apps | Minimal (partial) | Medium (redeploy previous) | Low | 1x |
328
+ | **Blue-Green** | Zero-downtime critical, need instant rollback | None | Instant (switch routing) | Medium | 2x |
329
+ | **Canary** | High-risk changes, gradual rollout | None | Fast (route 100% to stable) | High | 1.1-1.5x |
330
+ | **Recreate** | Dev/staging only, breaking changes | Full | Slow (redeploy) | Very Low | 1x |
331
+
332
+ **IMPORTANT:** Choose based on:
333
+ 1. Acceptable downtime: None → Blue-Green or Canary
334
+ 2. Resource budget: Limited → Rolling
335
+ 3. Risk level: High → Canary
336
+ 4. Rollback criticality: Instant needed → Blue-Green
337
+
338
+ ```yaml
339
+ # GOOD — Rolling deployment (Kubernetes)
340
+ apiVersion: apps/v1
341
+ kind: Deployment
342
+ metadata:
343
+ name: api
344
+ spec:
345
+ replicas: 4
346
+ strategy:
347
+ type: RollingUpdate
348
+ rollingUpdate:
349
+ maxSurge: 1 # Max 1 extra pod during update
350
+ maxUnavailable: 1 # Max 1 pod down during update
351
+ template:
352
+ spec:
353
+ containers:
354
+ - name: api
355
+ image: myapp:v2.0.0
356
+ readinessProbe:
357
+ httpGet:
358
+ path: /ready
359
+ port: 3000
360
+ initialDelaySeconds: 5
361
+ periodSeconds: 5
362
+ livenessProbe:
363
+ httpGet:
364
+ path: /health
365
+ Port: 3000
366
+ initialDelaySeconds: 15
367
+ periodSeconds: 10
368
+ ```
369
+
370
+ ### Nginx — Reverse Proxy & Static Serving
371
+
372
+ **IMPORTANT:** Always set proper proxy headers for upstream to get real client IP.
373
+
374
+ ```nginx
375
+ # GOOD — Production Nginx config
376
+ upstream api_backend {
377
+ least_conn;
378
+ server api1:3000 max_fails=3 fail_timeout=30s;
379
+ server api2:3000 max_fails=3 fail_timeout=30s;
380
+ server api3:3000 max_fails=3 fail_timeout=30s;
381
+ }
382
+
383
+ # Rate limiting zone
384
+ limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
385
+
386
+ server {
387
+ listen 80;
388
+ server_name api.example.com;
389
+
390
+ # Redirect HTTP to HTTPS
391
+ return 301 https://$server_name$request_uri;
392
+ }
393
+
394
+ server {
395
+ listen 443 ssl http2;
396
+ server_name api.example.com;
397
+
398
+ # SSL certificates (Let's Encrypt)
399
+ ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
400
+ ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
401
+ ssl_protocols TLSv1.2 TLSv1.3;
402
+ ssl_ciphers HIGH:!aNULL:!MD5;
403
+
404
+ # Security headers
405
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
406
+ add_header X-Frame-Options "SAMEORIGIN" always;
407
+ add_header X-Content-Type-Options "nosniff" always;
408
+ add_header X-XSS-Protection "1; mode=block" always;
409
+
410
+ # Static files
411
+ location /static/ {
412
+ alias /var/www/static/;
413
+ expires 1y;
414
+ access_log off;
415
+ add_header Cache-Control "public, immutable";
416
+ try_files $uri $uri/ =404;
417
+ }
418
+
419
+ # API proxy with rate limiting
420
+ location /api/ {
421
+ limit_req zone=api_limit burst=20 nodelay;
422
+
423
+ proxy_pass http://api_backend;
424
+ proxy_http_version 1.1;
425
+
426
+ # Preserve original request info
427
+ proxy_set_header Host $host;
428
+ proxy_set_header X-Real-IP $remote_addr;
429
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
430
+ proxy_set_header X-Forwarded-Proto $scheme;
431
+ proxy_set_header X-Request-ID $request_id;
432
+
433
+ # Timeouts
434
+ proxy_connect_timeout 5s;
435
+ proxy_send_timeout 60s;
436
+ proxy_read_timeout 60s;
437
+
438
+ # Buffering
439
+ proxy_buffering on;
440
+ proxy_buffer_size 4k;
441
+ proxy_buffers 8 4k;
442
+ }
443
+
444
+ # Health check endpoint (no rate limiting)
445
+ location /health {
446
+ access_log off;
447
+ proxy_pass http://api_backend;
448
+ }
449
+ }
450
+
451
+ # BAD — Insecure, no rate limiting, missing headers
452
+ server {
453
+ listen 80;
454
+ server_name api.example.com;
455
+
456
+ location / {
457
+ proxy_pass http://localhost:3000; # No upstream, hardcoded port
458
+ }
459
+ }
460
+ ```
461
+
462
+ ### Health Checks — Liveness vs Readiness
463
+
464
+ **CRITICAL distinction:**
465
+ - **Liveness:** Is the process alive? (Kill and restart if fails)
466
+ - **Readiness:** Can it serve traffic? (Remove from load balancer if fails)
467
+
468
+ ```javascript
469
+ // GOOD — Comprehensive health check implementation
470
+ const express = require('express');
471
+ const app = express();
472
+
473
+ // Liveness probe — lightweight, just confirms process is running
474
+ app.get('/health', (req, res) => {
475
+ res.status(200).json({
476
+ status: 'ok',
477
+ timestamp: new Date().toISOString(),
478
+ uptime: process.uptime(),
479
+ version: process.env.APP_VERSION || 'unknown'
480
+ });
481
+ });
482
+
483
+ // Readiness probe — checks all dependencies
484
+ app.get('/ready', async (req, res) => {
485
+ const checks = {
486
+ database: false,
487
+ redis: false,
488
+ diskSpace: false
489
+ };
490
+
491
+ try {
492
+ // Check database connection
493
+ await db.query('SELECT 1');
494
+ checks.database = true;
495
+
496
+ // Check Redis connection
497
+ await redis.ping();
498
+ checks.redis = true;
499
+
500
+ // Check disk space (optional but recommended)
501
+ const diskUsage = await checkDiskSpace('/');
502
+ checks.diskSpace = diskUsage.percentUsed < 90;
503
+
504
+ const allReady = Object.values(checks).every(status => status === true);
505
+
506
+ res.status(allReady ? 200 : 503).json({
507
+ status: allReady ? 'ready' : 'not_ready',
508
+ checks,
509
+ timestamp: new Date().toISOString()
510
+ });
511
+ } catch (error) {
512
+ res.status(503).json({
513
+ status: 'not_ready',
514
+ checks,
515
+ error: error.message,
516
+ timestamp: new Date().toISOString()
517
+ });
518
+ }
519
+ });
520
+
521
+ // BAD — Single endpoint, no dependency checks
522
+ app.get('/health', (req, res) => {
523
+ res.send('OK'); // No JSON, no version info, doesn't check DB/Redis
524
+ });
525
+ ```
526
+
527
+ ### Logging & Monitoring
528
+
529
+ **IMPORTANT:** Use structured JSON logs for production. Include request IDs for correlation.
530
+
531
+ ```javascript
532
+ // GOOD — Structured logging with request ID correlation
533
+ const winston = require('winston');
534
+ const { v4: uuidv4 } = require('uuid');
535
+
536
+ const logger = winston.createLogger({
537
+ level: process.env.LOG_LEVEL || 'info',
538
+ format: winston.format.combine(
539
+ winston.format.timestamp(),
540
+ winston.format.json()
541
+ ),
542
+ defaultMeta: {
543
+ service: 'api',
544
+ version: process.env.APP_VERSION,
545
+ environment: process.env.NODE_ENV
546
+ },
547
+ transports: [
548
+ new winston.transports.Console()
549
+ ]
550
+ });
551
+
552
+ // Request ID middleware
553
+ app.use((req, res, next) => {
554
+ req.id = req.headers['x-request-id'] || uuidv4();
555
+ res.setHeader('x-request-id', req.id);
556
+ next();
557
+ });
558
+
559
+ // Request logging middleware
560
+ app.use((req, res, next) => {
561
+ const start = Date.now();
562
+
563
+ res.on('finish', () => {
564
+ const duration = Date.now() - start;
565
+ logger.info('HTTP request', {
566
+ requestId: req.id,
567
+ method: req.method,
568
+ path: req.path,
569
+ statusCode: res.statusCode,
570
+ duration,
571
+ userAgent: req.headers['user-agent'],
572
+ ip: req.headers['x-real-ip'] || req.ip
573
+ });
574
+ });
575
+
576
+ next();
577
+ });
578
+
579
+ // Prometheus metrics endpoint
580
+ const client = require('prom-client');
581
+ const register = new client.Registry();
582
+
583
+ const httpRequestDuration = new client.Histogram({
584
+ name: 'http_request_duration_seconds',
585
+ help: 'Duration of HTTP requests in seconds',
586
+ labelNames: ['method', 'route', 'status_code'],
587
+ buckets: [0.1, 0.5, 1, 2, 5]
588
+ });
589
+
590
+ const httpRequestTotal = new client.Counter({
591
+ name: 'http_requests_total',
592
+ help: 'Total number of HTTP requests',
593
+ labelNames: ['method', 'route', 'status_code']
594
+ });
595
+
596
+ register.registerMetric(httpRequestDuration);
597
+ register.registerMetric(httpRequestTotal);
598
+
599
+ app.get('/metrics', async (req, res) => {
600
+ res.set('Content-Type', register.contentType);
601
+ res.end(await register.metrics());
602
+ });
603
+
604
+ // BAD — Unstructured logs, no correlation
605
+ console.log('User logged in'); // No timestamp, no user ID, no request context
606
+ ```
607
+
608
+ **Log levels decision tree:**
609
+ | Level | Use For | Production Volume |
610
+ |-------|---------|-------------------|
611
+ | error | System failures, unhandled exceptions | Low |
612
+ | warn | Recoverable issues, deprecated usage | Low-Medium |
613
+ | info | Business events, HTTP requests | Medium |
614
+ | debug | Detailed app flow, variable values | High (disable in prod) |
615
+
616
+ ### Database Migrations in CI/CD
617
+
618
+ **CRITICAL:** Run migrations BEFORE deploying new code. Migration failures must block deployment.
619
+
620
+ ```yaml
621
+ # GOOD — Separate migration job in CI/CD
622
+ jobs:
623
+ migrate:
624
+ runs-on: ubuntu-latest
625
+ environment: production
626
+ steps:
627
+ - uses: actions/checkout@v4
628
+
629
+ - name: Setup Node.js
630
+ uses: actions/setup-node@v4
631
+ with:
632
+ node-version: '20.11.0'
633
+
634
+ - name: Install dependencies
635
+ run: npm ci
636
+
637
+ - name: Run database migrations
638
+ env:
639
+ DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
640
+ run: npm run migrate
641
+ timeout-minutes: 5
642
+
643
+ - name: Verify migration success
644
+ env:
645
+ DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
646
+ run: npm run migrate:status
647
+
648
+ deploy:
649
+ runs-on: ubuntu-latest
650
+ needs: migrate # Deploy ONLY if migrations succeed
651
+ environment: production
652
+ steps:
653
+ - name: Deploy application
654
+ run: ./deploy.sh
655
+ ```
656
+
657
+ **NEVER do this:**
658
+ ```javascript
659
+ // BAD — Running migrations in application startup
660
+ async function startServer() {
661
+ await runMigrations(); // ❌ Causes race conditions with multiple instances
662
+ app.listen(3000);
663
+ }
664
+ ```
665
+
666
+ **Migration rollback strategy:**
667
+ - Each migration must have a `down()` method
668
+ - Test rollback in staging BEFORE production
669
+ - Automate rollback on deployment failure
670
+ - Keep migrations backward-compatible when possible
671
+
672
+ ### Git Workflow & Conventional Commits
673
+
674
+ **Branch naming convention:**
675
+ ```
676
+ feature/TICKET-123-add-user-auth
677
+ bugfix/TICKET-456-fix-memory-leak
678
+ hotfix/TICKET-789-patch-security-vuln
679
+ chore/update-dependencies
680
+ docs/api-documentation
681
+ ```
682
+
683
+ **Conventional commits format:**
684
+ ```
685
+ <type>(<scope>): <subject>
686
+
687
+ <body>
688
+
689
+ <footer>
690
+ ```
691
+
692
+ **Types:**
693
+ - `feat:` New feature
694
+ - `fix:` Bug fix
695
+ - `chore:` Maintenance (deps, config)
696
+ - `docs:` Documentation only
697
+ - `refactor:` Code change without behavior change
698
+ - `test:` Adding or updating tests
699
+ - `ci:` CI/CD pipeline changes
700
+ - `perf:` Performance improvement
701
+
702
+ **Example:**
703
+ ```
704
+ feat(api): add user authentication endpoint
705
+
706
+ Implement JWT-based authentication with refresh tokens.
707
+ Includes rate limiting and brute-force protection.
708
+
709
+ Closes #123
710
+ ```
711
+
712
+ **Protected main branch settings:**
713
+ - ✅ Require pull request reviews (min 1)
714
+ - ✅ Require status checks to pass (CI pipeline)
715
+ - ✅ Require branches to be up to date
716
+ - ✅ Include administrators in restrictions
717
+ - ❌ Allow force pushes
718
+ - ❌ Allow deletions
719
+
720
+ ## Conventions
721
+
722
+ ### Response Formats
723
+ **API health/ready responses:**
724
+ ```json
725
+ {
726
+ "status": "ok" | "ready" | "not_ready",
727
+ "timestamp": "ISO 8601 string",
728
+ "checks": { "database": true, "redis": true },
729
+ "version": "semver string"
730
+ }
731
+ ```
732
+
733
+ ### Naming Conventions
734
+ - Environment variables: `SCREAMING_SNAKE_CASE`
735
+ - Docker image tags: `semver` (e.g., `v1.2.3`) or `git-sha` (e.g., `abc1234`)
736
+ - Kubernetes resources: `kebab-case`
737
+ - Secrets in CI/CD: `UPPER_SNAKE_CASE` with service prefix (e.g., `DB_PASSWORD`, `API_KEY`)
738
+
739
+ ### File Paths
740
+ - Always use relative paths in configs
741
+ - Use environment variables for absolute paths that differ per environment
742
+ - Example: `${PROJECT_ROOT}/dist` not `/home/user/project/dist`
743
+
744
+ ### Port Assignments
745
+ | Service | Default Port | Environment Variable |
746
+ |---------|--------------|---------------------|
747
+ | API | 3000 | `API_PORT` |
748
+ | Frontend | 8080 | `FRONTEND_PORT` |
749
+ | PostgreSQL | 5432 | `DB_PORT` |
750
+ | Redis | 6379 | `REDIS_PORT` |
751
+ | Nginx | 80/443 | N/A |
752
+
753
+ ## Common Patterns
754
+
755
+ ### Pattern 1: Multi-Stage Docker Build with Non-Root User
756
+ ```dockerfile
757
+ FROM node:20.11.0-alpine AS builder
758
+ WORKDIR /app
759
+ COPY package*.json ./
760
+ RUN npm ci --only=production
761
+ COPY . .
762
+ RUN npm run build
763
+
764
+ FROM node:20.11.0-alpine AS runner
765
+ RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
766
+ WORKDIR /app
767
+ COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
768
+ COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
769
+ USER nodejs
770
+ EXPOSE 3000
771
+ CMD ["node", "dist/server.js"]
772
+ ```
773
+
774
+ ### Pattern 2: Docker Compose with Health-Based Dependencies
775
+ ```yaml
776
+ version: '3.8'
777
+ services:
778
+ db:
779
+ image: postgres:16.1-alpine
780
+ healthcheck:
781
+ test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
782
+ interval: 5s
783
+ timeout: 3s
784
+ retries: 5
785
+
786
+ api:
787
+ build: .
788
+ depends_on:
789
+ db:
790
+ condition: service_healthy
791
+ ```
792
+
793
+ ### Pattern 3: GitHub Actions Parallel Pipeline
794
+ ```yaml
795
+ jobs:
796
+ lint:
797
+ runs-on: ubuntu-latest
798
+ steps:
799
+ - uses: actions/checkout@v4
800
+ - uses: actions/setup-node@v4
801
+ with:
802
+ node-version: '20.11.0'
803
+ cache: 'npm'
804
+ - run: npm ci
805
+ - run: npm run lint
806
+
807
+ test:
808
+ runs-on: ubuntu-latest
809
+ steps:
810
+ - uses: actions/checkout@v4
811
+ - uses: actions/setup-node@v4
812
+ with:
813
+ node-version: '20.11.0'
814
+ cache: 'npm'
815
+ - run: npm ci
816
+ - run: npm test
817
+
818
+ deploy:
819
+ needs: [lint, test]
820
+ runs-on: ubuntu-latest
821
+ if: github.ref == 'refs/heads/main'
822
+ steps:
823
+ - name: Deploy
824
+ run: ./deploy.sh
825
+ ```
826
+
827
+ ### Pattern 4: Nginx Reverse Proxy with Rate Limiting
828
+ ```nginx
829
+ limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
830
+
831
+ upstream api_backend {
832
+ least_conn;
833
+ server api1:3000;
834
+ server api2:3000;
835
+ }
836
+
837
+ server {
838
+ listen 443 ssl http2;
839
+
840
+ location /api/ {
841
+ limit_req zone=api_limit burst=20 nodelay;
842
+ proxy_pass http://api_backend;
843
+ proxy_set_header X-Real-IP $remote_addr;
844
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
845
+ }
846
+ }
847
+ ```
848
+
849
+ ### Pattern 5: Comprehensive Health Checks
850
+ ```javascript
851
+ app.get('/health', (req, res) => {
852
+ res.status(200).json({
853
+ status: 'ok',
854
+ uptime: process.uptime(),
855
+ version: process.env.APP_VERSION
856
+ });
857
+ });
858
+
859
+ app.get('/ready', async (req, res) => {
860
+ const checks = {
861
+ database: await checkDB(),
862
+ redis: await checkRedis()
863
+ };
864
+ const ready = Object.values(checks).every(v => v === true);
865
+ res.status(ready ? 200 : 503).json({
866
+ status: ready ? 'ready' : 'not_ready',
867
+ checks
868
+ });
869
+ });
870
+ ```
871
+
872
+ ## Anti-Patterns
873
+
874
+ ### Anti-Pattern 1: Secrets in Dockerfile or Code
875
+ ```dockerfile
876
+ # BAD — Secret hardcoded in Dockerfile
877
+ ENV DATABASE_PASSWORD=super_secret_password
878
+ ENV API_KEY=sk-1234567890abcdef
879
+
880
+ # GOOD — Secrets injected at runtime
881
+ ENV DATABASE_PASSWORD=${DATABASE_PASSWORD}
882
+ ENV API_KEY=${API_KEY}
883
+ ```
884
+
885
+ ```javascript
886
+ // BAD — Secret hardcoded in code
887
+ const dbPassword = 'mypassword123';
888
+
889
+ // GOOD — Secret from environment variable
890
+ const dbPassword = process.env.DATABASE_PASSWORD;
891
+ if (!dbPassword) throw new Error('DATABASE_PASSWORD not set');
892
+ ```
893
+
894
+ ### Anti-Pattern 2: Running as Root in Containers
895
+ ```dockerfile
896
+ # BAD — Runs as root (default)
897
+ FROM node:20-alpine
898
+ WORKDIR /app
899
+ COPY . .
900
+ CMD ["node", "server.js"]
901
+
902
+ # GOOD — Creates and uses non-root user
903
+ FROM node:20-alpine
904
+ RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
905
+ WORKDIR /app
906
+ COPY --chown=nodejs:nodejs . .
907
+ USER nodejs
908
+ CMD ["node", "server.js"]
909
+ ```
910
+
911
+ ### Anti-Pattern 3: No Health Checks
912
+ ```yaml
913
+ # BAD — No health checks, depends_on without condition
914
+ services:
915
+ db:
916
+ image: postgres:16-alpine
917
+
918
+ api:
919
+ build: .
920
+ depends_on:
921
+ - db # Starts when db container starts, NOT when postgres is ready
922
+
923
+ # GOOD — Health checks with service_healthy condition
924
+ services:
925
+ db:
926
+ image: postgres:16-alpine
927
+ healthcheck:
928
+ test: ["CMD-SHELL", "pg_isready"]
929
+ interval: 5s
930
+ retries: 5
931
+
932
+ api:
933
+ build: .
934
+ depends_on:
935
+ db:
936
+ condition: service_healthy
937
+ ```
938
+
939
+ ### Anti-Pattern 4: Manual SSH Deployments
940
+ ```bash
941
+ # BAD — Manual deployment script
942
+ sshpass -p 'password' ssh user@server.com "cd /var/www && git pull && npm install && pm2 restart app"
943
+
944
+ # GOOD — Automated CI/CD deployment with secrets vault
945
+ # In GitHub Actions:
946
+ steps:
947
+ - name: Deploy via SSH
948
+ env:
949
+ SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
950
+ HOST: ${{ secrets.DEPLOY_HOST }}
951
+ run: |
952
+ echo "$SSH_KEY" > key
953
+ chmod 600 key
954
+ ssh -i key $HOST './deploy.sh'
955
+ ```
956
+
957
+ ### Anti-Pattern 5: Monolithic CI Jobs
958
+ ```yaml
959
+ # BAD — One giant job that does everything sequentially
960
+ jobs:
961
+ build-test-deploy:
962
+ runs-on: ubuntu-latest
963
+ steps:
964
+ - run: npm run lint
965
+ - run: npm test
966
+ - run: npm run build
967
+ - run: npm run deploy # If any step fails, all fail. No parallelization.
968
+
969
+ # GOOD — Separate jobs that run in parallel
970
+ jobs:
971
+ lint:
972
+ runs-on: ubuntu-latest
973
+ steps:
974
+ - run: npm run lint
975
+
976
+ test:
977
+ runs-on: ubuntu-latest
978
+ steps:
979
+ - run: npm test
980
+
981
+ deploy:
982
+ needs: [lint, test] # Runs only after both pass
983
+ runs-on: ubuntu-latest
984
+ steps:
985
+ - run: npm run deploy
986
+ ```
987
+
988
+ ## Integration Notes
989
+
990
+ ### Team Communication (if multi-session worker)
991
+ 1. **Before starting:** Call `team_check_inbox()` to check for messages from orchestrator or other workers
992
+ 2. **Read conventions:** Call `artifact_read({ artifactId: "shared-conventions" })` to get project-wide naming, format, and behavior standards
993
+ 3. **Publish infrastructure artifact:**
994
+ ```javascript
995
+ artifact_publish({
996
+ artifactId: "infrastructure",
997
+ title: "Infrastructure Configuration",
998
+ data: {
999
+ files: [
1000
+ "Dockerfile",
1001
+ "docker-compose.yml",
1002
+ ".dockerignore",
1003
+ ".github/workflows/ci-cd.yml",
1004
+ "nginx/nginx.conf"
1005
+ ],
1006
+ ports: {
1007
+ api: 3000,
1008
+ nginx: 80,
1009
+ nginxSsl: 443
1010
+ },
1011
+ healthEndpoints: {
1012
+ liveness: "/health",
1013
+ readiness: "/ready",
1014
+ metrics: "/metrics"
1015
+ },
1016
+ environmentVars: [
1017
+ "NODE_ENV",
1018
+ "DATABASE_URL",
1019
+ "REDIS_URL",
1020
+ "JWT_SECRET",
1021
+ "API_PORT",
1022
+ "APP_VERSION"
1023
+ ],
1024
+ deploymentStrategy: "rolling",
1025
+ cicdPipeline: ".github/workflows/ci-cd.yml"
1026
+ }
1027
+ })
1028
+ ```
1029
+ 4. **Coordinate with other workers:**
1030
+ - **Backend worker:** Confirm API port, health check endpoints (`/health`, `/ready`), metrics endpoint (`/metrics`)
1031
+ - **Database worker:** Get migration script paths, connection string format, health check query
1032
+ - **Frontend worker:** Confirm static asset paths for Nginx serving
1033
+ 5. **Broadcast completion:** Call `team_broadcast({ from: "devops", content: "Infrastructure setup complete. Dockerfile, docker-compose.yml, CI/CD pipeline, and Nginx config ready." })`
1034
+ 6. **Use relative paths only:** All file paths in artifacts must be relative to project root (e.g., `src/config.js` NOT `/home/user/project/src/config.js`)
1035
+
1036
+ ### Standalone Worker Mode
1037
+ If operating independently (no team tools available):
1038
+ 1. Create infrastructure files in project root: `Dockerfile`, `docker-compose.yml`, `.dockerignore`
1039
+ 2. Create `.github/workflows/ci-cd.yml` for GitHub Actions pipeline
1040
+ 3. Create `nginx/nginx.conf` if web server config is needed
1041
+ 4. Create `.env.example` with all required environment variables (no real values)
1042
+ 5. Verify all files use relative paths
1043
+ 6. Report completion with list of created files and configuration summary