From 1ece74fca0d404e8a673ea8d9cf12fbe45e64143 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:38:47 +0500 Subject: [PATCH 01/17] feat: codify PHP limits and block .user.ini per Task D152 & #264 --- wordpress/helm/templates/ingress.yaml | 146 ++++++++++-------- .../helm/templates/php-config-configmap.yaml | 34 ++-- 2 files changed, 96 insertions(+), 84 deletions(-) diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 64f8671..14b0c61 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,68 +1,78 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "wordpress.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} - app.kubernetes.io/component: ingress - annotations: - {{- toYaml .Values.ingress.annotations | nindent 4 }} - # TLS/SSL Security (Enterprise-Grade, Parameterized) - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} - nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} - nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} - {{- else }} - nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" - nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - {{- end }} - {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} - # Redirect root domain to www subdomain (using from-to-www annotation) - nginx.ingress.kubernetes.io/from-to-www-redirect: "true" - {{- end }} - # WordPress-specific optimizations - nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" - nginx.ingress.kubernetes.io/proxy-buffers-number: "8" - nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" - nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" - # Rate limiting for brute force protection - nginx.ingress.kubernetes.io/rate-limit: "100" - nginx.ingress.kubernetes.io/rate-limit-window: "1m" - nginx.ingress.kubernetes.io/rate-limit-connections: "10" -spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - tls: - - hosts: - - {{ .Values.wordpress.domain | quote }} - {{- if .Values.wordpress.includeWWW }} - - {{ printf "www.%s" .Values.wordpress.domain | quote }} - {{- end }} - secretName: wordpress-tls - rules: - - host: {{ .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- if .Values.wordpress.includeWWW }} - - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- end }} -{{- end }} +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "wordpress.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "wordpress.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress + annotations: + {{- toYaml .Values.ingress.annotations | nindent 4 }} + # Block sensitive files at the edge (Task #264) + nginx.ingress.kubernetes.io/server-snippet: | + location ~* /\.(git|env|user\.ini|htaccess) { + deny all; + return 403; + } + location = /wp-config.php { + deny all; + return 403; + } + # TLS/SSL Security (Enterprise-Grade, Parameterized) + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} + nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} + nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} + {{- else }} + nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" + nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" + {{- end }} + {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} + # Redirect root domain to www subdomain (using from-to-www annotation) + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" + {{- end }} + # WordPress-specific optimizations + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" + nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" + # Rate limiting for brute force protection + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + nginx.ingress.kubernetes.io/rate-limit-connections: "10" +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + tls: + - hosts: + - {{ .Values.wordpress.domain | quote }} + {{- if .Values.wordpress.includeWWW }} + - {{ printf "www.%s" .Values.wordpress.domain | quote }} + {{- end }} + secretName: wordpress-tls + rules: + - host: {{ .Values.wordpress.domain | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "wordpress.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- if .Values.wordpress.includeWWW }} + - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "wordpress.fullname" . }} + port: + number: {{ .Values.service.port }} + {{- end }} +{{- end }} diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index 48c20f3..139805d 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -1,16 +1,18 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "wordpress.fullname" . }}-php-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} -data: - uploads.ini: | - ; PHP Upload Configuration for WordPress - ; Allows plugin/theme uploads up to 64MB - upload_max_filesize = 64M - post_max_size = 64M - max_execution_time = 300 - max_input_time = 300 - memory_limit = 256M +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "wordpress.fullname" . }}-php-config + namespace: {{ .Release.Namespace }} + labels: + {{- include "wordpress.labels" . | nindent 4 }} +data: + uploads.ini: | + ; PHP Upload Configuration - Codified (D152 & #264) + upload_max_filesize = 128M + post_max_size = 128M + max_execution_time = 300 + max_input_time = 300 + memory_limit = 256M + + ; Wordfence WAF Hardening: Forces loading via PHP-FPM + auto_prepend_file = '/var/www/html/wordfence-waf.php' \ No newline at end of file From 8d44f583734b5a179bdea4e968450ca7851013da Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:43:00 +0500 Subject: [PATCH 02/17] chore(helm): implement multi-site values strategy for burnedout and ptoken --- wordpress/helm/values-burnedout.yaml | 13 + wordpress/helm/values-ptoken.yaml | 13 + wordpress/helm/values.yaml | 1136 +++++++++++++------------- 3 files changed, 594 insertions(+), 568 deletions(-) create mode 100644 wordpress/helm/values-burnedout.yaml create mode 100644 wordpress/helm/values-ptoken.yaml diff --git a/wordpress/helm/values-burnedout.yaml b/wordpress/helm/values-burnedout.yaml new file mode 100644 index 0000000..52563e7 --- /dev/null +++ b/wordpress/helm/values-burnedout.yaml @@ -0,0 +1,13 @@ +wordpress: + domain: "burnedout.xyz" + wordpressBlogName: "BurnedOut" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: burnedout-tls + hosts: + - burnedout.xyz \ No newline at end of file diff --git a/wordpress/helm/values-ptoken.yaml b/wordpress/helm/values-ptoken.yaml new file mode 100644 index 0000000..99b59aa --- /dev/null +++ b/wordpress/helm/values-ptoken.yaml @@ -0,0 +1,13 @@ +wordpress: + domain: "ptoken.agency" + wordpressBlogName: "PToken Agency" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: ptoken-tls + hosts: + - ptoken.agency \ No newline at end of file diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index 5bf097a..93c7447 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -1,568 +1,568 @@ -## Global Configuration -global: - imageRegistry: "" - imagePullSecrets: [] - storageClass: "do-block-storage" - -## Service Account Security -serviceAccount: - create: true - automount: false - annotations: {} - -# Pod Security Standards: Service Account Token Automount -automountServiceAccountToken: false - -## Pod Disruption Budget -podDisruptionBudget: - enabled: true - maxUnavailable: 1 - -## cert-manager Configuration -certManager: - email: "EMAIL_PLACEHOLDER" - createClusterIssuer: false # Use existing ClusterIssuer - -## WordPress Configuration -wordpress: - enableMultisite: false - - ## WordPress Basic Settings - wordpressUsername: admin - wordpressEmail: "" # Set via --set global.email or deploy script - wordpressBlogName: "WordPress Site" - wordpressScheme: https - wordpressTablePrefix: "wp_" - domain: "" # Set via --set global.domain or deploy script - includeWWW: false # Automatically configure www subdomain for main domain - redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) - redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) - wordpressExtraWpConfigContent: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); - - ## Container Image Configuration - image: - registry: docker.io - repository: wordpress - tag: 6.8.3-php8.3-apache - pullPolicy: IfNotPresent - - ## Storage Configuration - persistence: - enabled: true - - ## Security Keys (auto-generated) - security: - authKey: "CHANGE_ME_AUTH_KEY" - secureAuthKey: "CHANGE_ME_SECURE_AUTH_KEY" - loggedInKey: "CHANGE_ME_LOGGED_IN_KEY" - nonceKey: "CHANGE_ME_NONCE_KEY" - authSalt: "CHANGE_ME_AUTH_SALT" - secureAuthSalt: "CHANGE_ME_SECURE_AUTH_SALT" - loggedInSalt: "CHANGE_ME_LOGGED_IN_SALT" - nonceSalt: "CHANGE_ME_NONCE_SALT" - -## Zero-Trust NetworkPolicy (Enterprise Security) -networkPolicy: - enabled: true - policyTypes: - - Ingress - - Egress - - # Ingress: Only allow NGINX Ingress Controller and same namespace - ingress: - - from: - # Allow NGINX Ingress Controller - - namespaceSelector: - matchLabels: - name: ingress-nginx - # Allow same namespace communication - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 80 - - protocol: TCP - port: 443 - - # Egress: Restricted to essential services only - egress: - # DNS resolution - - to: [] - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # HTTPS for WordPress updates and plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow communication with MariaDB and Redis in same namespace - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 3306 # MariaDB - - protocol: TCP - port: 6379 # Redis - -## Replica Configuration -replicaCount: 1 - -## WordPress Configuration -wordpress: - image: - registry: docker.io - repository: wordpress - tag: "6.8.3-php8.3-apache" - pullPolicy: IfNotPresent - pullSecrets: [] - - ## WordPress Admin User (Configured via installation wizard) - # Leave empty to enable WordPress installation wizard - wordpressUsername: "" - wordpressEmail: "" - wordpressPassword: "" - - ## WordPress Site Configuration - wordpressBlogName: "Enterprise WordPress Site" - wordpressScheme: https - domain: "" # Set via --set global.domain or deploy script - - ## WordPress Extra WP Config Content - wordpressExtraWpConfigContent: | - // WordPress configuration - define('WP_DEBUG', false); - define('WP_DEBUG_LOG', false); - define('WP_DEBUG_DISPLAY', false); - define('AUTOMATIC_UPDATER_DISABLED', false); - define('WP_AUTO_UPDATE_CORE', 'minor'); - define('DISALLOW_FILE_EDIT', true); - define('FORCE_SSL_ADMIN', true); - define('WP_MEMORY_LIMIT', '256M'); - - // Disable WP-Cron (handled by Kubernetes CronJob) - define('DISABLE_WP_CRON', true); - - // Performance optimizations - define('WP_CACHE', true); - define('WP_POST_REVISIONS', 3); - define('AUTOSAVE_INTERVAL', 300); - define('EMPTY_TRASH_DAYS', 7); - - // Security hardening - ini_set('session.cookie_httponly', true); - ini_set('session.cookie_secure', true); - ini_set('session.use_only_cookies', true); - - // Enable object caching - define('WP_CACHE_KEY_SALT', 'wordpress_cache_'); - - // SMTP Configuration for reliable email delivery - define('SMTP_FROM_EMAIL', 'EMAIL_PLACEHOLDER'); - define('SMTP_FROM_NAME', 'YOUR_NAME'); - - // Enable WordPress mail debug logging - define('WP_MAIL_DEBUG', false); - - ## WordPress Security Keys (Generated automatically by deployment script) - security: - authKey: "AUTH_KEY_PLACEHOLDER" - secureAuthKey: "SECURE_AUTH_KEY_PLACEHOLDER" - loggedInKey: "LOGGED_IN_KEY_PLACEHOLDER" - nonceKey: "NONCE_KEY_PLACEHOLDER" - authSalt: "AUTH_SALT_PLACEHOLDER" - secureAuthSalt: "SECURE_AUTH_SALT_PLACEHOLDER" - loggedInSalt: "LOGGED_IN_SALT_PLACEHOLDER" - nonceSalt: "NONCE_SALT_PLACEHOLDER" - - ## Persistence Configuration - persistence: - enabled: true - storageClass: "do-block-storage" - accessModes: - - ReadWriteOnce - size: 8Gi - coreSize: 4Gi # WordPress core files persistence - # Separate volumes for security - volumes: - content: - mountPath: /var/www/html/wp-content - size: 8Gi - config: - mountPath: /var/www/html/wp-config-custom - size: 100Mi - cache: - mountPath: /var/cache/wordpress - size: 1Gi - - ## Extra environment variables - extraEnvVars: - - name: WORDPRESS_ENABLE_REDIS - value: "yes" # Enable Redis since we re-enabled it - - name: APACHE_HTTP_PORT - value: "8080" - - name: WORDPRESS_CONFIG_EXTRA - value: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); - - ## Init Containers (WordPress Hardening) - Disabled for resource optimization - initContainers: - enabled: false - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - runAsNonRoot: true - - ## Extra Init Containers for CI/CD wp-content sync - extraInitContainers: [] - # Removed non-functional plugin installation - # Plugins should be installed manually after deployment via WordPress admin - # or WP-CLI for production environments - - ## WordPress Security Context (Production Ready) - security: - automountServiceAccountToken: false - runAsUser: 0 # Required for Apache port 80 binding - runAsGroup: 0 - runAsNonRoot: false - fsGroup: 33 # www-data for file permissions - readOnlyRootFilesystem: false - allowPrivilegeEscalation: false - capabilities: {} # Use default capabilities for WordPress - seccompProfile: - type: RuntimeDefault - - ## Resource Configuration (WordPress Production Requirements - WeOwn Optimized) - # Based on actual usage: WordPress uses ~380Mi in production - resources: - limits: - cpu: 800m # Sufficient for Kadence AI theme installation - memory: 1Gi # Prevents OOMKilled during plugin installs - ephemeral-storage: 800Mi - requests: - cpu: 50m # Cluster-optimized baseline - memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) - ephemeral-storage: 200Mi - ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) - livenessProbe: - enabled: true - tcpSocket: - port: 80 - initialDelaySeconds: 120 - periodSeconds: 30 - timeoutSeconds: 10 - failureThreshold: 6 - successThreshold: 1 - - readinessProbe: - enabled: true - tcpSocket: - port: 80 - initialDelaySeconds: 60 - periodSeconds: 15 - timeoutSeconds: 10 - failureThreshold: 10 - successThreshold: 1 - - ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) - autoscaling: - enabled: false # Must be false when using ReadWriteOnce volumes - -## General Configuration -replicaCount: 1 - -## Redis Configuration (Production Caching) -redis: - enabled: false # Disabled for initial deployment - auth: - enabled: false - -## Environment Variables -extraEnvVars: [] - -## Ingress Configuration -ingress: - enabled: true - className: "nginx" - annotations: - cert-manager.io/cluster-issuer: "letsencrypt-prod" - # TLS/SSL Security - Enterprise-grade encryption (configured in template) - # force-ssl-redirect: "true" - # ssl-protocols: "TLSv1.2 TLSv1.3" - # ssl-ciphers: Modern cipher suites for mobile browser compatibility - # WordPress Performance - nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads - - ## TLS Security Configuration (Parameterized for flexibility) - tls: - protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) - # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility - # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) - ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - hosts: - - host: "" - paths: - - path: / - pathType: Prefix - tls: - - secretName: wordpress-tls - hosts: [] - -## Service Configuration -service: - type: ClusterIP - port: 80 - -## Cron Jobs (Production Maintenance) -cron: - enabled: true - schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) - image: - registry: docker.io - repository: wordpress - tag: 6.8.3-php8.3-apache - resources: - limits: - cpu: 100m - memory: 256Mi - requests: - cpu: 50m - memory: 128Mi - -## Backup Configuration (Production Critical) -backup: - enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: "30" # 30 days - resources: - limits: - cpu: 200m - memory: 512Mi - requests: - cpu: 100m - memory: 256Mi - -## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) -mariadb: - enabled: false # Using custom StatefulSet with official image - -mariadbOfficial: - enabled: true - image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 - auth: - database: wordpress - username: wordpress - password: "MARIADB_PASSWORD_PLACEHOLDER" - rootPassword: "MARIADB_ROOT_PASSWORD_PLACEHOLDER" - - ## Security Context (Pod Security Standards: Restricted) - podSecurityContext: - runAsUser: 999 # Official MariaDB mysql user - runAsGroup: 999 - runAsNonRoot: true - fsGroup: 999 - seccompProfile: - type: RuntimeDefault - - containerSecurityContext: - runAsUser: 999 # Official MariaDB mysql user - runAsGroup: 999 - runAsNonRoot: true - allowPrivilegeEscalation: false - readOnlyRootFilesystem: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - - ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) - resources: - limits: - cpu: 250m # Optimized for database operations - memory: 512Mi # Increased for initialization (prevents OOMKilled) - ephemeral-storage: 1Gi - requests: - cpu: 50m # Matches cluster optimization - memory: 256Mi # Increased for initialization stability - - ## MariaDB Performance Tuning (2-node cluster optimized) - configuration: | - [mysqld] - innodb_buffer_pool_size=64M # Reduced for memory efficiency - innodb_log_file_size=16M # Reduced for faster startup - query_cache_size=16M # Reduced but still beneficial - query_cache_limit=1M # Conservative limit - thread_cache_size=4 # Fewer threads for 2-node - table_open_cache=128 # Reduced cache size - performance_schema=OFF # Keep disabled for efficiency - innodb_flush_log_at_trx_commit=2 # Better performance - -## Redis Cache Configuration (Lightweight for 2-node cluster) -redis: - enabled: true - architecture: standalone - auth: - enabled: true - password: "REDIS_PASSWORD_PLACEHOLDER" - - ## Resource limits for 2-node efficiency - master: - resources: - limits: - cpu: 50m - memory: 48Mi - ephemeral-storage: 150Mi - requests: - cpu: 10m - memory: 24Mi - ephemeral-storage: 50Mi - - persistence: - enabled: false # Disabled for performance in 2-node cluster - - ## Security context for Redis - podSecurityContext: - runAsUser: 1001 - runAsGroup: 1001 - runAsNonRoot: true - fsGroup: 1001 - seccompProfile: - type: RuntimeDefault - - containerSecurityContext: - runAsUser: 1001 - runAsGroup: 1001 - runAsNonRoot: true - allowPrivilegeEscalation: false - readOnlyRootFilesystem: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - -## Service Configuration -service: - type: ClusterIP # Secure internal-only access - port: 80 - targetPort: 80 - annotations: {} - - -## NetworkPolicy (Zero-Trust Security) -networkPolicy: - enabled: false - policyTypes: - - Ingress - - Egress - - ingress: - # Allow NGINX Ingress Controller - - from: - - namespaceSelector: - matchLabels: - name: ingress-nginx - - podSelector: - matchLabels: {} - ports: - - protocol: TCP - port: 8080 - egress: - # Allow DNS resolution to kube-system namespace only - - to: - - namespaceSelector: - matchLabels: - name: kube-system - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # Allow HTTPS outbound for WordPress updates/plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow access to MariaDB - - to: - - podSelector: - matchLabels: - app.kubernetes.io/name: mariadb - ports: - - protocol: TCP - port: 3306 - # Allow access to Redis (if enabled) - - to: - - podSelector: - matchLabels: - app.kubernetes.io/name: redis - ports: - - protocol: TCP - port: 6379 - -## Backup Configuration (NEW - Enhanced Data Protection) -backup: - enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: 30 # Keep 30 days of backups - storage: - size: 20Gi - storageClass: "do-block-storage" - -## Monitoring & Observability -monitoring: - enabled: true - serviceMonitor: - enabled: true - interval: 30s - path: /wp-json/wp/v2/ - port: http - -## Must-Use Plugins Configuration -muPlugins: - cache: - enabled: true - authFix: - enabled: true # General REST API Authentication fix for containerized environments - disableXmlRpc: - enabled: true # Security best practice: Disable XML-RPC (use REST API instead) - smtp: - enabled: true # Enterprise SMTP Configuration via Environment Variables - -## WordPress Plugins (Manual Installation Recommended) -# Plugins are not pre-configured in this deployment for security and flexibility -# Install plugins manually after deployment via: -# 1. WordPress Admin > Plugins > Add New -# 2. WP-CLI: kubectl exec deployment/wordpress -n namespace -- wp plugin install plugin-name -# 3. Manual upload via WordPress Admin -# -# Recommended plugins for enterprise use: -# Security: wordfence, limit-login-attempts, wp-security-audit-log -# Performance: w3-total-cache, smush, shortpixel-image-optimiser -# SEO: yoast-seo, rankmath-seo -# Backup: updraftplus, backwpup -# -# plugins: -# security: -# - wordfence -# - limit-login-attempts -# performance: -# - w3-total-cache -# - smush -# seo: -# - yoast-seo +## Global Configuration +global: + imageRegistry: "" + imagePullSecrets: [] + storageClass: "do-block-storage" + +## Service Account Security +serviceAccount: + create: true + automount: false + annotations: {} + +# Pod Security Standards: Service Account Token Automount +automountServiceAccountToken: false + +## Pod Disruption Budget +podDisruptionBudget: + enabled: true + maxUnavailable: 1 + +## cert-manager Configuration +certManager: + email: "EMAIL_PLACEHOLDER" + createClusterIssuer: false # Use existing ClusterIssuer + +## WordPress Configuration +wordpress: + enableMultisite: false + + ## WordPress Basic Settings + wordpressUsername: admin + wordpressEmail: "" # Set via --set global.email or deploy script + wordpressBlogName: "WordPress Site" + wordpressScheme: https + wordpressTablePrefix: "wp_" + domain: "" # Set via --set global.domain or deploy script + includeWWW: false # Automatically configure www subdomain for main domain + redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) + redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) + wordpressExtraWpConfigContent: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); + + ## Container Image Configuration + image: + registry: docker.io + repository: wordpress + tag: 6.8.3-php8.3-apache + pullPolicy: IfNotPresent + + ## Storage Configuration + persistence: + enabled: true + + ## Security Keys (auto-generated) + security: + authKey: "CHANGE_ME_AUTH_KEY" + secureAuthKey: "CHANGE_ME_SECURE_AUTH_KEY" + loggedInKey: "CHANGE_ME_LOGGED_IN_KEY" + nonceKey: "CHANGE_ME_NONCE_KEY" + authSalt: "CHANGE_ME_AUTH_SALT" + secureAuthSalt: "CHANGE_ME_SECURE_AUTH_SALT" + loggedInSalt: "CHANGE_ME_LOGGED_IN_SALT" + nonceSalt: "CHANGE_ME_NONCE_SALT" + +## Zero-Trust NetworkPolicy (Enterprise Security) +networkPolicy: + enabled: true + policyTypes: + - Ingress + - Egress + + # Ingress: Only allow NGINX Ingress Controller and same namespace + ingress: + - from: + # Allow NGINX Ingress Controller + - namespaceSelector: + matchLabels: + name: ingress-nginx + # Allow same namespace communication + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 80 + - protocol: TCP + port: 443 + + # Egress: Restricted to essential services only + egress: + # DNS resolution + - to: [] + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # HTTPS for WordPress updates and plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow communication with MariaDB and Redis in same namespace + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 3306 # MariaDB + - protocol: TCP + port: 6379 # Redis + +## Replica Configuration +replicaCount: 1 + +## WordPress Configuration +wordpress: + image: + registry: docker.io + repository: wordpress + tag: "6.8.3-php8.3-apache" + pullPolicy: IfNotPresent + pullSecrets: [] + + ## WordPress Admin User (Configured via installation wizard) + # Leave empty to enable WordPress installation wizard + wordpressUsername: "" + wordpressEmail: "" + wordpressPassword: "" + + ## WordPress Site Configuration + wordpressBlogName: "Enterprise WordPress Site" + wordpressScheme: https + domain: "" # Set via --set global.domain or deploy script + + ## WordPress Extra WP Config Content + wordpressExtraWpConfigContent: | + // WordPress configuration + define('WP_DEBUG', false); + define('WP_DEBUG_LOG', false); + define('WP_DEBUG_DISPLAY', false); + define('AUTOMATIC_UPDATER_DISABLED', false); + define('WP_AUTO_UPDATE_CORE', 'minor'); + define('DISALLOW_FILE_EDIT', true); + define('FORCE_SSL_ADMIN', true); + define('WP_MEMORY_LIMIT', '256M'); + + // Disable WP-Cron (handled by Kubernetes CronJob) + define('DISABLE_WP_CRON', true); + + // Performance optimizations + define('WP_CACHE', true); + define('WP_POST_REVISIONS', 3); + define('AUTOSAVE_INTERVAL', 300); + define('EMPTY_TRASH_DAYS', 7); + + // Security hardening + ini_set('session.cookie_httponly', true); + ini_set('session.cookie_secure', true); + ini_set('session.use_only_cookies', true); + + // Enable object caching + define('WP_CACHE_KEY_SALT', 'wordpress_cache_'); + + // SMTP Configuration for reliable email delivery + define('SMTP_FROM_EMAIL', 'EMAIL_PLACEHOLDER'); + define('SMTP_FROM_NAME', 'YOUR_NAME'); + + // Enable WordPress mail debug logging + define('WP_MAIL_DEBUG', false); + + ## WordPress Security Keys (Generated automatically by deployment script) + security: + authKey: "AUTH_KEY_PLACEHOLDER" + secureAuthKey: "SECURE_AUTH_KEY_PLACEHOLDER" + loggedInKey: "LOGGED_IN_KEY_PLACEHOLDER" + nonceKey: "NONCE_KEY_PLACEHOLDER" + authSalt: "AUTH_SALT_PLACEHOLDER" + secureAuthSalt: "SECURE_AUTH_SALT_PLACEHOLDER" + loggedInSalt: "LOGGED_IN_SALT_PLACEHOLDER" + nonceSalt: "NONCE_SALT_PLACEHOLDER" + + ## Persistence Configuration + persistence: + enabled: true + storageClass: "do-block-storage" + accessModes: + - ReadWriteOnce + size: 8Gi + coreSize: 4Gi # WordPress core files persistence + # Separate volumes for security + volumes: + content: + mountPath: /var/www/html/wp-content + size: 8Gi + config: + mountPath: /var/www/html/wp-config-custom + size: 100Mi + cache: + mountPath: /var/cache/wordpress + size: 1Gi + + ## Extra environment variables + extraEnvVars: + - name: WORDPRESS_ENABLE_REDIS + value: "yes" # Enable Redis since we re-enabled it + - name: APACHE_HTTP_PORT + value: "8080" + - name: WORDPRESS_CONFIG_EXTRA + value: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); + + ## Init Containers (WordPress Hardening) - Disabled for resource optimization + initContainers: + enabled: false + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + runAsNonRoot: true + + ## Extra Init Containers for CI/CD wp-content sync + extraInitContainers: [] + # Removed non-functional plugin installation + # Plugins should be installed manually after deployment via WordPress admin + # or WP-CLI for production environments + + ## WordPress Security Context (Production Ready) + security: + automountServiceAccountToken: false + runAsUser: 0 # Required for Apache port 80 binding + runAsGroup: 0 + runAsNonRoot: false + fsGroup: 33 # www-data for file permissions + readOnlyRootFilesystem: false + allowPrivilegeEscalation: false + capabilities: {} # Use default capabilities for WordPress + seccompProfile: + type: RuntimeDefault + + ## Resource Configuration (WordPress Production Requirements - WeOwn Optimized) + # Based on actual usage: WordPress uses ~380Mi in production + resources: + limits: + cpu: 800m # Sufficient for Kadence AI theme installation + memory: 1Gi # Prevents OOMKilled during plugin installs + ephemeral-storage: 800Mi + requests: + cpu: 50m # Cluster-optimized baseline + memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) + ephemeral-storage: 200Mi + ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) + livenessProbe: + enabled: true + tcpSocket: + port: 80 + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + tcpSocket: + port: 80 + initialDelaySeconds: 60 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 10 + successThreshold: 1 + + ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) + autoscaling: + enabled: false # Must be false when using ReadWriteOnce volumes + +## General Configuration +replicaCount: 1 + +## Redis Configuration (Production Caching) +redis: + enabled: false # Disabled for initial deployment + auth: + enabled: false + +## Environment Variables +extraEnvVars: [] + +## Ingress Configuration +ingress: + enabled: false + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + # TLS/SSL Security - Enterprise-grade encryption (configured in template) + # force-ssl-redirect: "true" + # ssl-protocols: "TLSv1.2 TLSv1.3" + # ssl-ciphers: Modern cipher suites for mobile browser compatibility + # WordPress Performance + nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + + ## TLS Security Configuration (Parameterized for flexibility) + tls: + protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) + # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility + # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) + ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" + hosts: + - host: "" + paths: + - path: / + pathType: Prefix + tls: + - secretName: wordpress-tls + hosts: [] + +## Service Configuration +service: + type: ClusterIP + port: 80 + +## Cron Jobs (Production Maintenance) +cron: + enabled: true + schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) + image: + registry: docker.io + repository: wordpress + tag: 6.8.3-php8.3-apache + resources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + +## Backup Configuration (Production Critical) +backup: + enabled: true + schedule: "0 2 * * *" # Daily at 2 AM + retention: "30" # 30 days + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + +## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) +mariadb: + enabled: false # Using custom StatefulSet with official image + +mariadbOfficial: + enabled: true + image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 + auth: + database: wordpress + username: wordpress + password: "MARIADB_PASSWORD_PLACEHOLDER" + rootPassword: "MARIADB_ROOT_PASSWORD_PLACEHOLDER" + + ## Security Context (Pod Security Standards: Restricted) + podSecurityContext: + runAsUser: 999 # Official MariaDB mysql user + runAsGroup: 999 + runAsNonRoot: true + fsGroup: 999 + seccompProfile: + type: RuntimeDefault + + containerSecurityContext: + runAsUser: 999 # Official MariaDB mysql user + runAsGroup: 999 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + + ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) + resources: + limits: + cpu: 250m # Optimized for database operations + memory: 512Mi # Increased for initialization (prevents OOMKilled) + ephemeral-storage: 1Gi + requests: + cpu: 50m # Matches cluster optimization + memory: 256Mi # Increased for initialization stability + + ## MariaDB Performance Tuning (2-node cluster optimized) + configuration: | + [mysqld] + innodb_buffer_pool_size=64M # Reduced for memory efficiency + innodb_log_file_size=16M # Reduced for faster startup + query_cache_size=16M # Reduced but still beneficial + query_cache_limit=1M # Conservative limit + thread_cache_size=4 # Fewer threads for 2-node + table_open_cache=128 # Reduced cache size + performance_schema=OFF # Keep disabled for efficiency + innodb_flush_log_at_trx_commit=2 # Better performance + +## Redis Cache Configuration (Lightweight for 2-node cluster) +redis: + enabled: true + architecture: standalone + auth: + enabled: true + password: "REDIS_PASSWORD_PLACEHOLDER" + + ## Resource limits for 2-node efficiency + master: + resources: + limits: + cpu: 50m + memory: 48Mi + ephemeral-storage: 150Mi + requests: + cpu: 10m + memory: 24Mi + ephemeral-storage: 50Mi + + persistence: + enabled: false # Disabled for performance in 2-node cluster + + ## Security context for Redis + podSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + fsGroup: 1001 + seccompProfile: + type: RuntimeDefault + + containerSecurityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + +## Service Configuration +service: + type: ClusterIP # Secure internal-only access + port: 80 + targetPort: 80 + annotations: {} + + +## NetworkPolicy (Zero-Trust Security) +networkPolicy: + enabled: false + policyTypes: + - Ingress + - Egress + + ingress: + # Allow NGINX Ingress Controller + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + - podSelector: + matchLabels: {} + ports: + - protocol: TCP + port: 8080 + egress: + # Allow DNS resolution to kube-system namespace only + - to: + - namespaceSelector: + matchLabels: + name: kube-system + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # Allow HTTPS outbound for WordPress updates/plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow access to MariaDB + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: mariadb + ports: + - protocol: TCP + port: 3306 + # Allow access to Redis (if enabled) + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: redis + ports: + - protocol: TCP + port: 6379 + +## Backup Configuration (NEW - Enhanced Data Protection) +backup: + enabled: true + schedule: "0 2 * * *" # Daily at 2 AM + retention: 30 # Keep 30 days of backups + storage: + size: 20Gi + storageClass: "do-block-storage" + +## Monitoring & Observability +monitoring: + enabled: true + serviceMonitor: + enabled: true + interval: 30s + path: /wp-json/wp/v2/ + port: http + +## Must-Use Plugins Configuration +muPlugins: + cache: + enabled: true + authFix: + enabled: true # General REST API Authentication fix for containerized environments + disableXmlRpc: + enabled: true # Security best practice: Disable XML-RPC (use REST API instead) + smtp: + enabled: true # Enterprise SMTP Configuration via Environment Variables + +## WordPress Plugins (Manual Installation Recommended) +# Plugins are not pre-configured in this deployment for security and flexibility +# Install plugins manually after deployment via: +# 1. WordPress Admin > Plugins > Add New +# 2. WP-CLI: kubectl exec deployment/wordpress -n namespace -- wp plugin install plugin-name +# 3. Manual upload via WordPress Admin +# +# Recommended plugins for enterprise use: +# Security: wordfence, limit-login-attempts, wp-security-audit-log +# Performance: w3-total-cache, smush, shortpixel-image-optimiser +# SEO: yoast-seo, rankmath-seo +# Backup: updraftplus, backwpup +# +# plugins: +# security: +# - wordfence +# - limit-login-attempts +# performance: +# - w3-total-cache +# - smush +# seo: +# - yoast-seo From b98ac280032e7abdcea848b3d0df686f863844b5 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Thu, 23 Apr 2026 14:46:43 +0500 Subject: [PATCH 03/17] chore: bump helm chart version to 3.2.7 --- wordpress/helm/Chart.yaml | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 26d5e6a..6ea379d 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -1,27 +1,27 @@ -apiVersion: v2 -name: wordpress -description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS -type: application -version: 3.2.6 -appVersion: "6.8.3" - -keywords: -- wordpress -- cms -- blog -- php -- mariadb - -home: https://github.com/WeOwnNetwork/ai -sources: -- https://github.com/WeOwnNetwork/ai/tree/main/wordpress - -maintainers: -- name: Enterprise WordPress - email: admin@example.com - -# No external dependencies - using official MariaDB image via custom StatefulSet - -annotations: - category: CMS - licenses: Apache-2.0 +apiVersion: v2 +name: wordpress +description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS +type: application +version: 3.2.7 +appVersion: "6.8.3" + +keywords: +- wordpress +- cms +- blog +- php +- mariadb + +home: https://github.com/WeOwnNetwork/ai +sources: +- https://github.com/WeOwnNetwork/ai/tree/main/wordpress + +maintainers: +- name: Enterprise WordPress + email: admin@example.com + +# No external dependencies - using official MariaDB image via custom StatefulSet + +annotations: + category: CMS + licenses: Apache-2.0 From ab4be4537779777edcc75407f39d81520b72548a Mon Sep 17 00:00:00 2001 From: romandidomizio Date: Fri, 1 May 2026 14:48:23 -0600 Subject: [PATCH 04/17] docs(rulesets): update ADR-004 for 2-rule structure, CONTRIBUTING.md signing options, CHANGELOG entry --- .../ADR-004-copilot-auto-review-ruleset.md | 27 ++++++------ CHANGELOG.md | 10 +++++ CONTRIBUTING.md | 44 +++++++++++++++---- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index e6f4e54..8d65869 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -19,9 +19,8 @@ ADR-003 covers the strict `main`-only ruleset. But there are repo-wide invariants that must hold on **every branch**, not just `main`: -1. **Deletion protection on `~ALL` branches** — feature branches must not be deletable mid-PR (audit-trail loss; CI/CD broken; reviewer history orphaned). -2. **`non_fast_forward` (force-push block) on `~ALL` branches** — required so that `auto-pr-to-main.yml` can rely on the FIRST commit of any branch being immutable. The workflow's `Opened by:` field (see §3 of the workflows README) is computed from `git rev-list --reverse "${GIT_RANGE[@]}" | head -1` → `gh api /repos/.../commits/{first-sha} --jq .author.login`, and is documented as "stable across pushes" only because the first commit cannot be rewritten. -3. **`copilot_code_review` on `~ALL` branches** — every **newly-created** PR (regardless of base branch) gets Copilot AI review automatically **after ruleset enablement**. This is the WeOwn baseline AI safety pattern (one of two enforcement mechanisms; the other is `weown-bot` being a "human-type" account so legacy auto-trigger fires). **Note**: Copilot evaluates auto-review eligibility at PR-creation time, so PRs that already existed before the ruleset was applied (e.g., PR #13) do **not** retroactively gain auto-review — they must be triggered manually for the duration of their open lifecycle. See § Empirical Validation Results below for the controlled experiment confirming this PR-creation-time caching behavior. +1. **`non_fast_forward` (force-push block) on `~ALL` branches** — required so that `auto-pr-to-main.yml` can rely on the FIRST commit of any branch being immutable. The workflow's `Opened by:` field (see §3 of the workflows README) is computed from `git rev-list --reverse "${GIT_RANGE[@]}" | head -1` → `gh api /repos/.../commits/{first-sha} --jq .author.login`, and is documented as "stable across pushes" only because the first commit cannot be rewritten. +2. **`copilot_code_review` on `~ALL` branches** — every **newly-created** PR (regardless of base branch) gets Copilot AI review automatically **after ruleset enablement**. This is the WeOwn baseline AI safety pattern (one of two enforcement mechanisms; the other is `weown-bot` being a "human-type" account so legacy auto-trigger fires). **Note**: Copilot evaluates auto-review eligibility at PR-creation time, so PRs that already existed before the ruleset was applied (e.g., PR #13) do **not** retroactively gain auto-review — they must be triggered manually for the duration of their open lifecycle. See § Empirical Validation Results below for the controlled experiment confirming this PR-creation-time caching behavior. These can't go in ADR-003 because they target `~ALL`, not `~DEFAULT_BRANCH`. They warrant a separate ADR because: - Their compliance mappings differ (focus on data integrity + AI safety + deletion protection, not change-management gating) @@ -32,22 +31,23 @@ These can't go in ADR-003 because they target `~ALL`, not `~DEFAULT_BRANCH`. The ## Decision -**Two stacked rulesets enforce the same 3 rules on `~ALL` branches**, providing defense-in-depth: +**Two stacked rulesets enforce the same 2 rules on `~ALL` branches**, providing defense-in-depth: -### Layer 1 — Repo-level "Copilot auto-review" ruleset (id `12131972`, configured 2026-04-23) +### Layer 1 — Repo-level "Copilot auto-review" ruleset (id `12131972`, configured 2026-04-23, amended 2026-05-01) - **Scope**: `WeOwnNetwork/ai` repository, all branches (`include: ["~ALL"]`) - **Enforcement status**: Active - **Bypass list**: empty -- **Rules (3)**: - 1. **`deletion`** — block branch deletion (preserves audit trail of merged + abandoned branches) - 2. **`non_fast_forward`** — block force-push and rebase that would rewrite history (makes first-commit identity immutable on every branch, which `auto-pr-to-main.yml` depends on for the `Opened by:` attribution; also prevents post-review tampering of reviewed commits) - 3. **`copilot_code_review`** with `review_draft_pull_requests: true, review_on_push: true` — auto-request Copilot review for newly-created, eligible PRs (draft + ready), and re-request review on later pushes only for open PRs where Copilot was auto-requested at creation. **Note**: this is the same PR-creation-time eligibility caching documented in [§ Empirical Validation Results](#empirical-validation-results) below — PRs that pre-date ruleset enablement do NOT retroactively gain auto-review on subsequent pushes; remediation is close+reopen or merge+open-fresh +- **Rules (2)**: + 1. **`non_fast_forward`** — block force-push and rebase that would rewrite history (makes first-commit identity immutable on every branch, which `auto-pr-to-main.yml` depends on for the `Opened by:` attribution; also prevents post-review tampering of reviewed commits) + 2. **`copilot_code_review`** with `review_draft_pull_requests: true, review_on_push: true` — auto-request Copilot review for newly-created, eligible PRs (draft + ready), and re-request review on later pushes only for open PRs where Copilot was auto-requested at creation. **Note**: this is the same PR-creation-time eligibility caching documented in [§ Empirical Validation Results](#empirical-validation-results) below — PRs that pre-date ruleset enablement do NOT retroactively gain auto-review on subsequent pushes; remediation is close+reopen or merge+open-fresh -### Layer 2 — Enterprise-level ruleset (configured 2026-04-27) +**Note**: The `deletion` rule was **removed** from `~ALL` branches on 2026-05-01 (see Decision Log). Deletion protection remains on `main` via ADR-003 rule #11. Branches auto-delete on merge via the "Automatically delete head branches" repo setting. + +### Layer 2 — Enterprise-level ruleset (configured 2026-04-27, amended 2026-05-01) - **Scope**: WeOwn enterprise → all organizations → all repositories → all branches -- **Rules**: identical 3 rules to Layer 1 +- **Rules**: identical 2 rules to Layer 1 (post-2026-05-01: also excludes `deletion`) - **Why it exists**: Copilot Business entitlement is licensed at the enterprise level. The repo-level `copilot_code_review` rule on Layer 1 silently no-ops for `weown-bot`-authored PRs because the entitlement isn't visible at repo scope. The enterprise-level ruleset is configured at the same scope as the entitlement, which is where Copilot is expected to honor the auto-trigger for service accounts with Business seats. ### Defense-in-depth rationale (why both, not one) @@ -75,12 +75,12 @@ Both rulesets enforce **identical rules**. There is no rule-sync burden because | Framework / Control | Rule(s) | How this ADR satisfies it | |---|---|---| -| **SOC 2 CC6.1** (logical access — privileged operations) | `deletion`, `non_fast_forward` | Branch deletion + history rewriting are privileged-by-design and require explicit ruleset bypass (none granted) | +| **SOC 2 CC6.1** (logical access — privileged operations) | `non_fast_forward` | History rewriting is privileged-by-design and requires explicit ruleset bypass (none granted) | | **SOC 2 CC7.1** (system monitoring — anomaly detection) | `copilot_code_review` | Every PR gets AI-assisted code review with security-aware suggestions; commit-time anomaly detection layer beyond pre-commit hooks | | **SOC 2 CC7.2** (system change monitoring) | `copilot_code_review`, `non_fast_forward` | All branch changes reviewed before merge; history immutability prevents silent post-review tampering | | **SOC 2 CC8.1** (change management — formal review) | `copilot_code_review` | AI review on every push to every PR (draft + ready) is the first line of structured review (human review enforced separately by ADR-003 on `main`) | | **ISO/IEC 27001:2022 A.8.32** (change management) | All 3 rules | Branches cannot be deleted (audit preservation), history cannot be rewritten (review integrity), AI review is enforced (change scrutiny) | -| **ISO/IEC 27001:2022 A.8.13** (information backup — branches as audit) | `deletion` | Even abandoned branches are preserved for change-history reconstruction | +| **ISO/IEC 27001:2022 A.8.13** (information backup — branches as audit) | ADR-003 `deletion` on `main` | `main` branch is preserved; feature branches auto-delete post-merge via repo setting (cleanliness) while audit trail lives in `main` history | | **ISO/IEC 42001:2023 A.6.2.7** (AI system development — review) | `copilot_code_review` | AI-assisted review on every code change to AI infrastructure; meets the "appropriate review" bar for AI lifecycle management | | **ISO/IEC 42001:2023 A.6.2.8** (AI deployment review) | `copilot_code_review` | All deployment-related code (Helm, K8s, CI) gets AI review before merge to `main` | | **NIST CSF 2.0 PR.IP-3** (configuration change control) | `non_fast_forward` | Configuration changes (workflow YAML, Helm values, K8s manifests) cannot be silently rewritten after review | @@ -234,3 +234,4 @@ This validation also refines the pruning criteria for Layer 1: the "5+ consecuti | 2026-04-27 | `@romandidomizio` | Layer 2 (enterprise-level ruleset) added after rounds 1–5 of PR #13 confirmed Layer 1 alone does not auto-trigger Copilot for `weown-bot`. Hypothesis: Copilot Business entitlement requires enterprise-scoped enforcement | | 2026-04-27 | `@romandidomizio` | This ADR authored as part of v3.3.4.2 round-6 close-out; documents both layers + defense-in-depth rationale + pruning criteria | | 2026-04-27 | `@romandidomizio` | **ADR updated to v3.3.5.1 (round-7 close-out)**. Added "Empirical Validation Results" section with controlled-experiment findings: Layer 2 + Copilot Business entitlement work correctly; PR-creation-time caching is what prevents auto-trigger on pre-existing PR #13. First confirmed enterprise-level auto-trigger: the fresh PR opened on 2026-04-27 for the `velero-restic` branch (PR number + workflow run + timestamp to be filled in after merge). Resolved Copilot R7 comment #5 (retargeted this validation step from `PR7_HANDOFF_CHECKLIST.md` to this Decision Log). | +| 2026-05-01 | `@romandidomizio` | **`deletion` rule removed from `~ALL` branches in both Layer 1 and Layer 2**. This resolves the post-merge branch deletion blockage (branches could not be deleted after squash-merge due to `deletion` rule on `~ALL` with empty bypass list). Rationale: `main` branch already protected by ADR-003 rule #11; feature branches should auto-delete on merge for cleanliness. Requires "Automatically delete head branches" repo setting enabled (done). See `PR7_HANDOFF_CHECKLIST.md` §Branch Deletion Ruleset Issue for full context. | diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4960c..bebc9fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,16 @@ Application-specific changes live in per-directory CHANGELOGs. See the index bel Changes in this section will be promoted to a dated release entry on merge to `main`. +### Changed + +- **`.github/ADR-004-copilot-auto-review-ruleset.md` ruleset structure amended (2026-05-01)** — `deletion` rule removed from `~ALL` branches in both Layer 1 (repo-level) and Layer 2 (enterprise-level) rulesets. Retained 2 rules: `non_fast_forward` + `copilot_code_review`. Rationale: post-merge branch deletion was permanently blocked by `deletion` on `~ALL` with empty `bypass_actors` — no one (including org admins) could delete any branch. `main` branch deletion protection remains via ADR-003 rule #11. Decision Log appended. Compliance mappings adjusted (SOC 2 CC6.1, ISO 27001 A.8.13). +- **`CONTRIBUTING.md` §8 troubleshooting expanded (2026-05-01)** — added "My PR is blocked — commits are unsigned" scenario with two clear options: **Option 1** (rebase + force-push to keep same PR, preserving review history) and **Option 2** (close PR, cherry-pick to fresh branch, open new PR). Commands simplified. Explains why force-push is required (commit hash changes when signing is added). Replaces the older single "I need to sign commits I already pushed unsigned" subsection. +- **`PR7_HANDOFF_CHECKLIST.md` updated (2026-05-01)** — branch deletion ruleset issue marked RESOLVED with full resolution steps. Ruleset As-Enabled section updated to include new repo settings. + +### Infrastructure + +- **Repo Settings → General → Pull Requests (2026-05-01)**: enabled **"Automatically delete head branches"** (merged branches auto-delete, replacing the retired `deletion` rule on `~ALL`); enabled **"Always suggest updating pull request branches"** (keeps PRs current with `main` for cleaner merges). + --- ## [v3.3.5.1] — 2026-04-27 to 2026-04-28 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 628b060..dc5daf7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -609,22 +609,50 @@ Fix (pick one): git push --force-with-lease origin ``` -### "I need to sign commits I already pushed unsigned" +### "My PR is blocked — commits are unsigned" + +If your PR shows "Merge blocked — requires signed commits", you have **two options**. Pick based on your situation: + +#### Option 1: Keep the same PR (rebase + force-push) + +Best if you have active review comments you don't want to lose, or the PR has been open for a while with discussion history. ```bash -# Rebase in-place, amending each commit with -S (sign) +# Sign all commits on your branch in one shot git fetch origin main -git rebase --exec 'git commit --amend --no-edit --no-verify -S' origin/main +git rebase --exec 'git commit --amend --no-edit -S' origin/main -# Verify every commit on your branch is now signed -git log origin/main..HEAD --pretty='format:%h %G? %s' -# Every row must show 'G' +# Verify (every %G? must show 'G') +git log origin/main..HEAD --pretty='%h %G? %s' -# Force-push safely (fails if anyone else pushed to your branch) +# Push signed commits (safe — fails if someone else pushed) git push --force-with-lease origin ``` -`--force-with-lease` protects against overwriting teammate work. Do NOT use `--force` unless you're 100% sure you're the only committer on the branch. +The PR updates automatically with signed commits. Review history is preserved. + +#### Option 2: Close PR, create fresh branch + new PR + +Best if your branch is small, you haven't pushed much, or you want a clean start. + +```bash +# Close the blocked PR in GitHub UI first (just click Close) + +# Locally, create a fresh branch from main with your changes +git fetch origin main +git checkout -b feature/- origin/main + +# Cherry-pick your changes (each pick will auto-sign thanks to §3.1 setup) +git cherry-pick +# Repeat for each commit you want to keep + +# Push and let auto-PR create a new one +git push origin feature/- +``` + +This creates a new PR with 100% signed commits from the start. Link to the old closed PR in the body for context. + +**Why force-push is required for Option 1**: Signing a commit changes its hash (the signature is part of the commit object). Git sees this as "rewriting history" even though the content is identical. `--force-with-lease` is the safe way to update the remote — it rejects the push if anyone else added commits to your branch (protects against overwriting their work). ### "I'm getting `error: gpg failed to sign the data`" From 401618886cc92b62e649685a99bc99b4869c066d Mon Sep 17 00:00:00 2001 From: romandidomizio Date: Fri, 1 May 2026 16:26:27 -0600 Subject: [PATCH 05/17] docs(copilot): address auto-review findings + document PR-creation-time behavior --- .../ADR-004-copilot-auto-review-ruleset.md | 6 +-- .github/workflows/README.md | 3 +- CHANGELOG.md | 6 +-- CONTRIBUTING.md | 47 ++++++++----------- 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index 8d65869..bb6418e 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -23,7 +23,7 @@ ADR-003 covers the strict `main`-only ruleset. But there are repo-wide invariant 2. **`copilot_code_review` on `~ALL` branches** — every **newly-created** PR (regardless of base branch) gets Copilot AI review automatically **after ruleset enablement**. This is the WeOwn baseline AI safety pattern (one of two enforcement mechanisms; the other is `weown-bot` being a "human-type" account so legacy auto-trigger fires). **Note**: Copilot evaluates auto-review eligibility at PR-creation time, so PRs that already existed before the ruleset was applied (e.g., PR #13) do **not** retroactively gain auto-review — they must be triggered manually for the duration of their open lifecycle. See § Empirical Validation Results below for the controlled experiment confirming this PR-creation-time caching behavior. These can't go in ADR-003 because they target `~ALL`, not `~DEFAULT_BRANCH`. They warrant a separate ADR because: -- Their compliance mappings differ (focus on data integrity + AI safety + deletion protection, not change-management gating) +- Their compliance mappings differ (focus on data integrity + AI safety + history immutability, not change-management gating) - They have a defense-in-depth pairing with an **enterprise-level** ruleset that mirrors the same rules at a broader scope - Their pruning criteria (when to retire either layer) are distinct from ADR-003's review cadence @@ -79,7 +79,7 @@ Both rulesets enforce **identical rules**. There is no rule-sync burden because | **SOC 2 CC7.1** (system monitoring — anomaly detection) | `copilot_code_review` | Every PR gets AI-assisted code review with security-aware suggestions; commit-time anomaly detection layer beyond pre-commit hooks | | **SOC 2 CC7.2** (system change monitoring) | `copilot_code_review`, `non_fast_forward` | All branch changes reviewed before merge; history immutability prevents silent post-review tampering | | **SOC 2 CC8.1** (change management — formal review) | `copilot_code_review` | AI review on every push to every PR (draft + ready) is the first line of structured review (human review enforced separately by ADR-003 on `main`) | -| **ISO/IEC 27001:2022 A.8.32** (change management) | All 3 rules | Branches cannot be deleted (audit preservation), history cannot be rewritten (review integrity), AI review is enforced (change scrutiny) | +| **ISO/IEC 27001:2022 A.8.32** (change management) | `non_fast_forward` + `copilot_code_review` | History cannot be rewritten (review integrity), AI review is enforced on every PR (change scrutiny) | | **ISO/IEC 27001:2022 A.8.13** (information backup — branches as audit) | ADR-003 `deletion` on `main` | `main` branch is preserved; feature branches auto-delete post-merge via repo setting (cleanliness) while audit trail lives in `main` history | | **ISO/IEC 42001:2023 A.6.2.7** (AI system development — review) | `copilot_code_review` | AI-assisted review on every code change to AI infrastructure; meets the "appropriate review" bar for AI lifecycle management | | **ISO/IEC 42001:2023 A.6.2.8** (AI deployment review) | `copilot_code_review` | All deployment-related code (Helm, K8s, CI) gets AI review before merge to `main` | @@ -234,4 +234,4 @@ This validation also refines the pruning criteria for Layer 1: the "5+ consecuti | 2026-04-27 | `@romandidomizio` | Layer 2 (enterprise-level ruleset) added after rounds 1–5 of PR #13 confirmed Layer 1 alone does not auto-trigger Copilot for `weown-bot`. Hypothesis: Copilot Business entitlement requires enterprise-scoped enforcement | | 2026-04-27 | `@romandidomizio` | This ADR authored as part of v3.3.4.2 round-6 close-out; documents both layers + defense-in-depth rationale + pruning criteria | | 2026-04-27 | `@romandidomizio` | **ADR updated to v3.3.5.1 (round-7 close-out)**. Added "Empirical Validation Results" section with controlled-experiment findings: Layer 2 + Copilot Business entitlement work correctly; PR-creation-time caching is what prevents auto-trigger on pre-existing PR #13. First confirmed enterprise-level auto-trigger: the fresh PR opened on 2026-04-27 for the `velero-restic` branch (PR number + workflow run + timestamp to be filled in after merge). Resolved Copilot R7 comment #5 (retargeted this validation step from `PR7_HANDOFF_CHECKLIST.md` to this Decision Log). | -| 2026-05-01 | `@romandidomizio` | **`deletion` rule removed from `~ALL` branches in both Layer 1 and Layer 2**. This resolves the post-merge branch deletion blockage (branches could not be deleted after squash-merge due to `deletion` rule on `~ALL` with empty bypass list). Rationale: `main` branch already protected by ADR-003 rule #11; feature branches should auto-delete on merge for cleanliness. Requires "Automatically delete head branches" repo setting enabled (done). See `PR7_HANDOFF_CHECKLIST.md` §Branch Deletion Ruleset Issue for full context. | +| 2026-05-01 | `@romandidomizio` | **`deletion` rule removed from `~ALL` branches in both Layer 1 and Layer 2**. This resolves the post-merge branch deletion blockage (branches could not be deleted after squash-merge due to `deletion` rule on `~ALL` with empty bypass list). Rationale: `main` branch already protected by ADR-003 rule #11; feature branches should auto-delete on merge for cleanliness. Requires "Automatically delete head branches" repo setting enabled (done). See the 2026-04-27 and 2026-05-01 Decision Log entries in this ADR for the validation history and branch-deletion rationale. | diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 2013ce4..59e696a 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -583,7 +583,8 @@ Consolidated reference for the most common failure signatures across all workflo | **Branch-name-check** | | | | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | -| No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004 § Validation](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | +| No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | +| No Copilot review on first commit of auto-created PR | **Expected behavior.** The commit was pushed *before* the PR existed. Copilot evaluates auto-review at PR-creation time, not push time. The `review_on_push: true` setting means "re-review on push for PRs that had Copilot auto-requested at creation", not "request Copilot on every push for every PR". | Make any follow-up push to the same branch (trivial whitespace fix, comment addition, etc.). Copilot will review the new push. All subsequent pushes on the same open PR are reviewed automatically. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | diff --git a/CHANGELOG.md b/CHANGELOG.md index bebc9fb..b124688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,11 +30,7 @@ Changes in this section will be promoted to a dated release entry on merge to `m ### Changed - **`.github/ADR-004-copilot-auto-review-ruleset.md` ruleset structure amended (2026-05-01)** — `deletion` rule removed from `~ALL` branches in both Layer 1 (repo-level) and Layer 2 (enterprise-level) rulesets. Retained 2 rules: `non_fast_forward` + `copilot_code_review`. Rationale: post-merge branch deletion was permanently blocked by `deletion` on `~ALL` with empty `bypass_actors` — no one (including org admins) could delete any branch. `main` branch deletion protection remains via ADR-003 rule #11. Decision Log appended. Compliance mappings adjusted (SOC 2 CC6.1, ISO 27001 A.8.13). -- **`CONTRIBUTING.md` §8 troubleshooting expanded (2026-05-01)** — added "My PR is blocked — commits are unsigned" scenario with two clear options: **Option 1** (rebase + force-push to keep same PR, preserving review history) and **Option 2** (close PR, cherry-pick to fresh branch, open new PR). Commands simplified. Explains why force-push is required (commit hash changes when signing is added). Replaces the older single "I need to sign commits I already pushed unsigned" subsection. -- **`PR7_HANDOFF_CHECKLIST.md` updated (2026-05-01)** — branch deletion ruleset issue marked RESOLVED with full resolution steps. Ruleset As-Enabled section updated to include new repo settings. - -### Infrastructure - +- **`CONTRIBUTING.md` §8 troubleshooting expanded (2026-05-01)** — added "My PR is blocked — commits are unsigned" scenario. Removed Option 1 (rebase + force-push) because `non_fast_forward` ruleset on `~ALL` branches blocks force-push. Retained Option 2: close PR, cherry-pick to fresh branch with `git cherry-pick -S`, open new PR. Also added "Copilot review didn't start on my auto-created PR" troubleshooting entry documenting the PR-creation-time caching behavior: Copilot evaluates auto-review at PR-creation time, not push time, so the first commit on a new branch won't trigger review until a subsequent push. All subsequent pushes on an open PR are reviewed automatically. Replaces the older single "I need to sign commits I already pushed unsigned" subsection. - **Repo Settings → General → Pull Requests (2026-05-01)**: enabled **"Automatically delete head branches"** (merged branches auto-delete, replacing the retired `deletion` rule on `~ALL`); enabled **"Always suggest updating pull request branches"** (keeps PRs current with `main` for cleaner merges). --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc5daf7..94138f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -611,48 +611,39 @@ Fix (pick one): ### "My PR is blocked — commits are unsigned" -If your PR shows "Merge blocked — requires signed commits", you have **two options**. Pick based on your situation: - -#### Option 1: Keep the same PR (rebase + force-push) - -Best if you have active review comments you don't want to lose, or the PR has been open for a while with discussion history. +If your PR shows "Merge blocked — requires signed commits", the only viable path is to **close the PR and recreate it with signed commits**. The `non_fast_forward` ruleset on `~ALL` branches (see [ADR-004](.github/ADR-004-copilot-auto-review-ruleset.md)) blocks force-push, so rebase + force-push is not possible. ```bash -# Sign all commits on your branch in one shot -git fetch origin main -git rebase --exec 'git commit --amend --no-edit -S' origin/main - -# Verify (every %G? must show 'G') -git log origin/main..HEAD --pretty='%h %G? %s' - -# Push signed commits (safe — fails if someone else pushed) -git push --force-with-lease origin -``` - -The PR updates automatically with signed commits. Review history is preserved. - -#### Option 2: Close PR, create fresh branch + new PR - -Best if your branch is small, you haven't pushed much, or you want a clean start. +# Step 1: Close the blocked PR in GitHub UI (just click Close) -```bash -# Close the blocked PR in GitHub UI first (just click Close) +# Step 2: Complete §3.1 signing setup first if you haven't already +git config --global commit.gpgsign true +git config --global gpg.format ssh +git config --global user.signingkey ~/.ssh/id_ed25519.pub -# Locally, create a fresh branch from main with your changes +# Step 3: Create a fresh branch from main git fetch origin main git checkout -b feature/- origin/main -# Cherry-pick your changes (each pick will auto-sign thanks to §3.1 setup) -git cherry-pick +# Step 4: Cherry-pick your changes with explicit signing +git cherry-pick -S # Repeat for each commit you want to keep -# Push and let auto-PR create a new one +# Step 5: Push and let auto-PR create a new one git push origin feature/- ``` This creates a new PR with 100% signed commits from the start. Link to the old closed PR in the body for context. -**Why force-push is required for Option 1**: Signing a commit changes its hash (the signature is part of the commit object). Git sees this as "rewriting history" even though the content is identical. `--force-with-lease` is the safe way to update the remote — it rejects the push if anyone else added commits to your branch (protects against overwriting their work). +### "Copilot review didn't start on my auto-created PR" + +**Normal for the first commit on a new branch.** Copilot evaluates auto-review eligibility at **PR-creation time**, not push time. When `auto-pr-to-main.yml` creates the PR via `gh pr create`, the commits already exist on the branch — there is no "new push to an existing PR" event for Copilot to hook. + +**What to do**: Make any follow-up push to the same branch (even a trivial whitespace fix or comment addition). Copilot will review the new push. All subsequent pushes on the same open PR are reviewed automatically. + +**Verification**: Check the PR timeline for `Copilot AI review requested due to automatic review settings`. If you see this entry, the ruleset fired correctly — Copilot just needs a "new push" event to analyze. If the entry is missing entirely, check ADR-004 § Empirical Validation Results. + +--- ### "I'm getting `error: gpg failed to sign the data`" From 4cdbeedc5019787454d4f9edf2e1a6084718b4f2 Mon Sep 17 00:00:00 2001 From: romandidomizio Date: Fri, 1 May 2026 16:35:06 -0600 Subject: [PATCH 06/17] test(copilot): add explanatory comment to trigger auto-review --- .github/workflows/auto-pr-to-main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/auto-pr-to-main.yml b/.github/workflows/auto-pr-to-main.yml index 0f248a2..e193396 100644 --- a/.github/workflows/auto-pr-to-main.yml +++ b/.github/workflows/auto-pr-to-main.yml @@ -4,6 +4,11 @@ name: Auto-Create PR to Main # GitHub Copilot code review is auto-triggered (Copilot only reviews PRs # authored by human-type accounts, not GitHub Apps). # +# NOTE: Copilot evaluates auto-review eligibility at PR-CREATION time, not push +# time. The first commit on a new branch (pushed before the PR exists) won't +# trigger Copilot review. Any follow-up push to the SAME open PR does trigger +# review. See ADR-004 § Empirical Validation Results for the full analysis. +# # See: # - .github/workflows/README.md (authoritative ops reference) # - .github/ADR-001-service-account-pat.md From 994c8be23f65b455b28e33c7d7c321bd23790892 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:31:06 +0500 Subject: [PATCH 07/17] fix: resolve trivy security scan and linting issues --- wordpress/helm/Chart.yaml | 54 ++++---- wordpress/helm/templates/ingress.yaml | 157 +++++++++++----------- wordpress/helm/values-burnedout.yaml | 26 ++-- wordpress/helm/values.yaml | 179 +++++++++++++------------- 4 files changed, 207 insertions(+), 209 deletions(-) diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 6ea379d..4532793 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -1,27 +1,27 @@ -apiVersion: v2 -name: wordpress -description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS -type: application -version: 3.2.7 -appVersion: "6.8.3" - -keywords: -- wordpress -- cms -- blog -- php -- mariadb - -home: https://github.com/WeOwnNetwork/ai -sources: -- https://github.com/WeOwnNetwork/ai/tree/main/wordpress - -maintainers: -- name: Enterprise WordPress - email: admin@example.com - -# No external dependencies - using official MariaDB image via custom StatefulSet - -annotations: - category: CMS - licenses: Apache-2.0 +apiVersion: v2 +name: wordpress +description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS +type: application +version: 3.2.7 +appVersion: "6.8.3" + +keywords: + - wordpress + - cms + - blog + - php + - mariadb + +home: https://github.com/WeOwnNetwork/ai +sources: + - https://github.com/WeOwnNetwork/ai/tree/main/wordpress + +maintainers: + - name: Enterprise WordPress + email: admin@example.com + +# No external dependencies - using official MariaDB image via custom StatefulSet + +annotations: + category: CMS + licenses: Apache-2.0 diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 14b0c61..8ea9a21 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,78 +1,79 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "wordpress.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} - app.kubernetes.io/component: ingress - annotations: - {{- toYaml .Values.ingress.annotations | nindent 4 }} - # Block sensitive files at the edge (Task #264) - nginx.ingress.kubernetes.io/server-snippet: | - location ~* /\.(git|env|user\.ini|htaccess) { - deny all; - return 403; - } - location = /wp-config.php { - deny all; - return 403; - } - # TLS/SSL Security (Enterprise-Grade, Parameterized) - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - {{- if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} - nginx.ingress.kubernetes.io/ssl-protocols: {{ .Values.ingress.tls.protocols | default "TLSv1.2 TLSv1.3" | quote }} - nginx.ingress.kubernetes.io/ssl-ciphers: {{ .Values.ingress.tls.ciphers | default "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" | quote }} - {{- else }} - nginx.ingress.kubernetes.io/ssl-protocols: "TLSv1.2 TLSv1.3" - nginx.ingress.kubernetes.io/ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" - {{- end }} - {{- if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} - # Redirect root domain to www subdomain (using from-to-www annotation) - nginx.ingress.kubernetes.io/from-to-www-redirect: "true" - {{- end }} - # WordPress-specific optimizations - nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" - nginx.ingress.kubernetes.io/proxy-buffers-number: "8" - nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" - nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" - # Rate limiting for brute force protection - nginx.ingress.kubernetes.io/rate-limit: "100" - nginx.ingress.kubernetes.io/rate-limit-window: "1m" - nginx.ingress.kubernetes.io/rate-limit-connections: "10" -spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - tls: - - hosts: - - {{ .Values.wordpress.domain | quote }} - {{- if .Values.wordpress.includeWWW }} - - {{ printf "www.%s" .Values.wordpress.domain | quote }} - {{- end }} - secretName: wordpress-tls - rules: - - host: {{ .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- if .Values.wordpress.includeWWW }} - - host: {{ printf "www.%s" .Values.wordpress.domain | quote }} - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: {{ include "wordpress.fullname" . }} - port: - number: {{ .Values.service.port }} - {{- end }} -{{- end }} +# {{ if .Values.ingress.enabled }} +# {{ $tlsProtocols := "TLSv1.2 TLSv1.3" }} +# {{ $tlsCiphers := "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" }} +# {{ if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} +# {{ $tlsProtocols = .Values.ingress.tls.protocols | default $tlsProtocols }} +# {{ $tlsCiphers = .Values.ingress.tls.ciphers | default $tlsCiphers }} +# {{ end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: '{{ include "wordpress.fullname" . }}' + namespace: "{{ .Release.Namespace }}" + labels: + # {{ include "wordpress.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress + annotations: + # {{ toYaml .Values.ingress.annotations | nindent 4 }} + # Block sensitive files at the edge (Task #264) + nginx.ingress.kubernetes.io/server-snippet: | + location ~* /\.(git|env|user\.ini|htaccess) { + deny all; + return 403; + } + location = /wp-config.php { + deny all; + return 403; + } + # TLS/SSL Security (Enterprise-Grade, Parameterized) + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/ssl-protocols: "{{ $tlsProtocols }}" + nginx.ingress.kubernetes.io/ssl-ciphers: "{{ $tlsCiphers }}" + # {{ if and .Values.wordpress.includeWWW .Values.wordpress.redirectToWWW }} + # Redirect root domain to www subdomain (using from-to-www annotation) + nginx.ingress.kubernetes.io/from-to-www-redirect: "true" + # {{ end }} + # WordPress-specific optimizations + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/client-body-buffer-size: "16k" + nginx.ingress.kubernetes.io/large-client-header-buffers: "4 32k" + # Rate limiting for brute force protection + nginx.ingress.kubernetes.io/rate-limit: "100" + nginx.ingress.kubernetes.io/rate-limit-window: "1m" + nginx.ingress.kubernetes.io/rate-limit-connections: "10" +spec: + # {{ if .Values.ingress.className }} + ingressClassName: "{{ .Values.ingress.className }}" + # {{ end }} + tls: + - hosts: + - "{{ .Values.wordpress.domain }}" + # {{ if .Values.wordpress.includeWWW }} + - '{{ printf "www.%s" .Values.wordpress.domain }}' + # {{ end }} + secretName: wordpress-tls + rules: + - host: "{{ .Values.wordpress.domain }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: '{{ include "wordpress.fullname" . }}' + port: + # {{ printf "number: %v" .Values.service.port | nindent 18 }} + # {{ if .Values.wordpress.includeWWW }} + - host: '{{ printf "www.%s" .Values.wordpress.domain }}' + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: '{{ include "wordpress.fullname" . }}' + port: + # {{ printf "number: %v" .Values.service.port | nindent 18 }} + # {{ end }} +# {{ end }} diff --git a/wordpress/helm/values-burnedout.yaml b/wordpress/helm/values-burnedout.yaml index 52563e7..14c480a 100644 --- a/wordpress/helm/values-burnedout.yaml +++ b/wordpress/helm/values-burnedout.yaml @@ -1,13 +1,13 @@ -wordpress: - domain: "burnedout.xyz" - wordpressBlogName: "BurnedOut" - includeWWW: true - redirectToWWW: true - -ingress: - enabled: true - className: "nginx" - tls: - - secretName: burnedout-tls - hosts: - - burnedout.xyz \ No newline at end of file +wordpress: + domain: "burnedout.xyz" + wordpressBlogName: "BurnedOut" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: burnedout-tls + hosts: + - burnedout.xyz diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index 3df8c09..d4953c1 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -10,9 +10,6 @@ serviceAccount: automount: false annotations: {} -# Pod Security Standards: Service Account Token Automount -automountServiceAccountToken: false - ## Pod Disruption Budget podDisruptionBudget: enabled: true @@ -21,56 +18,56 @@ podDisruptionBudget: ## cert-manager Configuration certManager: email: "EMAIL_PLACEHOLDER" - createClusterIssuer: false # Use existing ClusterIssuer + createClusterIssuer: false # Use existing ClusterIssuer ## Zero-Trust NetworkPolicy (Enterprise Security) networkPolicy: enabled: true policyTypes: - - Ingress - - Egress + - Ingress + - Egress # Ingress: Only allow NGINX Ingress Controller and same namespace ingress: - - from: - # Allow NGINX Ingress Controller - - namespaceSelector: - matchLabels: - name: ingress-nginx - # Allow same namespace communication - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 80 - - protocol: TCP - port: 443 + - from: + # Allow NGINX Ingress Controller + - namespaceSelector: + matchLabels: + name: ingress-nginx + # Allow same namespace communication + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 80 + - protocol: TCP + port: 443 # Egress: Restricted to essential services only egress: - # DNS resolution - - to: [] - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # HTTPS for WordPress updates and plugins - - to: [] - ports: - - protocol: TCP - port: 443 - # Allow communication with MariaDB and Redis in same namespace - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: wordpress - ports: - - protocol: TCP - port: 3306 # MariaDB - - protocol: TCP - port: 6379 # Redis + # DNS resolution + - to: [] + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # HTTPS for WordPress updates and plugins + - to: [] + ports: + - protocol: TCP + port: 443 + # Allow communication with MariaDB and Redis in same namespace + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: wordpress + ports: + - protocol: TCP + port: 3306 # MariaDB + - protocol: TCP + port: 6379 # Redis ## Replica Configuration replicaCount: 1 @@ -93,12 +90,12 @@ wordpress: ## WordPress Site Configuration wordpressBlogName: "Enterprise WordPress Site" wordpressScheme: https - domain: "" # Set via --set global.domain or deploy script + domain: "" # Set via --set global.domain or deploy script enableMultisite: false wordpressTablePrefix: "wp_" - includeWWW: false # Automatically configure www subdomain for main domain - redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) - redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) + includeWWW: false # Automatically configure www subdomain for main domain + redirectToWWW: false # Redirect root domain to www subdomain (e.g., example.com → www.example.com) + redirectFromWWW: false # Redirect www subdomain to root domain (e.g., www.example.com → example.com) ## WordPress Extra WP Config Content wordpressExtraWpConfigContent: | @@ -148,13 +145,13 @@ wordpress: nonceSalt: "NONCE_SALT_PLACEHOLDER" ## Container / Pod Security Context (Production Ready) automountServiceAccountToken: false - runAsUser: 0 # Required for Apache port 80 binding + runAsUser: 0 # Required for Apache port 80 binding runAsGroup: 0 runAsNonRoot: false - fsGroup: 33 # www-data for file permissions + fsGroup: 33 # www-data for file permissions readOnlyRootFilesystem: false allowPrivilegeEscalation: false - capabilities: {} # Use default capabilities for WordPress + capabilities: {} # Use default capabilities for WordPress seccompProfile: type: RuntimeDefault @@ -165,7 +162,7 @@ wordpress: accessModes: - ReadWriteOnce size: 8Gi - coreSize: 4Gi # WordPress core files persistence + coreSize: 4Gi # WordPress core files persistence # Separate volumes for security volumes: content: @@ -180,17 +177,17 @@ wordpress: ## Extra environment variables extraEnvVars: - - name: WORDPRESS_ENABLE_REDIS - value: "yes" # Enable Redis since we re-enabled it - - name: APACHE_HTTP_PORT - value: "8080" - - name: WORDPRESS_CONFIG_EXTRA - value: | - define('WP_CACHE', true); - define('DISABLE_FILE_EDITING', true); - define('DISALLOW_FILE_MODS', true); - define('FORCE_SSL_ADMIN', true); - define('WP_AUTO_UPDATE_CORE', true); + - name: WORDPRESS_ENABLE_REDIS + value: "yes" # Enable Redis since we re-enabled it + - name: APACHE_HTTP_PORT + value: "80" + - name: WORDPRESS_CONFIG_EXTRA + value: | + define('WP_CACHE', true); + define('DISABLE_FILE_EDITING', true); + define('DISALLOW_FILE_MODS', true); + define('FORCE_SSL_ADMIN', true); + define('WP_AUTO_UPDATE_CORE', true); ## Init Containers (WordPress Hardening) - Disabled for resource optimization initContainers: @@ -210,12 +207,12 @@ wordpress: # Based on actual usage: WordPress uses ~380Mi in production resources: limits: - cpu: 800m # Sufficient for Kadence AI theme installation - memory: 1Gi # Prevents OOMKilled during plugin installs + cpu: 800m # Sufficient for Kadence AI theme installation + memory: 1Gi # Prevents OOMKilled during plugin installs ephemeral-storage: 800Mi requests: - cpu: 50m # Cluster-optimized baseline - memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) + cpu: 50m # Cluster-optimized baseline + memory: 384Mi # Matches actual usage to prevent eviction (was 256Mi) ephemeral-storage: 200Mi ## Health Checks (Production Ready - TCP mode for iThemes Security compatibility) livenessProbe: @@ -240,7 +237,7 @@ wordpress: ## Auto-scaling Configuration (Disabled for single replica + RWO volumes) autoscaling: - enabled: false # Must be false when using ReadWriteOnce volumes + enabled: false # Must be false when using ReadWriteOnce volumes ## Environment Variables extraEnvVars: [] @@ -256,25 +253,24 @@ ingress: # ssl-protocols: "TLSv1.2 TLSv1.3" # ssl-ciphers: Modern cipher suites for mobile browser compatibility # WordPress Performance - nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads ## TLS Security Configuration (Parameterized for flexibility) tls: - protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) + protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices) # Cipher suites: Mozilla "Intermediate" profile - balances security with compatibility # Supports: Perfect Forward Secrecy, mobile devices (CHACHA20), modern encryption (AES-GCM) ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" hosts: - - host: "" - paths: - - path: / - pathType: Prefix - + - host: "" + paths: + - path: / + pathType: Prefix ## Cron Jobs (Production Maintenance) cron: enabled: true - schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) + schedule: "*/5 * * * *" # Every 5 minutes (prevents action_scheduler delays) image: registry: docker.io repository: wordpress @@ -290,8 +286,8 @@ cron: ## Backup Configuration (Production Critical) backup: enabled: true - schedule: "0 2 * * *" # Daily at 2 AM - retention: 30 # Keep 30 days of backups + schedule: "0 2 * * *" # Daily at 2 AM + retention: 30 # Keep 30 days of backups resources: limits: cpu: 200m @@ -305,11 +301,11 @@ backup: ## MariaDB Configuration (Official MariaDB 12.0.2 Latest Stable) mariadb: - enabled: false # Using custom StatefulSet with official image + enabled: false # Using custom StatefulSet with official image mariadbOfficial: enabled: true - image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 + image: mariadb:12.0.2 # Official MariaDB Latest Stable - October 2025 auth: database: wordpress username: wordpress @@ -318,7 +314,7 @@ mariadbOfficial: ## Security Context (Pod Security Standards: Restricted) podSecurityContext: - runAsUser: 999 # Official MariaDB mysql user + runAsUser: 999 # Official MariaDB mysql user runAsGroup: 999 runAsNonRoot: true fsGroup: 999 @@ -326,26 +322,26 @@ mariadbOfficial: type: RuntimeDefault containerSecurityContext: - runAsUser: 999 # Official MariaDB mysql user + runAsUser: 999 # Official MariaDB mysql user runAsGroup: 999 runAsNonRoot: true allowPrivilegeEscalation: false readOnlyRootFilesystem: false capabilities: drop: - - ALL + - ALL seccompProfile: type: RuntimeDefault ## Resource Configuration (MariaDB Memory-Optimized - WeOwn Standardized) resources: limits: - cpu: 250m # Optimized for database operations - memory: 512Mi # Increased for initialization (prevents OOMKilled) + cpu: 250m # Optimized for database operations + memory: 512Mi # Increased for initialization (prevents OOMKilled) ephemeral-storage: 1Gi requests: - cpu: 50m # Matches cluster optimization - memory: 256Mi # Increased for initialization stability + cpu: 50m # Matches cluster optimization + memory: 256Mi # Increased for initialization stability ## MariaDB Performance Tuning (2-node cluster optimized) configuration: | @@ -380,7 +376,7 @@ redis: ephemeral-storage: 50Mi persistence: - enabled: false # Disabled for performance in 2-node cluster + enabled: false # Disabled for performance in 2-node cluster ## Security context for Redis podSecurityContext: @@ -399,13 +395,13 @@ redis: readOnlyRootFilesystem: false capabilities: drop: - - ALL + - ALL seccompProfile: type: RuntimeDefault ## Service Configuration service: - type: ClusterIP # Secure internal-only access + type: ClusterIP # Secure internal-only access port: 80 targetPort: 80 annotations: {} @@ -424,11 +420,12 @@ muPlugins: cache: enabled: true authFix: - enabled: true # General REST API Authentication fix for containerized environments + enabled: true # General REST API Authentication fix for containerized environments disableXmlRpc: - enabled: true # Security best practice: Disable XML-RPC (use REST API instead) + enabled: true # Security best practice: Disable XML-RPC (use REST API instead) smtp: - enabled: true # Enterprise SMTP Configuration via Environment Variables + enabled: true # Enterprise SMTP Configuration via Environment Variables + ## WordPress Plugins (Manual Installation Recommended) # Plugins are not pre-configured in this deployment for security and flexibility From c073eb0dd3efc1010460820cea711bd27ff0c1ce Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:42:50 +0500 Subject: [PATCH 08/17] udated version bump --- wordpress/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wordpress/CHANGELOG.md b/wordpress/CHANGELOG.md index c3f27f6..a16672c 100644 --- a/wordpress/CHANGELOG.md +++ b/wordpress/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this WordPress deployment will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.2.7] - 2026-05-06 + +### 🔧 **Version Bump & Maintenance** + +#### **Updated** + +- Chart version updated to 3.2.7 +- Maintenance and stability improvements + ## [3.3.8] - 2025-11-07 ### 🔧 **Critical Fix: WP_MEMORY_LIMIT Configuration & Database Restoration** From 52bc0a2890e8bda5894abace99d744f6644a8a15 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 22:52:40 +0500 Subject: [PATCH 09/17] fix: resolve yamllint line endings and comment indentation --- wordpress/helm/values-ptoken.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/wordpress/helm/values-ptoken.yaml b/wordpress/helm/values-ptoken.yaml index 99b59aa..cb5bd3b 100644 --- a/wordpress/helm/values-ptoken.yaml +++ b/wordpress/helm/values-ptoken.yaml @@ -1,13 +1,13 @@ -wordpress: - domain: "ptoken.agency" - wordpressBlogName: "PToken Agency" - includeWWW: true - redirectToWWW: true - -ingress: - enabled: true - className: "nginx" - tls: - - secretName: ptoken-tls - hosts: - - ptoken.agency \ No newline at end of file +wordpress: + domain: "ptoken.agency" + wordpressBlogName: "PToken Agency" + includeWWW: true + redirectToWWW: true + +ingress: + enabled: true + className: "nginx" + tls: + - secretName: ptoken-tls + hosts: + - ptoken.agency From 488ebfbdd524483ed3da4d511d845f6caf620978 Mon Sep 17 00:00:00 2001 From: "m.shahid" Date: Wed, 6 May 2026 23:03:36 +0500 Subject: [PATCH 10/17] fix: resolve yamllint line endings --- .../helm/templates/php-config-configmap.yaml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index 139805d..ca51b42 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -1,18 +1,18 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "wordpress.fullname" . }}-php-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "wordpress.labels" . | nindent 4 }} -data: - uploads.ini: | - ; PHP Upload Configuration - Codified (D152 & #264) - upload_max_filesize = 128M - post_max_size = 128M - max_execution_time = 300 - max_input_time = 300 - memory_limit = 256M - - ; Wordfence WAF Hardening: Forces loading via PHP-FPM - auto_prepend_file = '/var/www/html/wordfence-waf.php' \ No newline at end of file +apiVersion: v1 +kind: ConfigMap +metadata: + name: '{{ include "wordpress.fullname" . }}-php-config' + namespace: "{{ .Release.Namespace }}" + labels: + # {{ include "wordpress.labels" . | nindent 4 }} +data: + uploads.ini: | + ; PHP Upload Configuration - Codified (D152 & #264) + upload_max_filesize = 128M + post_max_size = 128M + max_execution_time = 300 + max_input_time = 300 + memory_limit = 256M + + ; Wordfence WAF Hardening: Forces loading via PHP-FPM + auto_prepend_file = '/var/www/html/wordfence-waf.php' From dc439ff2434c757db3698b63b2d3d05d8602c4c7 Mon Sep 17 00:00:00 2001 From: romandidomizio Date: Wed, 13 May 2026 11:58:01 -0600 Subject: [PATCH 11/17] docs(v3.3.5.1): CODEOWNERS handoff, 1-reviewer, Copilot PR#17 fixes + ADR updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## CODEOWNERS contributor handoff (2026-05-15) - Remove @romandidomizio from all path assignments (left 2026-05-15) - Remove @dhruvmalik007 references (no longer with team) - @ncimino is now sole assigned reviewer on all paths - @iamwaseem18 and @mshahid538 available at @ncimino's discretion - Drop all TODO(2026-05-15) handoff comments; clean up header ## Required reviewers: 2 → 1 - CODEOWNERS header, CONTRIBUTING.md §6.4, README.md Code Review Model - ADR-003 rule #1 table + Consequences; ADR-004 CIS row + pruning criteria - auto-pr-to-main.yml PR body; workflows/README.md §11 troubleshooting row ## ADR Deciders updated (all four ADRs) - Format: @romandidomizio (original author, left 2026-05-15) — @ncimino (current maintainer) - ADR-001: stewardship bullet updated; risk table stewardship row resolved - ADR-003: ADR-004 related-doc ref fixed (deletion rule removed); 2-reviewer → 1-reviewer throughout Consequences + Dev Attribution section ## Copilot PR #17 comment fixes - CONTRIBUTING.md §8 Cause B: remove git push --force-with-lease (blocked by non_fast_forward ruleset); redirect to close+recreate path - ADR-004 title: remove "Deletion Protection" (rule removed from ~ALL 2026-05-01) - ADR-004 verification expected output: ["deletion","non_fast_forward","copilot_code_review"] → ["non_fast_forward","copilot_code_review"]; "3 rules" → "2 rules" - ADR-004 Decision Log: remove PR7_HANDOFF_CHECKLIST.md reference - ADR-004 forward-looking posture: clarify auto-PR workflow push-before-PR pattern - auto-pr-to-main.yml NOTE: rewrite to clarify push-before-PR pattern; reconcile with ADR-004 empirical validation - workflows/README.md §11 Copilot row: clarify push-before-PR is specific to auto-pr-to-main.yml; manually-created PRs get Copilot at PR-creation time ## CONTRIBUTING.md additional updates - §1 prerequisites: @romandidomizio/@dhruvmalik007 contact → @ncimino - §10 Questions: governance contact → @ncimino + CODEOWNERS link ## auto-pr-to-main.yml reviewer assignment - --add-reviewer ncimino,romandidomizio → --add-reviewer ncimino (both paths) - Remove TODO(2026-05-15) comments from both PR update + PR create paths --- .github/ADR-001-service-account-pat.md | 8 +-- .github/ADR-002-infisical-github-sync.md | 2 +- .github/ADR-003-main-branch-ruleset.md | 12 ++-- .../ADR-004-copilot-auto-review-ruleset.md | 16 ++--- .github/CODEOWNERS | 70 ++++++++----------- .github/workflows/README.md | 6 +- .github/workflows/auto-pr-to-main.yml | 21 +++--- CHANGELOG.md | 14 ++++ CONTRIBUTING.md | 13 ++-- README.md | 2 +- 10 files changed, 86 insertions(+), 78 deletions(-) diff --git a/.github/ADR-001-service-account-pat.md b/.github/ADR-001-service-account-pat.md index 02dd466..0870229 100644 --- a/.github/ADR-001-service-account-pat.md +++ b/.github/ADR-001-service-account-pat.md @@ -3,7 +3,7 @@ **Status**: Accepted **Version**: v3.3.4.1 (#WeOwnVer) **Date**: 2026-04-23 -**Deciders**: `@romandidomizio`, `@ncimino` +**Deciders**: `@romandidomizio` (original author, left 2026-05-15) — `@ncimino` (current maintainer) **Supersedes**: None **Superseded by**: None @@ -40,7 +40,7 @@ Key properties: 4. **90-day expiration** — enforced by GitHub for fine-grained tokens 5. **Centralized secret management** — all PATs stored in Infisical project `weown-bot GitHub PATs` (see ADR-002) 6. **2FA mandatory** on the `weown-bot` GitHub account (TOTP + recovery codes held by infrastructure team) -7. **Documented stewardship** — primary owner today is `@romandidomizio`; transitions to one of Mohammed/Shahid/Dhruv post-2026-05-15 (see CODEOWNERS) +7. **Documented stewardship** — primary PAT steward is `@ncimino` (Nik) as of 2026-05-15 (see CODEOWNERS); `@iamwaseem18` and `@mshahid538` are secondary stewards at `@ncimino`'s discretion 8. **No direct commit access** — branch protection rules require PRs; `weown-bot` authors PRs but does not merge to `main` --- @@ -97,10 +97,10 @@ Some teams mint short-lived tokens via GitHub OIDC → an external IdP → GitHu | Risk | Likelihood | Impact | Mitigation | |---|---|---|---| -| PAT leaked | Low | Medium | Fine-grained scope, Infisical audit logs, immediate rotation procedure, branch protection still requires 2 reviewers | +| PAT leaked | Low | Medium | Fine-grained scope, Infisical audit logs, immediate rotation procedure, branch protection still requires 1 reviewer | | PAT expires unrotated | Medium | Low | 3-layer alert stack (GitHub email, Infisical reminder, scheduled `pat-health-check.yml`) | | `weown-bot` account compromised | Low | High | 2FA mandatory, unique email, enterprise-managed, no direct commit access, incident response in `INCIDENT_RESPONSE.md` | -| Stewardship gap post-2026-05-15 | Medium | Medium | CODEOWNERS TODO + transition checklist in workflows README | +| Stewardship gap post-2026-05-15 | Resolved | — | Transition complete 2026-05-15; `@ncimino` is primary steward per CODEOWNERS; `@iamwaseem18`/`@mshahid538` available at `@ncimino`'s discretion | --- diff --git a/.github/ADR-002-infisical-github-sync.md b/.github/ADR-002-infisical-github-sync.md index edb86c9..1b0c563 100644 --- a/.github/ADR-002-infisical-github-sync.md +++ b/.github/ADR-002-infisical-github-sync.md @@ -3,7 +3,7 @@ **Status**: Accepted **Version**: v3.3.5.1 (#WeOwnVer) **Date**: 2026-04-23 (initial) / 2026-04-28 (naming convention revised twice + canonical no-trailing-slash form for folder paths — see Decision Log) -**Deciders**: `@romandidomizio`, `@ncimino` +**Deciders**: `@romandidomizio` (original author, left 2026-05-15) — `@ncimino` (current maintainer) **Related**: ADR-001 (service account and PATs) **Supersedes**: None **Superseded by**: None diff --git a/.github/ADR-003-main-branch-ruleset.md b/.github/ADR-003-main-branch-ruleset.md index 2df963e..9855d0d 100644 --- a/.github/ADR-003-main-branch-ruleset.md +++ b/.github/ADR-003-main-branch-ruleset.md @@ -3,13 +3,13 @@ **Status**: Accepted **Version**: v3.3.5.1 (#WeOwnVer) **Date**: 2026-04-23 (ruleset configured) / 2026-04-27 (ADR last revised) -**Deciders**: `@romandidomizio`, `@ncimino` +**Deciders**: `@romandidomizio` (original author, left 2026-05-15) — `@ncimino` (current maintainer) **Supersedes**: None **Superseded by**: None **Related**: - [`ADR-001`](ADR-001-service-account-pat.md) — service account + PAT posture - [`ADR-002`](ADR-002-infisical-github-sync.md) — Infisical secret synchronization -- [`ADR-004`](ADR-004-copilot-auto-review-ruleset.md) — `~ALL` branches ruleset (deletion + non_fast_forward + copilot_code_review) at both repo and enterprise scope; complement to this ADR +- [`ADR-004`](ADR-004-copilot-auto-review-ruleset.md) — `~ALL` branches ruleset (`non_fast_forward` + `copilot_code_review`) at both repo and enterprise scope; complement to this ADR - [`.github/workflows/README.md` §8.1](workflows/README.md#81-branch-ruleset-on-main-configured-2026-04-23) — authoritative ruleset reference - [`.github/CODEOWNERS`](CODEOWNERS) — path-based reviewer enforcement @@ -54,7 +54,7 @@ Prior to this ADR, `main` was protected only by the legacy Branch Protection UI | # | Rule | SOC 2 | ISO 27001 | ISO 42001 | NIST CSF 2.0 | CIS v8 | Rationale | |---|---|---|---|---|---|---|---| -| 1 | Require PR with 2 reviewers | CC6.3, CC8.1 | A.5.15, A.5.37 | A.6.2.8 | PR.AC-4, PR.IP-3 | 16.9, 16.11 | Segregation of duties; no solo merges | +| 1 | Require PR with 1 reviewer | CC6.3, CC8.1 | A.5.15, A.5.37 | A.6.2.8 | PR.AC-4, PR.IP-3 | 16.9, 16.11 | Reviewer oversight; no unreviewed merges | | 2 | Dismiss stale approvals on new push | CC8.1 | A.5.37 | A.9.4 | PR.IP-1 | 16.11 | Prevents approve-then-amend bypass | | 3 | Require review from Code Owners | CC6.3 | A.5.15 | A.6.2.8 | PR.AC-4 | 16.9 | Path-specific expertise enforced | | 4 | Require approval of most recent reviewable push | CC8.1 | A.5.37 | A.9.4 | PR.IP-1 | 16.11 | Closes race: approve PR → sneak bad commit → merge | @@ -114,11 +114,11 @@ Under SOC 2 CC6.3 and ISO 27001 A.5.15, reviewers and approvers must be subject - **Mechanical enforcement**: All rules apply without human intervention. No "we forgot to check" gaps. - **AI review depth**: Rules #10 + CodeQL #9 ensure every change gets both rule-based (CodeQL) and context-aware (Copilot) review before human approval. - **Incident containment**: Rules #11 + #12 + signed commits (#6) make history rewriting / branch destruction cryptographically and administratively hard. -- **Small-team scalability**: With only 2 active approvers today (`@ncimino` + `@romandidomizio`), the 2-reviewer rule forces coordination but does not block progress. Post-2026-05-15 handoff expands the approver pool per `CODEOWNERS` and the transition checklist. +- **Small-team scalability**: `@ncimino` is the primary approver (sole CODEOWNERS assignee as of 2026-05-15); the 1-reviewer rule ensures coverage without blocking progress. `@iamwaseem18` and `@mshahid538` are available as secondary reviewers at `@ncimino`'s discretion per CODEOWNERS. ### Negative / trade-offs -- **Merge latency**: A PR needs 2 approvers to merge. With distributed teams this may add 12-24h per PR. Mitigation: same-day turnaround culture; urgent hotfixes route through `hotfix/*` with the same ruleset (no bypass) — escalation is a reviewer-availability issue, not a ruleset issue. +- **Merge latency**: A PR needs 1 approver to merge. With distributed teams this may add 12-24h per PR. Mitigation: same-day turnaround culture; urgent hotfixes route through `hotfix/*` with the same ruleset (no bypass) — escalation is a reviewer-availability issue, not a ruleset issue. - **CodeQL false positives**: Default Setup's "warning and higher" threshold means some low-confidence findings can block merges. Mitigation: reviewer dismisses with justification in the Code Quality tab (this action is itself audit-logged). - **External contributor friction**: Fork-PRs from outside the org need reviewers to explicitly trigger workflow runs + approve CodeQL. This is the intended posture — external contributions deserve extra scrutiny. - **Bypass list discipline**: Adding even one role to the bypass list breaks SOC 2 evidence. Any proposal to add a bypass must be documented here as a superseding ADR. @@ -151,7 +151,7 @@ We evaluated three postures for the `` segment: - Team size (~6 core contributors as of 2026-04-23) doesn't justify Option A's maintenance cost - External contributors (audit reviewers, one-time collaborators) are expected occasionally and must remain unblocked - PR review records + CODEOWNERS enforcement already provide audit-grade attribution -- The 2-reviewer rule (#1) + CODEOWNERS (#3) catch misuse socially +- The 1-reviewer rule (#1) + CODEOWNERS (#3) catch misuse socially - `auto-pr-to-main.yml` attributes automation activity using `${{ github.triggering_actor || github.actor }}`, so the recorded actor is the GitHub user who triggered the workflow run (push, `workflow_dispatch`, or re-run) when available, or the workflow actor otherwise. Attribution is derived directly from GitHub's event context rather than branch-name parsing, inline handle mapping, or git-author-email fallback — no maintenance, no drift risk, and audit evidence is consistent with GitHub's own audit log ### Upgrade triggers — when to revisit diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index bb6418e..c852fc0 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -1,9 +1,9 @@ -# ADR-004: `~ALL` Branches Ruleset (Copilot Auto-Review + Force-Push / Deletion Protection) — Repo-Level + Enterprise-Level Defense-in-Depth +# ADR-004: `~ALL` Branches Ruleset (Copilot Auto-Review + Force-Push Protection) — Repo-Level + Enterprise-Level Defense-in-Depth **Status**: Accepted **Version**: v3.3.5.1 (#WeOwnVer) **Date**: 2026-04-23 (repo-level) / 2026-04-27 (enterprise-level added) / 2026-04-28 (R13 + R18 clarifications on auto-trigger PR-creation-time semantics) -**Deciders**: `@romandidomizio`, `@ncimino` +**Deciders**: `@romandidomizio` (original author, left 2026-05-15) — `@ncimino` (current maintainer) **Supersedes**: None **Superseded by**: None **Related**: @@ -85,7 +85,7 @@ Both rulesets enforce **identical rules**. There is no rule-sync burden because | **ISO/IEC 42001:2023 A.6.2.8** (AI deployment review) | `copilot_code_review` | All deployment-related code (Helm, K8s, CI) gets AI review before merge to `main` | | **NIST CSF 2.0 PR.IP-3** (configuration change control) | `non_fast_forward` | Configuration changes (workflow YAML, Helm values, K8s manifests) cannot be silently rewritten after review | | **NIST CSF 2.0 DE.CM-1** (continuous monitoring of network) | `copilot_code_review` | AI continuously monitors PR diffs for security regressions | -| **CIS Controls v8 18.3** (development security — code review) | `copilot_code_review` | Automated security-aware review on all PRs (defense-in-depth alongside CodeQL + branch-name + 2-reviewer rule) | +| **CIS Controls v8 18.3** (development security — code review) | `copilot_code_review` | Automated security-aware review on all PRs (defense-in-depth alongside CodeQL + branch-name + 1-reviewer rule) | Audit evidence cross-reference: - Ruleset config exportable via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` (Layer 1) and the enterprise rulesets API (Layer 2) @@ -114,7 +114,7 @@ Audit evidence cross-reference: 1. **5+ consecutive bot-authored PRs** auto-trigger Copilot review via Layer 2 alone (i.e., temporarily disable Layer 1 for testing, observe Copilot fires on every push to every PR, re-enable Layer 1, then schedule pruning). 2. **No enterprise-level migration is planned** in the next 6 months (org restructure, billing change, SKU change). 3. **Layer 2 deletion + non_fast_forward enforcement is verified** via a deliberate test (e.g., attempt force-push from a fresh fork; confirm rejection with the expected enterprise-ruleset error message). -4. **Reviewer (`@ncimino` + `@romandidomizio` minimum) signs off** on a "drop Layer 1" PR with explicit ADR-004 update marking Layer 1 as "Removed YYYY-MM-DD". +4. **Reviewer (`@ncimino` minimum) signs off** on a "drop Layer 1" PR with explicit ADR-004 update marking Layer 1 as "Removed YYYY-MM-DD". If any criterion fails, KEEP Layer 1. @@ -144,7 +144,7 @@ Expected output: "enforcement": "active", "target": ["~ALL"], "bypass": [], - "rules": ["deletion", "non_fast_forward", "copilot_code_review"] + "rules": ["non_fast_forward", "copilot_code_review"] } ``` @@ -159,7 +159,7 @@ gh api /enterprises//rulesets --jq '.[] | select(.name | contai }' ``` -Expected: identical 3 rules, scope = all repos in all orgs. +Expected: identical 2 rules (`non_fast_forward` + `copilot_code_review`), scope = all repos in all orgs. ### End-to-end auto-trigger validation @@ -219,7 +219,7 @@ After Layer 2 was configured and 6 commits were pushed to PR #13 without any aut **Forward-looking posture**: - All NEW PRs after 2026-04-27 will auto-trigger Copilot without manual intervention. Validates the Layer 2 hypothesis. - PR #13 and any other pre-existing open PRs remain manual-review-only until merged. -- No workflow changes needed — the auto-PR workflow's `gh pr create` is the correct mechanism and gets the auto-trigger at creation time. +- **Note for auto-created PRs** (via `auto-pr-to-main.yml`): commits are pushed to the branch *before* the PR is created, so the PR-creation event has no new push delta for Copilot to review. The first Copilot review on auto-created PRs is triggered by the **next push** to the open PR (any follow-up commit). This is expected, documented, and does not require workflow changes — `review_on_push: true` ensures all subsequent pushes are reviewed automatically. See also [`.github/workflows/auto-pr-to-main.yml`](workflows/auto-pr-to-main.yml) NOTE comment at the top of the file. - The `copilot_code_review` rule in both Layer 1 and Layer 2 stays configured as-is (`review_draft_pull_requests: true, review_on_push: true`) because both semantics are desirable for future PRs. This validation also refines the pruning criteria for Layer 1: the "5+ consecutive auto-triggers" bar now starts counting from NEW PRs only; PR #13's 6+ pushes-without-trigger do not count as failures. @@ -233,5 +233,5 @@ This validation also refines the pruning criteria for Layer 1: the "5+ consecuti | 2026-04-23 | `@romandidomizio` | Layer 1 ("Copilot auto-review" ruleset, id 12131972) configured at repo level | | 2026-04-27 | `@romandidomizio` | Layer 2 (enterprise-level ruleset) added after rounds 1–5 of PR #13 confirmed Layer 1 alone does not auto-trigger Copilot for `weown-bot`. Hypothesis: Copilot Business entitlement requires enterprise-scoped enforcement | | 2026-04-27 | `@romandidomizio` | This ADR authored as part of v3.3.4.2 round-6 close-out; documents both layers + defense-in-depth rationale + pruning criteria | -| 2026-04-27 | `@romandidomizio` | **ADR updated to v3.3.5.1 (round-7 close-out)**. Added "Empirical Validation Results" section with controlled-experiment findings: Layer 2 + Copilot Business entitlement work correctly; PR-creation-time caching is what prevents auto-trigger on pre-existing PR #13. First confirmed enterprise-level auto-trigger: the fresh PR opened on 2026-04-27 for the `velero-restic` branch (PR number + workflow run + timestamp to be filled in after merge). Resolved Copilot R7 comment #5 (retargeted this validation step from `PR7_HANDOFF_CHECKLIST.md` to this Decision Log). | +| 2026-04-27 | `@romandidomizio` | **ADR updated to v3.3.5.1 (round-7 close-out)**. Added "Empirical Validation Results" section with controlled-experiment findings: Layer 2 + Copilot Business entitlement work correctly; PR-creation-time caching is what prevents auto-trigger on pre-existing PR #13. First confirmed enterprise-level auto-trigger: the fresh PR opened on 2026-04-27 for the `velero-restic` branch (PR number + workflow run + timestamp to be filled in after merge). Resolved Copilot R7 comment #5 (retargeted this validation step to this Decision Log). | | 2026-05-01 | `@romandidomizio` | **`deletion` rule removed from `~ALL` branches in both Layer 1 and Layer 2**. This resolves the post-merge branch deletion blockage (branches could not be deleted after squash-merge due to `deletion` rule on `~ALL` with empty bypass list). Rationale: `main` branch already protected by ADR-003 rule #11; feature branches should auto-delete on merge for cleanliness. Requires "Automatically delete head branches" repo setting enabled (done). See the 2026-04-27 and 2026-05-01 Decision Log entries in this ADR for the validation history and branch-deletion rationale. | diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 848464b..ffa1215 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,59 +1,51 @@ # CODEOWNERS — WeOwnNetwork/ai # ────────────────────────────────────────────────────────────────────── # PURPOSE: Default reviewers for all paths; enforced by branch protection -# requiring 2 approvals on `main` + review from Code Owners. +# requiring 1 approval on `main` + review from Code Owners. # -# CURRENT (pre-2026-05-15): -# @ncimino (universal reviewer — stays post-transition) -# @romandidomizio (primary repo steward — leaving 2026-05-15) +# PRIMARY REVIEWER (all paths): +# @ncimino (universal reviewer) # -# POST-2026-05-15 HANDOFF (handles confirmed 2026-04-23): -# @ncimino stays as universal reviewer on every path. -# @romandidomizio is replaced per-path by ONE of: -# @iamwaseem18 (IaC / Infisical) -# @mshahid538 (Docker / DigitalOcean) -# @dhruvmalik007 (Agentic AI) -# Final per-path assignments decided by @ncimino + @romandidomizio before 2026-05-15. +# ADDITIONAL REVIEWERS (assignable at @ncimino's discretion per path): +# @iamwaseem18 (Mohammed — IaC / Infisical) +# @mshahid538 (Shahid — Docker / DigitalOcean) # # EXECUTIVE STAKEHOLDER (not a path reviewer — avoids notification noise): # @YonksTEAM (looped in for org-wide policy changes via @ncimino, not per-path PRs). # -# PAT STEWARDSHIP: Post-2026-05-15 rotation lead is assigned from the handoff list -# above. See `.github/workflows/README.md` → "Transition Checklist 2026-05-15". +# PAT STEWARDSHIP: Primary rotation lead is @ncimino. +# See `.github/workflows/README.md` §10 Transition Checklist. +# +# Previous primary steward: @romandidomizio (left 2026-05-15) # # Extended operational details (legal names, tenure, recovery-credential custody, # on-call escalation) live in internal onboarding / security docs, not in this # public file. # ────────────────────────────────────────────────────────────────────── -# Global fallback — every file requires 2 reviewers by default -* @ncimino @romandidomizio +# Global fallback — every file reviewed by @ncimino +* @ncimino # Workflow & CI — infrastructure stewardship -/.github/ @ncimino @romandidomizio -/.github/workflows/ @ncimino @romandidomizio +/.github/ @ncimino +/.github/workflows/ @ncimino # Documentation & governance -/docs/ @ncimino @romandidomizio -/CHANGELOG.md @ncimino @romandidomizio -/README.md @ncimino @romandidomizio +/docs/ @ncimino +/CHANGELOG.md @ncimino +/README.md @ncimino -# Application-specific paths — placeholders for post-2026-05-15 handoff -# TODO(2026-05-15): Replace @romandidomizio with the appropriate specialist -# per Roman+Nik handoff decisions: -# @iamwaseem18 (Mohammed — IaC / Infisical) -# @mshahid538 (Shahid — Docker / DigitalOcean) -# @dhruvmalik007 (Dhruv — Agentic AI) -/anythingllm/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/wordpress/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/wordpress-dev/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/matomo/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/n8n/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/vaultwarden/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/nextcloud/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/fedarch/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/k8s/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/scripts/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/llm-d/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/braintrust-proxy/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 -/twilio-bridge/ @ncimino @romandidomizio # TODO(2026-05-15): assign @iamwaseem18 / @mshahid538 / @dhruvmalik007 +# Application-specific paths +/anythingllm/ @ncimino +/wordpress/ @ncimino +/wordpress-dev/ @ncimino +/matomo/ @ncimino +/n8n/ @ncimino +/vaultwarden/ @ncimino +/nextcloud/ @ncimino +/fedarch/ @ncimino +/k8s/ @ncimino +/scripts/ @ncimino +/llm-d/ @ncimino +/braintrust-proxy/ @ncimino +/twilio-bridge/ @ncimino diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 59e696a..47f85c2 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -584,11 +584,11 @@ Consolidated reference for the most common failure signatures across all workflo | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | | No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | -| No Copilot review on first commit of auto-created PR | **Expected behavior.** The commit was pushed *before* the PR existed. Copilot evaluates auto-review at PR-creation time, not push time. The `review_on_push: true` setting means "re-review on push for PRs that had Copilot auto-requested at creation", not "request Copilot on every push for every PR". | Make any follow-up push to the same branch (trivial whitespace fix, comment addition, etc.). Copilot will review the new push. All subsequent pushes on the same open PR are reviewed automatically. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | +| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results) and [ADR-004 § Forward-looking posture](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | -| Merge to `main` blocked with "requires 2 approvals" | Normal — `main` ruleset requires 2 human reviewers | Request additional reviewer per CODEOWNERS | +| Merge to `main` blocked with "requires 1 approval" | Normal — `main` ruleset requires 1 human reviewer | Request review from `@ncimino` per [CODEOWNERS](../CODEOWNERS) | | Merge blocked with "requires signed commits" | One or more commits in the PR are unsigned | Configure commit signing per [CONTRIBUTING.md §3](../../CONTRIBUTING.md#3-commit-signing-required); adding a new signed commit does **not** fix earlier unsigned commits. Because retroactive signing would rewrite history and `non-fast-forward` is blocked, recreate the branch/PR with all commits signed, or otherwise ensure every commit in the PR is signed. | | **Infisical sync** | | | | GitHub Secret `WEOWN_BOT_PAT` drifts from Infisical stored value | Infisical sync integration deleted / paused OR manual update in GitHub bypassed Infisical | See [ADR-002](../ADR-002-infisical-github-sync.md) §4 + [§6 recovery](#6-pat-rotation-procedure) | @@ -603,7 +603,7 @@ Consolidated reference for the most common failure signatures across all workflo - `.github/ADR-002-infisical-github-sync.md` — Why Infisical primary via GitHub Sync - `.github/SECURITY_ASSESSMENT.md` — Threat model, risk register, mitigations - `.github/INCIDENT_RESPONSE.md` — Incident scenarios, RTO/RPO, runbooks -- `.github/CODEOWNERS` — Review assignment + post-2026-05-15 handoff TODOs +- `.github/CODEOWNERS` — Review assignment (`@ncimino` universal reviewer; `@iamwaseem18`/`@mshahid538` assignable at `@ncimino`'s discretion) - `.github/CI_CD_WORKFLOWS.md` — Broader CI/CD strategy (validation workflows, not auto-PR) - `docs/COMPLIANCE_ROADMAP.md` — Multi-phase compliance strategy (NIST/CIS/CSA/ISO/SOC 2/ISO 42001) - `/CHANGELOG.md` — Repository-level change history diff --git a/.github/workflows/auto-pr-to-main.yml b/.github/workflows/auto-pr-to-main.yml index e193396..5f07b22 100644 --- a/.github/workflows/auto-pr-to-main.yml +++ b/.github/workflows/auto-pr-to-main.yml @@ -4,10 +4,14 @@ name: Auto-Create PR to Main # GitHub Copilot code review is auto-triggered (Copilot only reviews PRs # authored by human-type accounts, not GitHub Apps). # -# NOTE: Copilot evaluates auto-review eligibility at PR-CREATION time, not push -# time. The first commit on a new branch (pushed before the PR exists) won't -# trigger Copilot review. Any follow-up push to the SAME open PR does trigger -# review. See ADR-004 § Empirical Validation Results for the full analysis. +# NOTE: For auto-created PRs (this workflow's pattern), commits are pushed to +# the branch BEFORE the PR is created. Because there is no new push delta at +# PR-creation time, Copilot's `review_on_push: true` does not fire on the +# initial PR. Any follow-up push to the SAME open PR triggers Copilot review +# automatically. This is expected and documented — see ADR-004 +# § Empirical Validation Results + Forward-looking posture for full analysis. +# For manually-created PRs where the PR is opened before commits are pushed, +# Copilot fires at PR-creation time as described in ADR-004. # # See: # - .github/workflows/README.md (authoritative ops reference) @@ -384,8 +388,7 @@ jobs: echo "" echo "**🔍 Copilot AI Review**: Automatically triggered because PR is authored by \`weown-bot\` (human-type service account)." echo "" - echo "**👥 Required Reviewers**: 2 human approvals enforced by branch protection. \`@ncimino\` + \`@romandidomizio\` requested automatically." - echo "" + echo "**👥 Required Reviewers**: 1 human approval enforced by branch protection. `@ncimino` requested automatically." echo "" echo "**📚 Review Guidelines**: [\`.github/copilot-instructions.md\`](${BLOB_BASE}/.github/copilot-instructions.md) (phase-aware compliance directives)" echo "" @@ -416,8 +419,7 @@ jobs: echo "PR #$existing_pr already exists on $BRANCH_NAME; updating body + reviewers." gh pr edit "$existing_pr" --body-file "$PR_BODY" \ || echo "::warning::Failed to update body on PR #$existing_pr." - # TODO(2026-05-15): Update reviewer list per CODEOWNERS handoff. - gh pr edit "$existing_pr" --add-reviewer ncimino,romandidomizio \ + gh pr edit "$existing_pr" --add-reviewer ncimino \ || echo "::warning::Failed to (re)assign reviewers on PR #$existing_pr (they may already be assigned)." echo "pr_number=$existing_pr" >> "$GITHUB_OUTPUT" echo "Updated PR #$existing_pr" @@ -430,8 +432,7 @@ jobs: pr_number=$(echo "$pr_url" | grep -oE '[0-9]+$') echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" echo "Created PR #$pr_number: $pr_url" - # TODO(2026-05-15): Update reviewer list per CODEOWNERS handoff. - gh pr edit "$pr_number" --add-reviewer ncimino,romandidomizio \ + gh pr edit "$pr_number" --add-reviewer ncimino \ || echo "::warning::Failed to auto-assign reviewers on PR #$pr_number; they can be added manually." fi diff --git a/CHANGELOG.md b/CHANGELOG.md index b124688..6eba90d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,10 +29,24 @@ Changes in this section will be promoted to a dated release entry on merge to `m ### Changed +- **`.github/CODEOWNERS` contributor handoff (2026-05-13)** — `@romandidomizio` removed from all path assignments (left 2026-05-15); `@dhruvmalik007` references removed (no longer with team). `@ncimino` is now the sole assigned reviewer on every path. `@iamwaseem18` (Mohammed) and `@mshahid538` (Shahid) noted as available at `@ncimino`'s discretion. All `TODO(2026-05-15)` handoff comments removed. Header updated to reflect 1-approval requirement and Roman as previous steward. +- **Required reviewers changed from 2 → 1 (2026-05-13)** — updated across `.github/CODEOWNERS` header, `CONTRIBUTING.md` §6.4 merge requirements, `README.md` Code Review Model, `.github/ADR-004-copilot-auto-review-ruleset.md` CIS Controls v8 18.3 compliance row + Layer 1 pruning criteria reviewer requirement, `.github/workflows/auto-pr-to-main.yml` PR body "Required Reviewers" text, `.github/workflows/README.md` §11 troubleshooting row. +- **`CONTRIBUTING.md` §1 prerequisites + §10 Questions updated (2026-05-13)** — removed `@romandidomizio` and `@dhruvmalik007` references; org-access contact updated to `@ncimino`; process/governance contact updated to `@ncimino` + CODEOWNERS link. +- **`.github/workflows/auto-pr-to-main.yml` reviewer assignment updated (2026-05-13)** — `--add-reviewer ncimino,romandidomizio` → `--add-reviewer ncimino` (both existing PR update + new PR create paths). `TODO(2026-05-15)` comments removed. - **`.github/ADR-004-copilot-auto-review-ruleset.md` ruleset structure amended (2026-05-01)** — `deletion` rule removed from `~ALL` branches in both Layer 1 (repo-level) and Layer 2 (enterprise-level) rulesets. Retained 2 rules: `non_fast_forward` + `copilot_code_review`. Rationale: post-merge branch deletion was permanently blocked by `deletion` on `~ALL` with empty `bypass_actors` — no one (including org admins) could delete any branch. `main` branch deletion protection remains via ADR-003 rule #11. Decision Log appended. Compliance mappings adjusted (SOC 2 CC6.1, ISO 27001 A.8.13). - **`CONTRIBUTING.md` §8 troubleshooting expanded (2026-05-01)** — added "My PR is blocked — commits are unsigned" scenario. Removed Option 1 (rebase + force-push) because `non_fast_forward` ruleset on `~ALL` branches blocks force-push. Retained Option 2: close PR, cherry-pick to fresh branch with `git cherry-pick -S`, open new PR. Also added "Copilot review didn't start on my auto-created PR" troubleshooting entry documenting the PR-creation-time caching behavior: Copilot evaluates auto-review at PR-creation time, not push time, so the first commit on a new branch won't trigger review until a subsequent push. All subsequent pushes on an open PR are reviewed automatically. Replaces the older single "I need to sign commits I already pushed unsigned" subsection. - **Repo Settings → General → Pull Requests (2026-05-01)**: enabled **"Automatically delete head branches"** (merged branches auto-delete, replacing the retired `deletion` rule on `~ALL`); enabled **"Always suggest updating pull request branches"** (keeps PRs current with `main` for cleaner merges). +### Fixed + +- **Copilot PR #17 comment fixes (2026-05-13)** — addressed all open Copilot review comments on branch `feature/roman-ruleset-cleanup-and-docs`: + - **`CONTRIBUTING.md` §8 Cause B force-push** (comments 3175275840 + 3175470086): removed `git push --force-with-lease` step; replaced with note that force-push is blocked by `non_fast_forward` and directs users to the close+recreate path already documented in the same §8 section. + - **`ADR-004` title** — removed "Deletion Protection" from title since the `deletion` rule was removed from `~ALL`; title now accurately reflects the 2 active rules. + - **`ADR-004` Verification Procedure expected output** (comment 3175470115): updated Layer 1 expected `rules` array from `["deletion", "non_fast_forward", "copilot_code_review"]` → `["non_fast_forward", "copilot_code_review"]`; updated Layer 2 expected count from "identical 3 rules" → "identical 2 rules". + - **`ADR-004` Decision Log** (comment 3175275917): removed reference to gitignored `PR7_HANDOFF_CHECKLIST.md` in the round-7 close-out entry. + - **`auto-pr-to-main.yml` NOTE** (comment 3175470133): rewrote NOTE to clarify the push-before-PR pattern specific to this workflow — commits are pushed before the PR exists, so `review_on_push: true` has no new push delta at PR-creation time; first Copilot review fires on the next push to the open PR. Reconciled with ADR-004 § Empirical Validation Results. + - **`workflows/README.md` §11 Copilot row** (comment 3175470144): updated "No Copilot review on first commit of auto-created PR" row to clarify this is specific to the `auto-pr-to-main.yml` push-before-PR pattern (not all PRs), and that manually-created PRs do get Copilot at PR-creation time. + --- ## [v3.3.5.1] — 2026-04-27 to 2026-04-28 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94138f7..db58267 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ Welcome. This guide covers everything you need to contribute to the WeOwn AI infrastructure repository. **Version**: v3.3.5.1 (#WeOwnVer — see [`docs/VERSIONING_WEOWNVER.md`](docs/VERSIONING_WEOWNVER.md)) -**Last updated**: 2026-04-28 (R12 §4 attribution-fallback fix + R13 header date sync + R19 §4 `Contributors on this branch:` label canonicalization) +**Last updated**: 2026-05-13 (R12 §4 attribution-fallback fix + R13 header date sync + R19 §4 `Contributors on this branch:` label canonicalization + Copilot R1 §8 force-push fix + contributor/reviewer updates) --- @@ -25,7 +25,7 @@ Welcome. This guide covers everything you need to contribute to the WeOwn AI inf Before your first contribution, ensure you have: -- **GitHub account** added to the `WeOwnNetwork` organization (ask `@romandidomizio` or — post-2026-05-15 — one of Mohammed / Shahid / Dhruv) +- **GitHub account** added to the `WeOwnNetwork` organization (ask `@ncimino`) - **2FA enabled** on your GitHub account (required by org policy) - **Git 2.34+** installed locally (`git --version` to check — earlier versions don't support SSH signing) - **SSH key** for GitHub authentication (the same key you use for `git push` will be reused for signing) @@ -454,7 +454,7 @@ Before merging to `main`, your PR must satisfy **all** of: - ✅ All commits signed (green "Verified" badge on every commit) — [§3](#3-commit-signing-required) - ✅ `branch-name-check.yml` status check passing - ✅ Copilot AI review completed -- ✅ 2 human approvals (enforced by branch protection + CODEOWNERS) +- ✅ 1 human approval (enforced by branch protection + CODEOWNERS) - ✅ All conversation threads resolved - ✅ Up-to-date with `main` (rebase if needed) @@ -605,8 +605,9 @@ Fix (pick one): # Amend the last commit to pick up the new committer email + re-sign git commit --amend --no-edit --reset-author - # If you already pushed, force-push safely: - git push --force-with-lease origin + # If you have NOT pushed yet, you're done — push normally. + # If you already pushed, force-push is BLOCKED by the non_fast_forward ruleset. + # Use the close+recreate path in "My PR is blocked — commits are unsigned" below. ``` ### "My PR is blocked — commits are unsigned" @@ -686,6 +687,6 @@ Host * - **Technical questions about the repo**: open an issue with label `question` - **Security concerns**: see [`.github/SECURITY_ASSESSMENT.md` §Incident Response](.github/SECURITY_ASSESSMENT.md) — do NOT open a public issue for security vulnerabilities -- **Process / governance**: contact the current primary owner (`@romandidomizio` until 2026-05-15, then one of Mohammed / Shahid / Dhruv per [`.github/CODEOWNERS`](.github/CODEOWNERS)) +- **Process / governance**: contact `@ncimino` (Nik) or reach out via [`.github/CODEOWNERS`](.github/CODEOWNERS) Thanks for contributing. 🚀 diff --git a/README.md b/README.md index b24eae6..dd73e10 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ WeOwn AI infrastructure follows a layered, phased compliance program progressing Every PR to `main` requires: - ✅ GitHub Copilot AI code review (automatic on PRs from `weown-bot`) -- ✅ 2 human approvals (enforced by branch protection + CODEOWNERS) +- ✅ 1 human approval (enforced by branch protection + CODEOWNERS) - ✅ Review from Code Owners per [`.github/CODEOWNERS`](.github/CODEOWNERS) - ✅ All conversations resolved before merge - ✅ `branch-name-check.yml` status check passes From 4666bb5595f2b66d74a01295379cd15ee56ea608 Mon Sep 17 00:00:00 2001 From: Roman Di Domizio <123122767+romandidomizio@users.noreply.github.com> Date: Wed, 13 May 2026 12:06:51 -0600 Subject: [PATCH 12/17] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 47f85c2..12c6d3c 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -584,7 +584,7 @@ Consolidated reference for the most common failure signatures across all workflo | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | | No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | -| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results) and [ADR-004 § Forward-looking posture](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | +| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results) and [ADR-004](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | From 5961680ad0bd2ea0d4a14e5ea9d134d930f9b400 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 18:17:11 +0000 Subject: [PATCH 13/17] docs: align review guidance, ruleset wording, and version metadata Agent-Logs-Url: https://github.com/WeOwnNetwork/ai/sessions/981d5570-7e8e-4abf-a496-c0ce9608b2d8 Co-authored-by: romandidomizio <123122767+romandidomizio@users.noreply.github.com> --- .github/ADR-001-service-account-pat.md | 2 +- .github/ADR-004-copilot-auto-review-ruleset.md | 2 +- .github/CODEOWNERS | 2 ++ .github/workflows/README.md | 2 +- .github/workflows/auto-pr-to-main.yml | 2 +- CONTRIBUTING.md | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/ADR-001-service-account-pat.md b/.github/ADR-001-service-account-pat.md index 0870229..9dfed1e 100644 --- a/.github/ADR-001-service-account-pat.md +++ b/.github/ADR-001-service-account-pat.md @@ -1,7 +1,7 @@ # ADR-001: Ecosystem-Wide GitHub Service Account (`weown-bot`) + Fine-Grained PATs **Status**: Accepted -**Version**: v3.3.4.1 (#WeOwnVer) +**Version**: v3.4.2.1 (#WeOwnVer) **Date**: 2026-04-23 **Deciders**: `@romandidomizio` (original author, left 2026-05-15) — `@ncimino` (current maintainer) **Supersedes**: None diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index c852fc0..358d4b7 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -113,7 +113,7 @@ Audit evidence cross-reference: 1. **5+ consecutive bot-authored PRs** auto-trigger Copilot review via Layer 2 alone (i.e., temporarily disable Layer 1 for testing, observe Copilot fires on every push to every PR, re-enable Layer 1, then schedule pruning). 2. **No enterprise-level migration is planned** in the next 6 months (org restructure, billing change, SKU change). -3. **Layer 2 deletion + non_fast_forward enforcement is verified** via a deliberate test (e.g., attempt force-push from a fresh fork; confirm rejection with the expected enterprise-ruleset error message). +3. **Layer 2 non_fast_forward + copilot_code_review enforcement is verified** via deliberate tests (e.g., attempt force-push from a fresh fork and confirm rejection for non-fast-forward; verify Copilot auto-review request appears on a new bot-authored PR). 4. **Reviewer (`@ncimino` minimum) signs off** on a "drop Layer 1" PR with explicit ADR-004 update marking Layer 1 as "Removed YYYY-MM-DD". If any criterion fails, KEEP Layer 1. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ffa1215..f1c0dda 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,8 @@ # ────────────────────────────────────────────────────────────────────── # PURPOSE: Default reviewers for all paths; enforced by branch protection # requiring 1 approval on `main` + review from Code Owners. +# Temporary small-team posture: 1 required reviewer; additional +# second reviewers can be added by @ncimino at their discretion. # # PRIMARY REVIEWER (all paths): # @ncimino (universal reviewer) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 12c6d3c..448578c 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -30,7 +30,7 @@ | Workflow | Trigger | Purpose | Owner | |---|---|---|---| -| `auto-pr-to-main.yml` | push to `feature/*`, `fix/*`, `docs/*`, `hotfix/*` | Creates/updates PR to `main` authored by `weown-bot`; triggers Copilot review; auto-assigns 2 human reviewers | Infra team | +| `auto-pr-to-main.yml` | push to `feature/*`, `fix/*`, `docs/*`, `hotfix/*` | Creates/updates PR to `main` authored by `weown-bot`; triggers Copilot review; auto-assigns 1 human reviewer (`@ncimino`) with optional second reviewers at `@ncimino`'s discretion | Infra team | | `branch-name-check.yml` | push (any branch except `main`) | Validates branch follows `/-` convention; blocks merge if non-conforming | Infra team | | `pat-health-check.yml` | schedule: weekly (Mondays 09:00 UTC) + manual dispatch | Checks `WEOWN_BOT_PAT` validity + days-to-expiration; opens issue at 14 days; hard-fails at 3 days | Infra team | diff --git a/.github/workflows/auto-pr-to-main.yml b/.github/workflows/auto-pr-to-main.yml index 5f07b22..db338ec 100644 --- a/.github/workflows/auto-pr-to-main.yml +++ b/.github/workflows/auto-pr-to-main.yml @@ -386,7 +386,7 @@ jobs: echo "" echo "---" echo "" - echo "**🔍 Copilot AI Review**: Automatically triggered because PR is authored by \`weown-bot\` (human-type service account)." + echo "**🔍 Copilot AI Review**: For PRs auto-created by this workflow, Copilot review is triggered on the first follow-up push to the same open PR (\`review_on_push: true\`). For manually-created PRs, Copilot can be requested at PR creation." echo "" echo "**👥 Required Reviewers**: 1 human approval enforced by branch protection. `@ncimino` requested automatically." echo "" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db58267..ddec1d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Welcome. This guide covers everything you need to contribute to the WeOwn AI infrastructure repository. -**Version**: v3.3.5.1 (#WeOwnVer — see [`docs/VERSIONING_WEOWNVER.md`](docs/VERSIONING_WEOWNVER.md)) +**Version**: v3.4.2.1 (#WeOwnVer — see [`docs/VERSIONING_WEOWNVER.md`](docs/VERSIONING_WEOWNVER.md)) **Last updated**: 2026-05-13 (R12 §4 attribution-fallback fix + R13 header date sync + R19 §4 `Contributors on this branch:` label canonicalization + Copilot R1 §8 force-push fix + contributor/reviewer updates) --- From 3429a1b4cbef3bcef01679eaef49352e88ffdef3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 18:18:08 +0000 Subject: [PATCH 14/17] docs: clarify copilot trigger wording and layer-2 pruning criterion Agent-Logs-Url: https://github.com/WeOwnNetwork/ai/sessions/981d5570-7e8e-4abf-a496-c0ce9608b2d8 Co-authored-by: romandidomizio <123122767+romandidomizio@users.noreply.github.com> --- .github/ADR-004-copilot-auto-review-ruleset.md | 2 +- .github/workflows/auto-pr-to-main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index 358d4b7..43c3c3c 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -113,7 +113,7 @@ Audit evidence cross-reference: 1. **5+ consecutive bot-authored PRs** auto-trigger Copilot review via Layer 2 alone (i.e., temporarily disable Layer 1 for testing, observe Copilot fires on every push to every PR, re-enable Layer 1, then schedule pruning). 2. **No enterprise-level migration is planned** in the next 6 months (org restructure, billing change, SKU change). -3. **Layer 2 non_fast_forward + copilot_code_review enforcement is verified** via deliberate tests (e.g., attempt force-push from a fresh fork and confirm rejection for non-fast-forward; verify Copilot auto-review request appears on a new bot-authored PR). +3. **Layer 2-alone enforcement is verified as sufficient** via deliberate tests (e.g., attempt force-push from a fresh fork and confirm rejection for non-fast-forward; verify Copilot auto-review request appears on a new bot-authored PR), demonstrating Layer 1 can be removed without losing either control. 4. **Reviewer (`@ncimino` minimum) signs off** on a "drop Layer 1" PR with explicit ADR-004 update marking Layer 1 as "Removed YYYY-MM-DD". If any criterion fails, KEEP Layer 1. diff --git a/.github/workflows/auto-pr-to-main.yml b/.github/workflows/auto-pr-to-main.yml index db338ec..a4aa8fc 100644 --- a/.github/workflows/auto-pr-to-main.yml +++ b/.github/workflows/auto-pr-to-main.yml @@ -386,7 +386,7 @@ jobs: echo "" echo "---" echo "" - echo "**🔍 Copilot AI Review**: For PRs auto-created by this workflow, Copilot review is triggered on the first follow-up push to the same open PR (\`review_on_push: true\`). For manually-created PRs, Copilot can be requested at PR creation." + echo "**🔍 Copilot AI Review**: Copilot is configured to auto-request review for bot-authored PRs. If an auto-created PR opens without an initial Copilot review, push a follow-up commit to the same open PR (\`review_on_push: true\`) to trigger review automatically." echo "" echo "**👥 Required Reviewers**: 1 human approval enforced by branch protection. `@ncimino` requested automatically." echo "" From cac46059841589a05550f140096b7d2f64b9fdd2 Mon Sep 17 00:00:00 2001 From: romandidomizio Date: Wed, 13 May 2026 12:36:54 -0600 Subject: [PATCH 15/17] docs(v3.4.2.1): fix remaining Copilot PR#17 comments (round-2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## workflows/README.md — stale "2 reviewer" refs (comment 3236477797) - §6 PAT rotation step 13: "approved by 2 humans" → "1 human" - §8.1 rule #1 table: "2 reviewers" → "1 reviewer" - §8.1 Interaction with workflows: "2 reviewers + Code Owners" → "1 reviewer" - §9 step 4: "requires 2 approvals" → "1 approval" - §10 item #7: "2 reviewers" → "1 reviewer" ## workflows/README.md §9 Reviewer Rotation — stale Roman/dhruvmalik007 text - Step 1: updated to reflect handoff complete 2026-05-15 (PR #17); @romandidomizio + @dhruvmalik007 removed; @ncimino sole reviewer - Step 2: removed @dhruvmalik007 from done-placeholder list - Step 3: --add-reviewer ncimino only (was ncimino,) ## workflows/README.md §10 Transition Checklist — stale @romandidomizio refs - Items 1, 4, 5: marked ✅ done with 2026-05-15 / PR #17 notes - Item 1 owner: @ncimino (was @romandidomizio + @ncimino) - Item 4 owner: @ncimino (was @romandidomizio + @ncimino) - Item 5 owner: @ncimino (was New rotation lead) - Items 2, 6 owner: @YonksTEAM (was @romandidomizio + @YonksTEAM) - Item 9 owner: @ncimino (was @romandidomizio) ## workflows/README.md §11 duplicate link (comment 3236447687) - Row "No Copilot review on first commit of auto-created PR": removed redundant second [ADR-004](...#empirical-validation-results) link; kept single [ADR-004 § Empirical Validation Results] link with anchor ## CODEOWNERS risk-acceptance note (comment 3236477912) - Added RISK ACCEPTANCE block explaining deliberate 2→1 reviewer posture change, rationale (team transition), and compensating controls (Copilot AI review + CodeQL); references ADR-003 rule #1 + PR #17 ## CHANGELOG.md - Added "Copilot PR #17 round-2 comment fixes" entry under ### Fixed - Renamed previous entry to "round-1" --- .github/CODEOWNERS | 10 ++++++++-- .github/workflows/README.md | 30 +++++++++++++++--------------- CHANGELOG.md | 9 ++++++++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f1c0dda..2f10412 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,8 +2,14 @@ # ────────────────────────────────────────────────────────────────────── # PURPOSE: Default reviewers for all paths; enforced by branch protection # requiring 1 approval on `main` + review from Code Owners. -# Temporary small-team posture: 1 required reviewer; additional -# second reviewers can be added by @ncimino at their discretion. +# +# RISK ACCEPTANCE (effective 2026-05-15, PR #17): +# Reduced from 2-reviewer to 1-reviewer posture. Previous model required +# @romandidomizio + @ncimino; @romandidomizio left 2026-05-15. With one +# active Code Owner, the 2-reviewer rule is no longer operable. This is +# a deliberate small-team posture change documented in ADR-003 rule #1 +# and CHANGELOG.md [Unreleased]. Copilot AI review + CodeQL remain as +# compensating controls. Revisit when team size allows a second Code Owner. # # PRIMARY REVIEWER (all paths): # @ncimino (universal reviewer) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 448578c..4411b1f 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -377,7 +377,7 @@ Reserve `weown-bot` for: 10. **Update** §2.4 Usage Table "Last Rotated" and "Expiration" columns in this file 11. **Close** the rotation reminder issue (if opened by `pat-health-check.yml`) 12. **Log** the rotation in `/CHANGELOG.md` under the `### Changed` section for that date -13. **Commit** the `/CHANGELOG.md` + this file updates via PR (will be auto-reviewed by Copilot, approved by 2 humans) +13. **Commit** the `/CHANGELOG.md` + this file updates via PR (will be auto-reviewed by Copilot, approved by 1 human) ### 6.1 Sync Options Configuration @@ -456,7 +456,7 @@ GitHub's own alert is often missed because it goes to an email box that may not | # | Rule | Compliance control | |---|---|---| -| 1 | **Require a pull request before merging** with **2 reviewers** | SOC 2 CC6.3; CIS 16.9; NIST PR.AC-4 | +| 1 | **Require a pull request before merging** with **1 reviewer** | SOC 2 CC6.3; CIS 16.9; NIST PR.AC-4 | | 2 | **Dismiss stale pull request approvals when new commits are pushed** | SOC 2 CC8.1 (change management integrity) | | 3 | **Require review from Code Owners** (enforces `.github/CODEOWNERS`) | SOC 2 CC6.3; ISO 27001 A.5.15 | | 4 | **Require approval of the most recent reviewable push** | Closes approve-then-sneak-bad-commit race condition | @@ -481,7 +481,7 @@ GitHub's own alert is often missed because it goes to an email box that may not **Interaction with workflows**: - `auto-pr-to-main.yml` runs `gh pr edit --add-reviewer` to *request* a specific reviewer — this is a suggestion, not enforcement. -- The ruleset's "2 reviewers + Code Owners review" is the *enforcement* layer. Both are needed: request for discoverability, ruleset for gating. +- The ruleset's "1 reviewer + Code Owners review" is the *enforcement* layer. Both are needed: request for discoverability, ruleset for gating. - `branch-name-check.yml` is the only workflow currently required as a status check. `pat-health-check.yml` runs on `schedule:` so it cannot be a PR-time required status check; it surfaces red-X independently in the Actions tab when the PAT is ≤3 days from expiration. ### 8.2 Branch Naming Enforcement @@ -521,14 +521,14 @@ Together these two layers ensure: Triggered when a CODEOWNERS path's primary reviewer changes (e.g., Roman → Mohammed for `/anythingllm/`). -1. **Update CODEOWNERS**: replace `@romandidomizio` with the new specialist on the affected paths (per-path assignment — pending decision by `@ncimino` + `@romandidomizio` before 2026-05-15) -2. ~~Replace `@-TODO` placeholders with real GitHub usernames~~ ✅ **done 2026-04-23** (v3.3.4.2): `@iamwaseem18`, `@mshahid538`, `@dhruvmalik007`. `@YonksTEAM` added to CODEOWNERS header as executive stakeholder (not a path reviewer — avoids notification noise). +1. **Update CODEOWNERS**: assign the new specialist to the affected paths — `@ncimino` remains as universal reviewer on all paths. ✅ **done 2026-05-15** (PR #17): `@romandidomizio` and `@dhruvmalik007` removed; `@ncimino` is now sole assigned reviewer; `@iamwaseem18`/`@mshahid538` assignable at `@ncimino`'s discretion. +2. ~~Replace `@-TODO` placeholders with real GitHub usernames~~ ✅ **done 2026-04-23** (v3.3.4.2): `@iamwaseem18`, `@mshahid538`. `@YonksTEAM` added to CODEOWNERS header as executive stakeholder (not a path reviewer — avoids notification noise). 3. **Update workflow reviewer list** in `auto-pr-to-main.yml`: ```bash - gh pr edit "$pr_number" --add-reviewer ncimino, + gh pr edit "$pr_number" --add-reviewer ncimino ``` - `@ncimino` always stays in the list -4. **Verify branch protection** requires 2 approvals + Code Owners review (§8) +4. **Verify branch protection** requires 1 approval + Code Owners review (§8) 5. **Document** the change in `/CHANGELOG.md` --- @@ -541,15 +541,15 @@ Triggered when a CODEOWNERS path's primary reviewer changes (e.g., Roman → Moh | # | Item | Action | Owner | |---|---|---|---| -| 1 | **PAT stewardship** | Assign ONE of Mohammed/Shahid/Dhruv as the primary PAT rotation lead | `@romandidomizio` + `@ncimino` | -| 2 | **`weown-bot` account access** | Transfer 2FA administration per internal runbook to enterprise admin + rotation lead | `@romandidomizio` + `@YonksTEAM` | +| 1 | **PAT stewardship** | ✅ `@ncimino` is primary PAT rotation lead as of 2026-05-15. `@iamwaseem18`/`@mshahid538` available as secondary at `@ncimino`'s discretion. | `@ncimino` | +| 2 | **`weown-bot` account access** | Transfer 2FA administration per internal runbook to enterprise admin + rotation lead | `@YonksTEAM` | | 3 | **Bot email** | Update the service account's email to the permanent bot email (details tracked per internal runbook) | `@YonksTEAM` | -| 4 | **CODEOWNERS update** | Replace `@romandidomizio` with per-path specialists (per-path decision pending). Placeholder handles ✅ replaced 2026-04-23 with `@iamwaseem18` / `@mshahid538` / `@dhruvmalik007`. | `@romandidomizio` + `@ncimino` | -| 5 | **Workflow reviewer update** | Update `gh pr edit --add-reviewer` line in `auto-pr-to-main.yml` to reflect new specialist per the paths being changed | New rotation lead | -| 6 | **Infisical project access** | Transfer admin role on project `weown-bot GitHub PATs` to rotation lead + `@YonksTEAM` | `@romandidomizio` + `@YonksTEAM` | -| 7 | **Branch protection check** | Verify `main` branch protection still enforces 2 reviewers + review from Code Owners | `@ncimino` | +| 4 | **CODEOWNERS update** | ✅ done 2026-05-15 (PR #17): `@romandidomizio` removed from all paths; `@ncimino` is sole assigned reviewer. | `@ncimino` | +| 5 | **Workflow reviewer update** | ✅ done 2026-05-15 (PR #17): `--add-reviewer ncimino` only (was `ncimino,romandidomizio`). | `@ncimino` | +| 6 | **Infisical project access** | Transfer admin role on project `weown-bot GitHub PATs` to rotation lead + `@YonksTEAM` | `@YonksTEAM` | +| 7 | **Branch protection check** | Verify `main` branch protection enforces 1 reviewer + Code Owners review | `@ncimino` | | 8 | **Alert routing** | Update GitHub native email recipient for `weown-bot` to rotation lead's email | New rotation lead | -| 9 | **Knowledge transfer session** | Walk rotation lead through the full rotation procedure (§6) live | `@romandidomizio` | +| 9 | **Knowledge transfer session** | Walk rotation lead through the full rotation procedure (§6) live | `@ncimino` | | 10 | **Documentation review** | Rotation lead reads this README, ADR-001, ADR-002, SECURITY_ASSESSMENT, INCIDENT_RESPONSE, COMPLIANCE_ROADMAP end-to-end | Rotation lead | ### Automated Safety Nets (in place regardless of handoff) @@ -584,7 +584,7 @@ Consolidated reference for the most common failure signatures across all workflo | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | | No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | -| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results) and [ADR-004](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | +| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eba90d..1525031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,14 @@ Changes in this section will be promoted to a dated release entry on merge to `m ### Fixed -- **Copilot PR #17 comment fixes (2026-05-13)** — addressed all open Copilot review comments on branch `feature/roman-ruleset-cleanup-and-docs`: +- **Copilot PR #17 round-2 comment fixes (2026-05-13)** — addressed remaining Copilot comments from the 2026-05-13 push: + - **`workflows/README.md` stale "2 reviewer" references** (comment 3236477797): updated 5 remaining "2 reviewers/approvals" → "1 reviewer/approval" at §6 PAT rotation step 13, §8.1 rule #1 table row, §8.1 "Interaction with workflows", §9 step 4, §10 item #7. + - **`workflows/README.md` §9 Reviewer Rotation**: updated step 1 to reflect handoff complete (2026-05-15, PR #17); removed `@dhruvmalik007` from the done-placeholder list; `--add-reviewer ncimino` only. + - **`workflows/README.md` §10 Transition Checklist**: marked items 1 (PAT stewardship), 4 (CODEOWNERS), 5 (workflow reviewer) as ✅ done; updated owners to reflect `@ncimino` and `@YonksTEAM` post-transition; removed `@romandidomizio` from action-owner column. + - **`workflows/README.md` §11 line 587 duplicate link** (comment 3236447687): removed redundant second `[ADR-004]` link in the "No Copilot review on first commit" row; kept single `[ADR-004 § Empirical Validation Results]` link with correct anchor. + - **`CODEOWNERS` risk-acceptance note** (comment 3236477912): added explicit `RISK ACCEPTANCE` block in the CODEOWNERS header documenting the deliberate 2-reviewer → 1-reviewer posture change, compensating controls (Copilot AI review + CodeQL), and trigger for revisiting. + +- **Copilot PR #17 round-1 comment fixes (2026-05-13)** — addressed all open Copilot review comments on branch `feature/roman-ruleset-cleanup-and-docs`: - **`CONTRIBUTING.md` §8 Cause B force-push** (comments 3175275840 + 3175470086): removed `git push --force-with-lease` step; replaced with note that force-push is blocked by `non_fast_forward` and directs users to the close+recreate path already documented in the same §8 section. - **`ADR-004` title** — removed "Deletion Protection" from title since the `deletion` rule was removed from `~ALL`; title now accurately reflects the 2 active rules. - **`ADR-004` Verification Procedure expected output** (comment 3175470115): updated Layer 1 expected `rules` array from `["deletion", "non_fast_forward", "copilot_code_review"]` → `["non_fast_forward", "copilot_code_review"]`; updated Layer 2 expected count from "identical 3 rules" → "identical 2 rules". From 0318268eeb1b6a4f15714e4f0159ecb1793f0385 Mon Sep 17 00:00:00 2001 From: Nik Date: Wed, 13 May 2026 23:35:57 -0600 Subject: [PATCH 16/17] docs: apply markdownlint autofixes for PR #15 CI - ADR-004: add blank line before bulleted list (MD032) - workflows/README.md: switch *emphasis* to _emphasis_ for style consistency (MD049) Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/ADR-004-copilot-auto-review-ruleset.md | 1 + .github/workflows/README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/ADR-004-copilot-auto-review-ruleset.md b/.github/ADR-004-copilot-auto-review-ruleset.md index 9292d49..ae64434 100644 --- a/.github/ADR-004-copilot-auto-review-ruleset.md +++ b/.github/ADR-004-copilot-auto-review-ruleset.md @@ -24,6 +24,7 @@ ADR-003 covers the strict `main`-only ruleset. But there are repo-wide invariant 2. **`copilot_code_review` on `~ALL` branches** — every **newly-created** PR (regardless of base branch) gets Copilot AI review automatically **after ruleset enablement**. This is the WeOwn baseline AI safety pattern (one of two enforcement mechanisms; the other is `weown-bot` being a "human-type" account so legacy auto-trigger fires). **Note**: Copilot evaluates auto-review eligibility at PR-creation time, so PRs that already existed before the ruleset was applied (e.g., PR #13) do **not** retroactively gain auto-review — they must be triggered manually for the duration of their open lifecycle. See § Empirical Validation Results below for the controlled experiment confirming this PR-creation-time caching behavior. These can't go in ADR-003 because they target `~ALL`, not `~DEFAULT_BRANCH`. They warrant a separate ADR because: + - Their compliance mappings differ (focus on data integrity + AI safety + history immutability, not change-management gating) - They have a defense-in-depth pairing with an **enterprise-level** ruleset that mirrors the same rules at a broader scope - Their pruning criteria (when to retire either layer) are distinct from ADR-003's review cadence diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a57c4e0..f6f0117 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -489,8 +489,8 @@ GitHub's own alert is often missed because it goes to an email box that may not **Interaction with workflows**: -- `auto-pr-to-main.yml` runs `gh pr edit --add-reviewer` to *request* a specific reviewer — this is a suggestion, not enforcement. -- The ruleset's "1 reviewer + Code Owners review" is the *enforcement* layer. Both are needed: request for discoverability, ruleset for gating. +- `auto-pr-to-main.yml` runs `gh pr edit --add-reviewer` to _request_ a specific reviewer — this is a suggestion, not enforcement. +- The ruleset's "1 reviewer + Code Owners review" is the _enforcement_ layer. Both are needed: request for discoverability, ruleset for gating. - `branch-name-check.yml` is the only workflow currently required as a status check. `pat-health-check.yml` runs on `schedule:` so it cannot be a PR-time required status check; it surfaces red-X independently in the Actions tab when the PAT is ≤3 days from expiration. ### 8.2 Branch Naming Enforcement @@ -596,7 +596,7 @@ Consolidated reference for the most common failure signatures across all workflo | "Branch Name Check" shows red ✗ on PR | Branch doesn't match regex or uses `` <2 chars / `` <3 chars | Rename the branch locally; force-push is BLOCKED by `non_fast_forward` ruleset — open a NEW branch with a compliant name instead | | **Copilot auto-review** | | | | No Copilot review after push to existing PR | PR was created before Copilot Business entitlement was provisioned (2026-04-27). Auto-trigger is PR-creation-time. | Manual trigger via `gh api --method POST /repos/WeOwnNetwork/ai/pulls//requested_reviewers -f reviewers[]=copilot-pull-request-reviewer` (canonical GitHub Copilot reviewer login — same value referenced by [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) or the "Request review" button in GitHub UI. For the long-term fix (new PRs auto-trigger correctly) see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md). | -| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch *before* creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made *while the PR is open*. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | +| No Copilot review on first commit of auto-created PR | **Expected behavior specific to `auto-pr-to-main.yml`.** The workflow pushes commits to the branch _before_ creating the PR, so there is no new push delta when the PR is opened — Copilot's `review_on_push: true` only fires on pushes made _while the PR is open_. For manually-created PRs (PR opened before commits are pushed), Copilot fires at PR-creation time. | Make any follow-up push to the same branch. Copilot will review the new push automatically. All subsequent pushes on an open PR are reviewed. See [ADR-004 § Empirical Validation Results](../ADR-004-copilot-auto-review-ruleset.md#empirical-validation-results). | | No Copilot review on brand-new PR (post-2026-04-27) | Either (a) `weown-bot` Copilot Business seat revoked, or (b) rulesets misconfigured | Verify via `gh api /repos/WeOwnNetwork/ai/rulesets/12131972` → rules include `copilot_code_review`; verify enterprise-level ruleset still active in Enterprise Settings | | **Branch protection / rulesets** | | | | `Push rejected: non-fast-forward` on feature branch | Normal — force-push blocked on `~ALL` branches by Layer 1 + Layer 2 rulesets (see [ADR-004](../ADR-004-copilot-auto-review-ruleset.md)) | Don't force-push. Open a new branch or use merge instead of rebase. | From 82490724b44b45d8ae3953be74d1b037e073e05e Mon Sep 17 00:00:00 2001 From: Nik Date: Wed, 13 May 2026 23:49:36 -0600 Subject: [PATCH 17/17] fix(wordpress/helm): address PR #15 review items 10-12 - ingress.yaml: parameterize spec.tls[0].secretName from .Values.ingress.tls[0].secretName so per-site overrides (burnedout-tls, ptoken-tls) actually take effect. Falls back to "wordpress-tls" when .Values.ingress.tls is a map or unset (preserves default and TLS hardening map). - php-config-configmap.yaml: gate "auto_prepend_file = wordfence-waf.php" behind .Values.wordpress.wordfence.enabled (default false) to avoid PHP warnings when the plugin is not installed. - ingress.yaml: gate the file-blocking server-snippet annotation behind .Values.ingress.serverSnippet.enabled (default true) so the chart can deploy on hardened controllers that set allow-snippet-annotations: false. - values.yaml: add wordpress.wordfence.enabled and ingress.serverSnippet.enabled. - Chart.yaml: bump 3.2.7 -> 3.3.0 (SemVer + WeOwnVer valid). - wordpress/CHANGELOG.md: document 3.3.0. Verified: helm template renders correctly with default, values-burnedout, and values-ptoken; helm lint clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- wordpress/CHANGELOG.md | 15 +++++++++++++++ wordpress/helm/Chart.yaml | 2 +- wordpress/helm/templates/ingress.yaml | 8 +++++++- .../helm/templates/php-config-configmap.yaml | 2 ++ wordpress/helm/values.yaml | 13 +++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/wordpress/CHANGELOG.md b/wordpress/CHANGELOG.md index a16672c..27da7b4 100644 --- a/wordpress/CHANGELOG.md +++ b/wordpress/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this WordPress deployment will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.0] - 2026-05-13 + +### 🛠️ **Template Hardening Fixes (PR #15 Review)** + +#### **Fixed** + +- **`templates/ingress.yaml`**: TLS `secretName` was hardcoded to `wordpress-tls`. Per-site overrides in `values-burnedout.yaml` (`burnedout-tls`) and `values-ptoken.yaml` (`ptoken-tls`) were silently ignored. The template now reads `secretName` from `.Values.ingress.tls[0].secretName` when `.Values.ingress.tls` is a list, falling back to `wordpress-tls` for backward compatibility. +- **`templates/php-config-configmap.yaml`**: `auto_prepend_file = '/var/www/html/wordfence-waf.php'` was emitted unconditionally, which causes PHP warnings on every request when the Wordfence plugin is not installed. Now gated behind the new `wordpress.wordfence.enabled` flag (default `false`). +- **`templates/ingress.yaml`**: `nginx.ingress.kubernetes.io/server-snippet` annotation is now opt-out via `ingress.serverSnippet.enabled` (default `true`). Hardened ingress-nginx controllers reject snippet annotations when `allow-snippet-annotations: false`; setting this to `false` allows deployment on those clusters. + +#### **Added** + +- `wordpress.wordfence.enabled` (default `false`) in `values.yaml`. +- `ingress.serverSnippet.enabled` (default `true`) in `values.yaml`. + ## [3.2.7] - 2026-05-06 ### 🔧 **Version Bump & Maintenance** diff --git a/wordpress/helm/Chart.yaml b/wordpress/helm/Chart.yaml index 4532793..cf2e5fa 100644 --- a/wordpress/helm/Chart.yaml +++ b/wordpress/helm/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: wordpress description: Enterprise-grade WordPress with enhanced security, zero-trust networking, and automated TLS type: application -version: 3.2.7 +version: 3.3.0 appVersion: "6.8.3" keywords: diff --git a/wordpress/helm/templates/ingress.yaml b/wordpress/helm/templates/ingress.yaml index 8ea9a21..1410f51 100644 --- a/wordpress/helm/templates/ingress.yaml +++ b/wordpress/helm/templates/ingress.yaml @@ -1,10 +1,14 @@ # {{ if .Values.ingress.enabled }} # {{ $tlsProtocols := "TLSv1.2 TLSv1.3" }} # {{ $tlsCiphers := "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305" }} +# {{ $tlsSecretName := "wordpress-tls" }} # {{ if and (hasKey .Values.ingress "tls") (kindIs "map" .Values.ingress.tls) }} # {{ $tlsProtocols = .Values.ingress.tls.protocols | default $tlsProtocols }} # {{ $tlsCiphers = .Values.ingress.tls.ciphers | default $tlsCiphers }} # {{ end }} +# {{ if and (hasKey .Values.ingress "tls") (kindIs "slice" .Values.ingress.tls) (gt (len .Values.ingress.tls) 0) }} +# {{ $tlsSecretName = (index .Values.ingress.tls 0).secretName | default $tlsSecretName }} +# {{ end }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -15,6 +19,7 @@ metadata: app.kubernetes.io/component: ingress annotations: # {{ toYaml .Values.ingress.annotations | nindent 4 }} + # {{ if and (hasKey .Values.ingress "serverSnippet") (kindIs "map" .Values.ingress.serverSnippet) .Values.ingress.serverSnippet.enabled }} # Block sensitive files at the edge (Task #264) nginx.ingress.kubernetes.io/server-snippet: | location ~* /\.(git|env|user\.ini|htaccess) { @@ -25,6 +30,7 @@ metadata: deny all; return 403; } + # {{ end }} # TLS/SSL Security (Enterprise-Grade, Parameterized) nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/ssl-protocols: "{{ $tlsProtocols }}" @@ -52,7 +58,7 @@ spec: # {{ if .Values.wordpress.includeWWW }} - '{{ printf "www.%s" .Values.wordpress.domain }}' # {{ end }} - secretName: wordpress-tls + secretName: "{{ $tlsSecretName }}" rules: - host: "{{ .Values.wordpress.domain }}" http: diff --git a/wordpress/helm/templates/php-config-configmap.yaml b/wordpress/helm/templates/php-config-configmap.yaml index ca51b42..c4558f2 100644 --- a/wordpress/helm/templates/php-config-configmap.yaml +++ b/wordpress/helm/templates/php-config-configmap.yaml @@ -13,6 +13,8 @@ data: max_execution_time = 300 max_input_time = 300 memory_limit = 256M + {{- if and (hasKey .Values.wordpress "wordfence") .Values.wordpress.wordfence.enabled }} ; Wordfence WAF Hardening: Forces loading via PHP-FPM auto_prepend_file = '/var/www/html/wordfence-waf.php' + {{- end }} diff --git a/wordpress/helm/values.yaml b/wordpress/helm/values.yaml index d4953c1..52e4571 100644 --- a/wordpress/helm/values.yaml +++ b/wordpress/helm/values.yaml @@ -133,6 +133,12 @@ wordpress: // Enable WordPress mail debug logging define('WP_MAIL_DEBUG', false); + ## Wordfence WAF integration. Enable only after the Wordfence plugin is + ## installed AND `wordfence-waf.php` exists at /var/www/html/. Otherwise + ## PHP will warn on every request and may abort execution. + wordfence: + enabled: false + ## WordPress Security Keys (Generated automatically by deployment script) security: authKey: "AUTH_KEY_PLACEHOLDER" @@ -255,6 +261,13 @@ ingress: # WordPress Performance nginx.ingress.kubernetes.io/proxy-body-size: "64m" # Allow large plugin/media uploads + ## Edge-level NGINX server-snippet (file blocking). + ## Hardened ingress-nginx controllers may disable snippet annotations via + ## `allow-snippet-annotations: false`. Set enabled=false in that environment + ## and enforce the equivalent rules via controller ConfigMap or WAF. + serverSnippet: + enabled: true + ## TLS Security Configuration (Parameterized for flexibility) tls: protocols: "TLSv1.2 TLSv1.3" # Both for maximum compatibility (99.9% devices)