diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..2ddc3ce
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_size = 2
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..db2ef4c
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,38 @@
+{
+ "root": true,
+ "parser": "@typescript-eslint/parser",
+ "parserOptions": {
+ "ecmaVersion": 2020,
+ "sourceType": "module",
+ "project": "./tsconfig.json"
+ },
+ "plugins": ["@typescript-eslint"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "ignorePatterns": [
+ "dist/",
+ "cdk.out/",
+ "node_modules/",
+ "*.js",
+ "*.d.ts"
+ ],
+ "rules": {
+ "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-non-null-assertion": "warn",
+ "no-console": "off",
+ "prefer-const": "error",
+ "no-var": "error"
+ },
+ "overrides": [
+ {
+ "files": ["*.test.ts"],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "off"
+ }
+ }
+ ]
+}
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..8cf5309
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,37 @@
+## Description
+
+
+
+## Type of Change
+
+- [ ] Bug fix
+- [ ] New feature
+- [ ] Enhancement (improvement to existing functionality)
+- [ ] Documentation update
+- [ ] Refactoring / Tech debt
+- [ ] Dependency update
+- [ ] CI/CD change
+
+## Testing
+
+- [ ] `npm run build` passes
+- [ ] `npm test` passes
+- [ ] `npm run lint` passes (if applicable)
+- [ ] `npm run synth` passes (if applicable)
+
+## Platform Impact
+
+
+
+## Checklist
+
+- [ ] My code follows the project's code style
+- [ ] I have added tests that prove my fix/feature works
+- [ ] New and existing tests pass
+- [ ] I have updated documentation as needed
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..fb9999a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,41 @@
+version: 2
+updates:
+ - package-ecosystem: npm
+ directory: /
+ schedule:
+ interval: weekly
+ day: monday
+ open-pull-requests-limit: 10
+ labels:
+ - dependencies
+ - automerge
+ groups:
+ aws-cdk:
+ patterns:
+ - aws-cdk
+ - aws-cdk-lib
+ - cdk-nag
+ jest:
+ patterns:
+ - jest
+ - ts-jest
+ - "@types/jest"
+
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: weekly
+ day: monday
+ open-pull-requests-limit: 5
+ labels:
+ - dependencies
+ - github-actions
+
+ - package-ecosystem: terraform
+ directory: /terraform
+ schedule:
+ interval: weekly
+ day: monday
+ labels:
+ - dependencies
+ - terraform
diff --git a/.github/workflows/platform-iac-ci.yml b/.github/workflows/platform-iac-ci.yml
index 2f45c34..e456f04 100644
--- a/.github/workflows/platform-iac-ci.yml
+++ b/.github/workflows/platform-iac-ci.yml
@@ -5,6 +5,8 @@ on:
branches: [ main ]
paths:
- 'platform/**'
+ - 'packages/**'
+ - 'applications/examples/**'
- 'lib/**'
- 'bin/**'
- 'test/**'
@@ -32,13 +34,16 @@ jobs:
- name: Install deps
run: npm ci
+ - name: ESLint check
+ run: npm run lint
+
- name: TypeScript build
run: npm run build
- - name: Unit and snapshot tests
- run: npm test -- --ci
+ - name: Unit, snapshot, and compliance tests
+ run: npm test -- --ci --coverage
- - name: CDK synth
+ - name: CDK synth with cdk-nag compliance guardrails
run: npm run synth
- name: Static security scan (Checkov)
@@ -51,8 +56,19 @@ jobs:
output_file_path: console,results.sarif
quiet: true
- - name: Upload Checkov SARIF report
+ - name: Check for Checkov SARIF report
+ id: checkov-sarif
if: always()
+ run: |
+ if [ -f results.sarif ]; then
+ echo "exists=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "exists=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Upload Checkov SARIF report
+ if: always() && steps.checkov-sarif.outputs.exists == 'true'
+ continue-on-error: true
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
@@ -65,4 +81,4 @@ jobs:
skip-dirs: cdk.out,node_modules
hide-progress: true
severity: CRITICAL,HIGH
- exit-code: '0'
+ exit-code: '1'
diff --git a/.github/workflows/vault-approle-demo.yml b/.github/workflows/vault-approle-demo.yml
new file mode 100644
index 0000000..497e28b
--- /dev/null
+++ b/.github/workflows/vault-approle-demo.yml
@@ -0,0 +1,37 @@
+name: vault-approle-demo
+
+on:
+ workflow_dispatch: {}
+
+permissions: {}
+
+jobs:
+ approle-demo:
+ runs-on: ubuntu-latest
+ env:
+ VAULT_ADDR: ${{ secrets.VAULT_ADDR }}
+ # Default secret path for demo; override by setting SECRET_PATH in workflow run environment variables
+ SECRET_PATH: jenkins/demo
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Vault CLI
+ uses: hashicorp/setup-vault@v2
+ with:
+ vault_version: '1.14.3'
+
+ - name: AppRole login and fetch secret
+ env:
+ ROLE_ID: ${{ secrets.VAULT_ROLE_ID }}
+ SECRET_ID: ${{ secrets.VAULT_SECRET_ID }}
+ SECRET_PATH: ${{ env.SECRET_PATH }}
+ run: |
+ set -euo pipefail
+ echo "Logging in with AppRole (ROLE_ID masked by GitHub secrets)"
+ vault login -method=approle role_id="$ROLE_ID" secret_id="$SECRET_ID"
+ echo "Fetching secret from secret/$SECRET_PATH (field: password)"
+ SECRET_VALUE=$(vault kv get -field=password "secret/$SECRET_PATH")
+ # Mask secret in logs
+ echo "::add-mask::$SECRET_VALUE"
+ echo "Successfully fetched secret (value masked)."
diff --git a/.gitignore b/.gitignore
index f4f7fc1..5af2b8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,11 @@
-*.js
-!jest.config.js
+# Built JavaScript output (source is TypeScript)
+dist/**/*.js
+dist/**/*.d.ts
+
+# TypeScript declarations
*.d.ts
+
+# Do NOT add blanket *.js ignores — jest.config.js and other configs are JS
node_modules
.DS_Store
.env
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..209e3ef
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+20
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..3d2ed5e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,34 @@
+# Contributing
+
+## Platform API compatibility
+
+The platform construct APIs are treated as internal product contracts.
+
+- Additive optional props are minor-compatible changes.
+- Changing defaults that affect deployed infrastructure requires a migration note.
+- Removing props, changing required props, or replacing resources requires a deprecation cycle.
+
+## Deprecation policy
+
+Deprecated construct props and behavior remain available for at least one release train before removal.
+
+Every deprecation must include:
+
+- replacement guidance,
+- migration steps,
+- target removal release,
+- test coverage for both old and new behavior during the deprecation window.
+
+## Pull request expectations
+
+Platform changes must include:
+
+- `npm run build`
+- `npm test`
+- `npm run synth`
+- focused tests for new constructs, policies, or environment behavior
+- documentation updates when the consumer contract changes
+
+## Ownership
+
+The platform team owns reusable constructs, configuration contracts, policy packs, and CI gates. Application teams own service-specific code and configuration that consumes those platform APIs.
diff --git a/Makefile b/Makefile
index f3d5e11..cdda7eb 100644
--- a/Makefile
+++ b/Makefile
@@ -3,51 +3,93 @@ ENV ?= dev
SERVICE ?= sample-service
TAG ?= latest
-.PHONY: help build test synth platform-check platform-plan platform-apply app-bootstrap app-deploy app-policy-test platform-progress
+.PHONY: help build test lint format coverage synth clean \
+ platform-check platform-plan platform-apply \
+ app-bootstrap app-deploy app-policy-test \
+ precommit setup
help:
- @echo "make build # Build TypeScript"
- @echo "make test # Run tests"
- @echo "make synth # CDK synth"
- @echo "make platform-check # Build + synth + lint placeholder"
- @echo "make platform-plan ENV=dev # Plan platform changes"
- @echo "make platform-apply ENV=dev # Apply platform changes"
- @echo "make app-bootstrap SERVICE=name # Bootstrap app from template"
- @echo "make app-deploy ENV=dev SERVICE=name TAG=v1.0.0"
- @echo "make app-policy-test # Run local policy bundle checks"
- @echo "make platform-progress # Show platform-as-product progress tracker"
+ @echo '──────────────────────────────────────────────────────'
+ @echo ' InfraAsCodeWithCDK — Platform as a Product'
+ @echo '──────────────────────────────────────────────────────'
+ @echo ''
+ @echo ' Development:'
+ @echo ' make setup # Install dependencies'
+ @echo ' make build # TypeScript compile (tsc)'
+ @echo ' make lint # ESLint check'
+ @echo ' make format # Prettier check'
+ @echo ' make format:fix # Prettier auto-fix'
+ @echo ' make test # Run unit tests'
+ @echo ' make coverage # Run tests with coverage report'
+ @echo ' make synth # CDK synth (PLATFORM_ENV=$(ENV))'
+ @echo ' make clean # Remove build artifacts'
+ @echo ''
+ @echo ' Pipeline:'
+ @echo ' make precommit # build + lint + test (pre-push gate)'
+ @echo ' make platform-check # build + test + synth (full gate)'
+ @echo ' make platform-plan # Plan platform changes for ENV'
+ @echo ' make platform-apply # Apply platform changes for ENV'
+ @echo ''
+ @echo ' Application Lifecycle:'
+ @echo ' make app-bootstrap # Scaffold new service from Backstage template'
+ @echo ' make app-deploy # Update GitOps manifest and let Argo CD reconcile'
+ @echo ' make app-policy-test # Run conftest against gitops manifests'
+ @echo ''
+ @echo ' Parameters:'
+ @echo ' ENV=dev|stage|prod # Target environment (default: dev)'
+ @echo ' SERVICE=name # Service name for app commands'
+ @echo ' TAG=v1.0.0 # Container image tag'
+ @echo ''
+
+setup:
+ npm ci
build:
npm run build
+lint:
+ npm run lint
+
+format:
+ npm run format
+
+format-fix:
+ npm run format:fix
+
test:
npm test
+coverage:
+ npm run coverage
+
synth:
- npx cdk synth
+ PLATFORM_ENV=$(ENV) npm run synth
+
+clean:
+ rm -rf dist cdk.out coverage
-platform-check: build synth
- @echo "[platform-check] add checkov/tfsec/cdk-nag in CI"
+precommit:
+ npm run precommit
+
+platform-check: build test synth
+ @echo '[platform-check] build, tests, and synth completed for ENV=$(ENV)'
platform-plan:
- @echo "[platform-plan] ENV=$(ENV)"
- @echo "Use environment overlays in platform/environments/$(ENV)"
+ @echo '[platform-plan] ENV=$(ENV)'
+ @echo 'Use environment overlays in platform/environments/$(ENV)'
platform-apply:
- @echo "[platform-apply] ENV=$(ENV)"
- @echo "Run approved deploy pipeline for $(ENV)"
+ @echo '[platform-apply] ENV=$(ENV)'
+ @echo 'Run approved deploy pipeline for $(ENV)'
app-bootstrap:
- @echo "[app-bootstrap] SERVICE=$(SERVICE)"
- @echo "Scaffold from templates/service-catalog/template.yaml via Backstage"
+ @echo '[app-bootstrap] SERVICE=$(SERVICE)'
+ @echo 'Scaffold from templates/service-catalog/template.yaml via Backstage'
app-deploy:
- @echo "[app-deploy] ENV=$(ENV) SERVICE=$(SERVICE) TAG=$(TAG)"
- @echo "Update GitOps manifest tag and let Argo CD reconcile"
+ @echo '[app-deploy] ENV=$(ENV) SERVICE=$(SERVICE) TAG=$(TAG)'
+ @echo 'Update GitOps manifest tag and let Argo CD reconcile'
app-policy-test:
- @echo "[app-policy-test] run conftest against applications/gitops/base with applications/policy"
+ @echo '[app-policy-test] run conftest against applications/gitops/base with applications/policy'
conftest test applications/gitops/base/*.yaml -p applications/policy
-
-platform-progress:
- @cat docs/platform-product-progress.md
diff --git a/README.md b/README.md
index 9f25b5a..ab75eea 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,7 @@ graph TB
│ ├── services/ # Platform services (Argo CD, Backstage, monitoring)
│ └── environments/ # Per-environment composition (dev/stage/prod)
├── applications/ # Developer/App team owned
+│ ├── examples/orders-service/ # Canonical CDK consumer of platform constructs
│ ├── gitops/ # Kubernetes manifests + Kustomize overlays
│ │ ├── base/ # Reference: Namespace, Deployment, Service
│ │ └── overlays/ (dev/stage/prod)
@@ -132,8 +133,10 @@ graph TB
│ ├── template.yaml # Backstage scaffolder v1beta3
│ └── scaffold/ # Scaffold files (CI, observability, policy)
├── templates/service-catalog/ # Additional Backstage template (multi-language)
+├── packages/platform-constructs/ # Reusable golden-path CDK constructs
+│ └── src/api-lambda-dynamo-service/ # API Gateway + Lambda + DynamoDB construct
├── lib/ # CDK application source
-│ ├── cdk-app-stack.ts # Main stack — all AWS resources
+│ ├── cdk-app-stack.ts # Main stack composing platform constructs
│ ├── function.ts # Lambda handler (Node.js 18)
│ ├── platform-config.ts # Typed env config with validation
│ └── security-guardrails.ts # ALB-WAF CDK Aspect validation
@@ -153,6 +156,7 @@ graph TB
│ ├── platform-product-operating-model.md
│ ├── platform-engineering-consulting-profile.md
│ ├── observability-as-a-service.md
+│ ├── onboarding/first-service.md
│ └── oaas-implementation-flow.md
├── catalog-info.yaml # Backstage entity registration
├── Makefile # DX: platform-check, app-deploy, policy-test, etc.
@@ -317,7 +321,10 @@ kubeconform validation → Conftest OPA policy checks
| `docs/platform-engineering-consulting-profile.md` | Portfolio framing: strategy → architecture → implementation → adoption |
| `docs/observability-as-a-service.md` | OaaS maturity assessment + baseline implementation |
| `docs/oaas-implementation-flow.md` | End-to-end OaaS flow with ownership model and definition of done |
+| `docs/onboarding/first-service.md` | 30-minute first-service onboarding path using the golden-path construct |
| `docs/code-review-resolution.md` | Audit trail of how review feedback was addressed |
+| `packages/platform-constructs/README.md` | Reusable CDK construct contract and consumer example |
+| `CONTRIBUTING.md` | Compatibility, deprecation, ownership, and PR expectations |
| `PLATFORM_PRODUCT_SETUP.md` | Full platform transformation guide (905 lines) |
| `applications/policy/README.md` | OPA policy bundle documentation |
| `terraform/README.md` | HashiCorp Vault setup guide |
diff --git a/applications/examples/orders-service/infra/orders-service-stack.ts b/applications/examples/orders-service/infra/orders-service-stack.ts
new file mode 100644
index 0000000..c005212
--- /dev/null
+++ b/applications/examples/orders-service/infra/orders-service-stack.ts
@@ -0,0 +1,39 @@
+import * as cdk from 'aws-cdk-lib';
+import { Construct } from 'constructs';
+import { ApiLambdaDynamoService } from '../../../../packages/platform-constructs/src';
+import { PlatformConfig } from '../../../../lib/platform-config';
+import { applyComplianceGuardrails, stackSuppressionsForApiLambdaDynamo } from '../../../../lib/platform-compliance';
+
+export interface OrdersServiceStackProps extends cdk.StackProps {
+ readonly platformConfig: PlatformConfig;
+}
+
+export class OrdersServiceStack extends cdk.Stack {
+ public readonly service: ApiLambdaDynamoService;
+
+ constructor(scope: Construct, id: string, props: OrdersServiceStackProps) {
+ super(scope, id, props);
+
+ this.service = new ApiLambdaDynamoService(this, 'OrdersApiService', {
+ serviceName: 'orders-api',
+ stageName: props.platformConfig.environment,
+ catalogEntityRef: 'component:default/orders-service',
+ recommendedPathTemplateName: 'recommended-path-service',
+ recommendedPathTemplatePath: 'backstage/templates/recommended-path-service/template.yaml',
+ handlerEntry: `${__dirname}/../../../../lib/function.ts`,
+ environment: {
+ SERVICE_NAME: 'orders-api',
+ },
+ });
+
+ cdk.Tags.of(this).add('environment', props.platformConfig.environment);
+ cdk.Tags.of(this).add('project', props.platformConfig.project);
+ cdk.Tags.of(this).add('owner', props.platformConfig.owner);
+ cdk.Tags.of(this).add('cost-center', props.platformConfig.costCenter);
+ cdk.Tags.of(this).add('data-classification', props.platformConfig.dataClassification);
+ cdk.Tags.of(this).add('finops-managed', 'true');
+
+ applyComplianceGuardrails(this);
+ stackSuppressionsForApiLambdaDynamo(this, 'orders-api');
+ }
+}
diff --git a/coverage/clover.xml b/coverage/clover.xml
new file mode 100644
index 0000000..3844ef5
--- /dev/null
+++ b/coverage/clover.xml
@@ -0,0 +1,6 @@
+
+
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|