Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
93eccfe
feat: add architecture decisions log and implementation status tracker
Stualyttle May 24, 2026
fb54f39
feat: improve secret handling guidance and update environment configu…
Stualyttle May 24, 2026
70cc822
feat: update Node.js version to 24.16.0 and refresh npm packages
Stualyttle May 24, 2026
078b049
feat: implement authenticated-by-default control-plane API with publi…
Stualyttle May 24, 2026
025c84f
feat: enhance health monitoring with explicit probe endpoints and dep…
Stualyttle May 24, 2026
9856748
feat: implement fail-fast supervision for container processes and upd…
Stualyttle May 24, 2026
0c20d05
feat: implement explicit control-plane endpoint configuration for int…
Stualyttle May 24, 2026
f160925
feat: implement identity-aware authentication foundation with bearer-…
Stualyttle May 24, 2026
e3093d4
feat: implement explicit RBAC policies with a global authorization guard
Stualyttle May 24, 2026
939d675
feat: implement durable audit logging for privileged and mutating ope…
Stualyttle May 24, 2026
7a3f355
feat: enhance database connection configuration with dynamic pool siz…
Stualyttle May 24, 2026
1a68ec4
feat: implement lease-based leader coordination with generation fenci…
Stualyttle May 24, 2026
73879be
feat: implement lease-backed heartbeat and leader reconciliation
Stualyttle May 24, 2026
55b66b7
feat: implement durable cluster operation journal with per-node ackno…
Stualyttle May 24, 2026
df5206c
feat: implement staged NGINX runtime releases with atomic activation …
Stualyttle May 24, 2026
d2b3570
feat: implement validated allowlisted nginx_custom_code fragments wit…
Stualyttle May 24, 2026
a447e86
feat: implement strict domain validation and safe process execution f…
Stualyttle May 24, 2026
383eaa0
feat: implement durable certificate order state machine with artifact…
Stualyttle May 24, 2026
2435f9f
feat: implement ACK-backed certificate artifact activation with rollb…
Stualyttle May 24, 2026
bda8ad6
feat: implement explicit ACME challenge strategy with database-backed…
Stualyttle May 26, 2026
7dd4b0d
feat: implement Nest-managed ACME strategy with database-backed HTTP-…
Stualyttle May 26, 2026
347e5b6
feat: enforce production-hardened ACME strategy with explicit rejecti…
Stualyttle May 26, 2026
6aefc15
feat: implement envelope encryption for persisted certificate private…
Stualyttle May 26, 2026
3b27581
feat: implement encrypted backup artifacts with integrity verificatio…
Stualyttle May 26, 2026
5c5b2bb
feat: implement proxy management API with validation-first CRUD opera…
Stualyttle May 26, 2026
b483d2c
feat: add cluster overview and per-node inspection APIs with operatio…
Stualyttle May 26, 2026
ccef89b
feat: add security administration APIs for posture review, API key ro…
Stualyttle May 28, 2026
3787dc7
feat: implement structured operational logging with request context a…
Stualyttle May 28, 2026
498ba13
feat: expand metrics and health endpoints with dependency drilldowns …
Stualyttle May 28, 2026
d631e9f
feat: implement classified Node.js test harness with explicit unit, i…
Stualyttle May 28, 2026
b6f0730
feat: add chaos test suite for deterministic fault-injection validati…
Stualyttle May 28, 2026
967464d
feat: implement multi-job GitHub Actions release gates with coverage …
Stualyttle May 28, 2026
c5fc113
feat: update operator-facing documentation with current architecture,…
Stualyttle May 28, 2026
58bebf5
feat: finalize production readiness with checklist and deferment regi…
Stualyttle May 28, 2026
524f105
feat: update final production checklist with validation outputs and v…
Stualyttle May 28, 2026
37f996d
feat: reorganize test files into appropriate directories for better s…
Stualyttle May 28, 2026
d6c54fb
feat: update error messages and environment variable defaults for cla…
Stualyttle May 28, 2026
e921ee7
feat: update documentation for clarity and accuracy, refine SQL migra…
Stualyttle May 28, 2026
f2a1eb5
feat: add tests for domain utility functions and enforce test concurr…
Stualyttle May 28, 2026
9cf3bb7
feat: update certificate dates in tests to use dynamic values for imp…
Stualyttle Jun 5, 2026
0d2985f
feat: update GitHub Actions configuration to use latest versions of a…
Stualyttle Jun 5, 2026
7de4294
feat: enhance Dockerfile and CI workflow with pull option for image b…
Stualyttle Jun 5, 2026
1572e09
feat: improve entrypoint recovery tests to validate NGINX shutdown lo…
Stualyttle Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 120 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Environment Configuration Example for LyttlenGinx Application
#
# This file is intentionally safe to commit.
# Do not place live secrets in this file or commit your local `.env` / `.env.*` overrides.
# For production, inject sensitive values through Docker Swarm secrets or an external secret store.

# ===============================================
# DATABASE CONFIGURATION (REQUIRED)
# ===============================================
DATABASE_URL="postgresql://user:password@localhost:5432/lyttlenginx?connection_limit=20&pool_timeout=10&connect_timeout=10"
DATABASE_URL="postgresql://lyttlenginx:<db-password>@localhost:5432/lyttlenginx?connect_timeout=360&pool_timeout=360&pool_size=1&schema=public&sslmode=no-verify"
# If you provide `pool_size`, the runtime will mirror it into Prisma's
# `connection_limit` setting automatically.

# ===============================================
# ADMIN CONFIGURATION (REQUIRED FOR PRODUCTION)
Expand All @@ -13,19 +19,94 @@ DATABASE_URL="postgresql://user:password@localhost:5432/lyttlenginx?connection_l
ADMIN_EMAIL="admin@example.com"

# ===============================================
# SECURITY (REQUIRED - CHANGE THESE IN PRODUCTION!)
# SECURITY (REQUIRED - INJECT THESE AT RUNTIME)
# ===============================================
# Generate secure random strings for production:
# - Use: openssl rand -base64 32
# API_KEY supports multiple comma-separated keys
API_KEY="your-secure-api-key-change-this"
# Generate strong secrets at deploy time, for example:
# openssl rand -base64 32
# API_KEY supports multiple comma-separated keys.
# Keep the real value in a Swarm secret or external secret manager.
# API keys remain available as a temporary compatibility bridge, but bearer
# tokens should be the default client credential format going forward.
API_KEY="<inject-runtime-secret>"

# JWT / bearer-token foundation.
# AUTH_JWT_SECRET enables locally signed HS256 access tokens (including
# `/auth/token` exchange from a legacy API key). For external identity providers,
# prefer AUTH_JWT_PUBLIC_KEY plus the expected issuer/audience below.
AUTH_JWT_SECRET="<inject-runtime-secret>"
# AUTH_JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
AUTH_JWT_ISSUER="lyttle-nginx"
AUTH_JWT_AUDIENCE="lyttle-nginx-admin"
AUTH_JWT_ALLOWED_ALGS="HS256,RS256"
AUTH_ACCESS_TOKEN_TTL_SECONDS="900"

# Temporary default identity mapping for legacy API keys during the bearer-token migration.
# Supported RBAC roles are: viewer, operator, security-admin, platform-admin.
AUTH_DEFAULT_ADMIN_ROLES="platform-admin"
AUTH_DEFAULT_ADMIN_SCOPES="admin:full"

# Certificate private-key encryption at rest.
# The current build uses a local application-layer envelope-encryption provider by default.
# Generate a strong secret, for example: openssl rand -base64 32
# Change PRIVATE_KEY_ENCRYPTION_KEY_VERSION when you intentionally rotate the master key.
PRIVATE_KEY_ENCRYPTION_PROVIDER="local"
PRIVATE_KEY_ENCRYPTION_MASTER_KEY="<inject-runtime-secret>"
PRIVATE_KEY_ENCRYPTION_KEY_VERSION="v1"

# Backup encryption and integrity protection.
# This key encrypts downloaded backup artifacts and signs their manifests.
# Generate a strong secret, for example: openssl rand -base64 32
BACKUP_ENCRYPTION_KEY="<inject-runtime-secret>"
BACKUP_ENCRYPTION_KEY_VERSION="v1"

# ===============================================
# APPLICATION SETTINGS
# ===============================================
NODE_ENV="production"
PORT="3000"

# Restrict `nginx_custom_code` root/alias directives to approved static-content
# prefixes. The application validates fragments against this allowlist before any
# staged config activation occurs.
NGINX_CUSTOM_CODE_ALLOWED_PATH_PREFIXES="/var/www,/srv/www,/etc/nginx/custom,/usr/share/nginx/html,/etc/nginx/html"

# ===============================================
# CLUSTER CONTROL-PLANE SETTINGS
# ===============================================
# Configure the host/address and port that peer nodes should use for inter-node
# control-plane traffic. Do not rely on public-IP autodiscovery.
#
# Single-node/local example:
# CLUSTER_CONTROL_ADDRESS="app"
# CLUSTER_CONTROL_PORT="3000"
#
# Swarm global-mode example with host-published admin port:
# CLUSTER_CONTROL_ADDRESS="node-a.internal"
# CLUSTER_CONTROL_PORT="3003"
#
# You may alternatively provide CLUSTER_CONTROL_URL="http://node-a.internal:3003"
# instead of the address/port pair.
CLUSTER_CONTROL_ADDRESS="node-a.internal"
CLUSTER_CONTROL_PORT="3003"
CLUSTER_CONTROL_PROTOCOL="http"

# Leader coordination lease tuning.
# Keep the TTL comfortably larger than the renew interval so transient delays do
# not cause avoidable leader loss.
CLUSTER_LEASE_TTL_SECONDS="30"
CLUSTER_LEASE_RENEW_INTERVAL_MS="10000"

# Health/readiness freshness thresholds for local config apply and certificate sync.
HEALTH_CONFIG_APPLY_MAX_AGE_MS="900000"
HEALTH_CERTIFICATE_SYNC_MAX_AGE_MS="900000"

# Metrics/alerting freshness thresholds used by /metrics and the recommended
# Prometheus alerts documented in docs/runbooks/monitoring-alerts.md.
METRICS_CLUSTER_OPERATION_STALE_MAX_AGE_MS="900000"
METRICS_CLUSTER_OPERATION_FAILURE_WINDOW_MS="3600000"
METRICS_CERTIFICATE_ORDER_STALE_MAX_AGE_MS="1800000"
METRICS_BACKUP_MAX_AGE_MS="86400000"

# ===============================================
# CERTIFICATE MANAGEMENT SETTINGS
# ===============================================
Expand All @@ -39,6 +120,28 @@ ALERT_THRESHOLD_DAYS="14"
# Set to "true" to avoid rate limits during testing
# LETSENCRYPT_STAGING="false"

# ACME challenge strategy selection.
# - auto : production default; currently resolves to the built-in shared HTTP-01 flow
# - http-01: force the built-in shared HTTP-01 flow explicitly
# Wildcard orders are intentionally rejected because the hardened ACME flow must
# not require DNS TXT changes.
ACME_CHALLENGE_STRATEGY="auto"

# Optional explicit ACME directory URL override.
# Leave unset for the default Let's Encrypt production/staging selection.
# ACME_DIRECTORY_URL="https://acme-v02.api.letsencrypt.org/directory"

# ACME account key persistence. The application creates this key on first use and
# reuses it for later orders instead of relying on external shell scripts.
ACME_ACCOUNT_PRIVATE_KEY_PATH="/app/state/acme/account.pem"

# Built-in HTTP-01 configuration.
ACME_HTTP01_PROVIDER="database-http-01"
ACME_HTTP01_PROPAGATION_SECONDS="5"
ACME_HTTP01_VERIFICATION_TIMEOUT_MS="30000"
ACME_HTTP01_POLL_INTERVAL_MS="2000"


# ===============================================
# EMAIL/SMTP CONFIGURATION (OPTIONAL - RECOMMENDED)
# ===============================================
Expand All @@ -48,24 +151,30 @@ SMTP_HOST=""
SMTP_PORT="587"
SMTP_SECURE="false"
SMTP_USER=""
SMTP_PASS=""
SMTP_PASS="<inject-runtime-secret-or-leave-empty>"
ALERT_EMAIL=""
ALERT_FROM_EMAIL=""

# ===============================================
# ALERT WEBHOOKS (OPTIONAL - RECOMMENDED)
# ===============================================
# Slack webhook URL for alerts
SLACK_WEBHOOK_URL=""
SLACK_WEBHOOK_URL="<inject-runtime-secret-or-leave-empty>"

# Discord webhook URL for alerts
DISCORD_WEBHOOK_URL=""
DISCORD_WEBHOOK_URL="<inject-runtime-secret-or-leave-empty>"

# ===============================================
# RATE LIMITING (OPTIONAL)
# ===============================================
RATE_LIMIT_TTL="60"
RATE_LIMIT_MAX="100"



# ===============================================
# SECRET-HANDLING NOTES
# ===============================================
# - `.env.example` is the only env file intended to be tracked in git.
# - Keep real `.env`, `.env.production`, `.env.local`, and similar files out of version control.
# - Prefer Docker Swarm secrets, Vault, AWS Secrets Manager, GCP Secret Manager, or equivalent.
# - Until the application gains first-class *_FILE secret ingestion, inject runtime values from
# your deployment system rather than committing them to the repository.
186 changes: 179 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,184 @@
name: MAIN Workflow
name: CI and Release Gates

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
NODE_VERSION: 24.16.0
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
COVERAGE_MIN_LINES: '70'
COVERAGE_MIN_BRANCHES: '67'
COVERAGE_MIN_FUNCTIONS: '76'

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Run CI lint gate
run: npm run lint:ci

typecheck:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Generate Prisma client
run: npm run prisma:generate

- name: Run TypeScript typecheck
run: npm run typecheck

tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Generate Prisma client
run: npm run prisma:generate

- name: Run tests with coverage gate
run: npm run test:coverage:ci

build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6.0.3

- name: Set up Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Generate Prisma client
run: npm run prisma:generate

- name: Build application
run: npm run build

dependency-audit:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Node.js
uses: actions/setup-node@v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Run production dependency audit
run: npm run audit:prod

container-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build container image for scanning
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
load: true
pull: true
tags: lyttle-nginx:ci-${{ github.sha }}

- name: Scan container image with Trivy
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: lyttle-nginx:ci-${{ github.sha }}
format: table
exit-code: '1'
ignore-unfixed: true
vuln-type: os,library
severity: HIGH,CRITICAL

publish-image:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs:
- lint
- typecheck
- tests
- build
- dependency-audit
- container-scan
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Prepare container metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/lyttlenginx
tags: |
type=raw,value=main
type=sha,prefix=sha-

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
Expand All @@ -19,8 +187,12 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build API Docker image
run: docker build -t ghcr.io/lyttle-development/lyttlenginx:main -f Dockerfile .

- name: Push API Docker image to GitHub Container Registry
run: docker push ghcr.io/lyttle-development/lyttlenginx:main
- name: Build and push API Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
pull: true
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Loading
Loading