From c072d69358b3f42bda3a8a2f2bba8931bd64ee67 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:13:37 -0500
Subject: [PATCH 1/9] run 'npm install --save raw-loader'

---
 package-lock.json | 71 +++++++++++++++++++++++++++++++++++++++++++++++
 package.json      |  1 +
 2 files changed, 72 insertions(+)

diff --git a/package-lock.json b/package-lock.json
index 18d2b8bb..2bffe5b7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
         "octokit": "^4.0.2",
         "plugin-image-zoom": "^1.2.0",
         "prism-react-renderer": "^2.4.0",
+        "raw-loader": "^4.0.2",
         "react": "^18.0.0",
         "react-dom": "^18.0.0",
         "react-error-boundary": "^4.1.2",
@@ -20241,6 +20242,76 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/raw-loader": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+      "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+      "license": "MIT",
+      "dependencies": {
+        "loader-utils": "^2.0.0",
+        "schema-utils": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/raw-loader/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/raw-loader/node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/raw-loader/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "license": "MIT"
+    },
+    "node_modules/raw-loader/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
     "node_modules/rc": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
diff --git a/package.json b/package.json
index 0607a80f..89e9271f 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
     "octokit": "^4.0.2",
     "plugin-image-zoom": "^1.2.0",
     "prism-react-renderer": "^2.4.0",
+    "raw-loader": "^4.0.2",
     "react": "^18.0.0",
     "react-dom": "^18.0.0",
     "react-error-boundary": "^4.1.2",

From f4d6651bbb376bf098750e9fb2ccbc1d849531e1 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:14:20 -0500
Subject: [PATCH 2/9] move docker-compose content to external file

---
 docs/getting-started/docker-compose.yaml | 281 ++++++++++++++++++++++
 docs/getting-started/index.mdx           | 287 +----------------------
 2 files changed, 286 insertions(+), 282 deletions(-)
 create mode 100644 docs/getting-started/docker-compose.yaml

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
new file mode 100644
index 00000000..ccf06763
--- /dev/null
+++ b/docs/getting-started/docker-compose.yaml
@@ -0,0 +1,281 @@
+name: opentdf
+volumes:
+  configs:
+  keys:
+  caddy_data:
+configs:
+  caddy_config:
+    content: |
+      {
+        log {
+            level INFO
+            output stdout
+        }
+      }
+      https://keycloak.opentdf.local:9443 {
+        tls internal
+        reverse_proxy keycloak:8888
+      }
+      https://platform.opentdf.local:8443 {
+        tls internal
+        reverse_proxy {
+            to h2c://platform:8080
+            transport http {
+                versions h2c 2 1.1  # Enable gRPC proxying
+            }
+        }
+
+      }
+services:
+  caddy:
+    image: caddy:alpine
+    command: ['caddy','run', '--config', '/etc/caddy/Caddyfile']
+    configs:
+      - source: caddy_config
+        target: /etc/caddy/Caddyfile
+    ports:
+      - '9443:9443'
+      - '8443:8443'
+    volumes:
+      - caddy_data:/data
+    restart: unless-stopped
+    healthcheck:
+      test: ["CMD-SHELL", "wget -q --server-response --tries=1 http://127.0.0.1:2019/metrics 2>&1 | awk '/^  HTTP/{print $2}' | grep -q '200'"]
+      interval: 5s
+      timeout: 5s
+      retries: 3
+  check-certs:
+    image: bash:latest
+    volumes:
+      - type: volume
+        source: caddy_data
+        target: /etc/ssl/certs
+        volume:
+          subpath: caddy/certificates/local/keycloak.opentdf.local/
+    command:
+      - bash
+      - -c
+      - |
+        echo "Checking certificates"
+        ls -alh /etc/ssl/certs
+        cat /etc/ssl/certs/keycloak.opentdf.local.crt
+    depends_on:
+      caddy:
+        condition: service_healthy
+      ensure-permissions:
+        condition: service_completed_successfully
+  ensure-permissions:
+    image: alpine
+    command:
+    - 'sh'
+    - '-c'
+    - |
+      chmod -R 777 /configs
+      ls -alh /configs
+      chmod -R 777 /keys
+      ls -alh /keys
+      chmod -R 777 /data
+      ls -alh /data
+    volumes:
+      - configs:/configs
+      - keys:/keys
+      - caddy_data:/data
+
+  #================================================================
+
+# Start Keycloak
+
+  #----------------------------------------------------------------
+  keycloak:
+    image: keycloak/keycloak:25.0
+    restart: unless-stopped
+    command: ['start-dev']
+    environment:
+      KC_DB: postgres
+      KC_DB_URL_HOST: keycloak-db
+      KC_DB_URL_PORT: 5432
+      KC_DB_URL_DATABASE: keycloak
+      KC_DB_USERNAME: postgres
+      KC_DB_PASSWORD: changeme
+      KC_HOSTNAME: 'https://keycloak.opentdf.local:9443'
+      KC_HOSTNAME_ADMIN: 'https://keycloak.opentdf.local:9443'
+      KC_HTTP_ENABLED: 'true'
+      KC_HTTP_PORT: 8888
+      KEYCLOAK_ADMIN: admin
+      KEYCLOAK_ADMIN_PASSWORD: changeme
+      KC_FEATURES: 'preview,token-exchange'
+      KC_HEALTH_ENABLED: 'true'
+      JAVA_OPTS_APPEND: '${JAVA_OPTS_APPEND:-}'
+    healthcheck:
+      test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java ${JAVA_OPTS_APPEND} /tmp/HealthCheck.java http://localhost:9000/health/ready']
+      interval: 5s
+      timeout: 10s
+      retries: 3
+      start_period: 5m
+    depends_on:
+      keycloak-db:
+        condition: service_healthy
+        restart: true
+  keycloak-db:
+    image: postgres:15-alpine
+    restart: unless-stopped
+    environment:
+      POSTGRES_PASSWORD: changeme
+      POSTGRES_USER: postgres
+      POSTGRES_DB: keycloak
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U postgres"]
+      interval: 5s
+      timeout: 5s
+      retries: 10
+      start_period: 2m
+  download-keycloak-config:
+    image: curlimages/curl:latest
+    volumes:
+      - configs:/configs
+    command: ['-o', '/configs/keycloak-config.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/cmd/keycloak_data.yaml']
+    depends_on:
+      ensure-permissions:
+        condition: service_completed_successfully
+  #================================================================
+
+# Provisioning Keycloak with expected realm, clients, and users
+
+  #----------------------------------------------------------------
+  keycloak-provisioning:
+    image: registry.opentdf.io/platform:nightly
+    volumes:
+      - configs:/configs
+    command:
+      [
+        'provision',
+        'keycloak',
+        '-e',
+        'http://keycloak:8888',
+        '-f',
+        '/configs/keycloak-config.yaml',
+      ]
+    depends_on:
+      keycloak:
+        condition: service_healthy
+        restart: true
+      download-keycloak-config:
+        condition: service_completed_successfully
+        restart: true
+  #================================================================
+
+# Start the OpenTDF service
+
+  #----------------------------------------------------------------
+  download-platform-config:
+    image: curlimages/curl:latest
+    volumes:
+      - configs:/configs
+    command: ['-o', '/configs/.opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-dev.yaml']
+    depends_on:
+      ensure-permissions:
+        condition: service_completed_successfully
+  modify-platform-config:
+    image: bash:latest
+    volumes:
+      - configs:/configs
+    command:
+      - bash
+      - -c
+      - |
+        echo "Modifying /configs/.opentdf.yaml"
+        echo "$(</configs/.opentdf.yaml )"
+        sed -i 's|kas-private.pem|/keys/kas-private.pem|g' /configs/.opentdf.yaml
+        sed -i 's|kas-cert.pem|/keys/kas-cert.pem|g' /configs/.opentdf.yaml
+        sed -i 's|kas-ec-private.pem|/keys/kas-ec-private.pem|g' /configs/.opentdf.yaml
+        sed -i 's|kas-ec-cert.pem|/keys/kas-ec-cert.pem|g' /configs/.opentdf.yaml
+        sed -i 's|# db:|db: |g' /configs/.opentdf.yaml
+        sed -i 's|#   host: localhost|  host: |g' /configs/.opentdf.yaml
+        sed -i 's|issuer: http://localhost:8888/auth/realms/opentdf|issuer: http://keycloak:8888/realms/opentdf|g' /configs/.opentdf.yaml
+        sed -i 's|tokenendpoint: http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token|tokenendpoint: http://keycloak:8888/realms/opentdf/protocol/openid-connect/token|g' /configs/.opentdf.yaml
+        sed -i 's|url: http://localhost:8888/auth|url: http://keycloak:8888|g' /configs/.opentdf.yaml
+        echo "$(</configs/.opentdf.yaml )"
+    depends_on:
+      download-platform-config:
+        condition: service_completed_successfully
+  generate-kas-rsa-keys:
+    image: alpine/openssl
+    volumes:
+      - keys:/keys
+    entrypoint: ["/bin/sh", "-c"]
+    command:
+      - |
+        echo "Generating RSA keys"
+        openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=kas" -keyout /keys/kas-private.pem -out /keys/kas-cert.pem -days 365
+        chmod 444 /keys/kas-private.pem
+        chmod 444 /keys/kas-cert.pem
+    depends_on:
+      ensure-permissions:
+        condition: service_completed_successfully
+  generate-kas-ec-keys:
+    image: alpine/openssl
+    volumes:
+      - keys:/keys
+    entrypoint: ["/bin/sh", "-c"]
+    command:
+      - |
+        echo "Generating EC keys"
+        openssl ecparam -name secp256r1 -out /keys/secp256r1.pem && \
+        openssl req -x509 -nodes -newkey ec:/keys/secp256r1.pem -subj "/CN=kas" -keyout /keys/kas-ec-private.pem -out /keys/kas-ec-cert.pem -days 365
+        chmod 444 /keys/kas-ec-private.pem
+        chmod 444 /keys/kas-ec-cert.pem
+    depends_on:
+      ensure-permissions:
+        condition: service_completed_successfully
+  platform:
+    image: registry.opentdf.io/platform:nightly
+    volumes:
+      - configs:/configs
+      - keys:/keys
+      - type: volume
+        source: caddy_data
+        target: /etc/ssl/certs
+        volume:
+          subpath: caddy/certificates/local/keycloak.opentdf.local
+    extra_hosts:
+      - "keycloak.opentdf.local:host-gateway"
+    command: ['start','--config-file','/configs/.opentdf.yaml','--config-key','opentdf']
+    restart: always
+    environment:
+      OPENTDF_DB_HOST: platform-db
+      OPENTDF_DB_USER: postgres
+      OPENTDF_DB_PASSWORD: changeme
+    depends_on:
+      keycloak:
+        condition: service_healthy
+        restart: true
+      keycloak-provisioning:
+        condition: service_completed_successfully
+      platform-db:
+        condition: service_healthy
+        restart: true
+      download-platform-config:
+        condition: service_completed_successfully
+      generate-kas-rsa-keys:
+        condition: service_completed_successfully
+      generate-kas-ec-keys:
+        condition: service_completed_successfully
+      modify-platform-config:
+        condition: service_completed_successfully
+      caddy:
+        condition: service_healthy
+      check-certs:
+        condition: service_completed_successfully
+  platform-db:
+    image: postgres:15-alpine
+    restart: unless-stopped
+    environment:
+      POSTGRES_USER: postgres
+      POSTGRES_PASSWORD: changeme
+      POSTGRES_DB: opentdf
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U postgres"]
+      interval: 5s
+      timeout: 5s
+      retries: 10
+
diff --git a/docs/getting-started/index.mdx b/docs/getting-started/index.mdx
index 0ac68bbd..7f1015cb 100644
--- a/docs/getting-started/index.mdx
+++ b/docs/getting-started/index.mdx
@@ -1,6 +1,10 @@
 ---
 slug: /getting-started
 ---
+
+import dockerComposeContent from '!!raw-loader!./docker-compose.yaml';
+import CodeBlock from '@theme/CodeBlock';
+
 # Getting Started
 
 This guide will walk you through setting up a new platform locally and walk you through some of the basic concepts within the OpenTDF platform.
@@ -31,289 +35,8 @@ Not for production use.
 
 <details>
 <summary>Docker Compose</summary>
-```yaml
-name: opentdf
-volumes:
-  configs:
-  keys:
-  caddy_data:
-configs:
-  caddy_config:
-    content: |
-      {
-        log {
-            level INFO
-            output stdout
-        }
-      }
-      https://keycloak.opentdf.local:9443 {
-        tls internal
-        reverse_proxy keycloak:8888
-      }
-      https://platform.opentdf.local:8443 {
-        tls internal
-        reverse_proxy {
-            to h2c://platform:8080
-            transport http {
-                versions h2c 2 1.1  # Enable gRPC proxying
-            }
-        }
-
-      }
-services:
-  caddy:
-    image: caddy:alpine
-    command: ['caddy','run', '--config', '/etc/caddy/Caddyfile']
-    configs:
-      - source: caddy_config
-        target: /etc/caddy/Caddyfile
-    ports:
-      - '9443:9443'
-      - '8443:8443'
-    volumes:
-      - caddy_data:/data
-    restart: unless-stopped
-    healthcheck:
-      test: ["CMD-SHELL", "wget -q --server-response --tries=1 http://127.0.0.1:2019/metrics 2>&1 | awk '/^  HTTP/{print $2}' | grep -q '200'"]
-      interval: 5s
-      timeout: 5s
-      retries: 3
-  check-certs:
-    image: bash:latest
-    volumes:
-      - type: volume
-        source: caddy_data
-        target: /etc/ssl/certs
-        volume:
-          subpath: caddy/certificates/local/keycloak.opentdf.local/
-    command:
-      - bash
-      - -c
-      - |
-        echo "Checking certificates"
-        ls -alh /etc/ssl/certs
-        cat /etc/ssl/certs/keycloak.opentdf.local.crt
-    depends_on:
-      caddy:
-        condition: service_healthy
-      ensure-permissions:
-        condition: service_completed_successfully
-  ensure-permissions:
-    image: alpine
-    command:
-    - 'sh'
-    - '-c'
-    - |
-      chmod -R 777 /configs
-      ls -alh /configs
-      chmod -R 777 /keys
-      ls -alh /keys
-      chmod -R 777 /data
-      ls -alh /data
-    volumes:
-      - configs:/configs
-      - keys:/keys
-      - caddy_data:/data
-
-  #================================================================
-
-# Start Keycloak
-
-  #----------------------------------------------------------------
-  keycloak:
-    image: keycloak/keycloak:25.0
-    restart: unless-stopped
-    command: ['start-dev']
-    environment:
-      KC_DB: postgres
-      KC_DB_URL_HOST: keycloak-db
-      KC_DB_URL_PORT: 5432
-      KC_DB_URL_DATABASE: keycloak
-      KC_DB_USERNAME: postgres
-      KC_DB_PASSWORD: changeme
-      KC_HOSTNAME: 'https://keycloak.opentdf.local:9443'
-      KC_HOSTNAME_ADMIN: 'https://keycloak.opentdf.local:9443'
-      KC_HTTP_ENABLED: 'true'
-      KC_HTTP_PORT: 8888
-      KEYCLOAK_ADMIN: admin
-      KEYCLOAK_ADMIN_PASSWORD: changeme
-      KC_FEATURES: 'preview,token-exchange'
-      KC_HEALTH_ENABLED: 'true'
-      JAVA_OPTS_APPEND: '${JAVA_OPTS_APPEND:-}'
-    healthcheck:
-      test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java ${JAVA_OPTS_APPEND} /tmp/HealthCheck.java http://localhost:9000/health/ready']
-      interval: 5s
-      timeout: 10s
-      retries: 3
-      start_period: 5m
-    depends_on:
-      keycloak-db:
-        condition: service_healthy
-        restart: true
-  keycloak-db:
-    image: postgres:15-alpine
-    restart: unless-stopped
-    environment:
-      POSTGRES_PASSWORD: changeme
-      POSTGRES_USER: postgres
-      POSTGRES_DB: keycloak
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U postgres"]
-      interval: 5s
-      timeout: 5s
-      retries: 10
-      start_period: 2m
-  download-keycloak-config:
-    image: curlimages/curl:latest
-    volumes:
-      - configs:/configs
-    command: ['-o', '/configs/keycloak-config.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/cmd/keycloak_data.yaml']
-    depends_on:
-      ensure-permissions:
-        condition: service_completed_successfully
-  #================================================================
-
-# Provisioning Keycloak with expected realm, clients, and users
-
-  #----------------------------------------------------------------
-  keycloak-provisioning:
-    image: registry.opentdf.io/platform:nightly
-    volumes:
-      - configs:/configs
-    command:
-      [
-        'provision',
-        'keycloak',
-        '-e',
-        'http://keycloak:8888',
-        '-f',
-        '/configs/keycloak-config.yaml',
-      ]
-    depends_on:
-      keycloak:
-        condition: service_healthy
-        restart: true
-      download-keycloak-config:
-        condition: service_completed_successfully
-        restart: true
-  #================================================================
-
-# Start the OpenTDF service
-
-  #----------------------------------------------------------------
-  download-platform-config:
-    image: curlimages/curl:latest
-    volumes:
-      - configs:/configs
-    command: ['-o', '/configs/.opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-dev.yaml']
-    depends_on:
-      ensure-permissions:
-        condition: service_completed_successfully
-  modify-platform-config:
-    image: bash:latest
-    volumes:
-      - configs:/configs
-    command:
-      - bash
-      - -c
-      - |
-        echo "Modifying /configs/.opentdf.yaml"
-        echo "$(</configs/.opentdf.yaml )"
-        sed -i 's|kas-private.pem|/keys/kas-private.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-cert.pem|/keys/kas-cert.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-ec-private.pem|/keys/kas-ec-private.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-ec-cert.pem|/keys/kas-ec-cert.pem|g' /configs/.opentdf.yaml
-        sed -i 's|# db:|db: |g' /configs/.opentdf.yaml
-        sed -i 's|#   host: localhost|  host: |g' /configs/.opentdf.yaml
-        sed -i 's|issuer: http://localhost:8888/auth/realms/opentdf|issuer: http://keycloak:8888/realms/opentdf|g' /configs/.opentdf.yaml
-        sed -i 's|tokenendpoint: http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token|tokenendpoint: http://keycloak:8888/realms/opentdf/protocol/openid-connect/token|g' /configs/.opentdf.yaml
-        sed -i 's|url: http://localhost:8888/auth|url: http://keycloak:8888|g' /configs/.opentdf.yaml
-        echo "$(</configs/.opentdf.yaml )"
-    depends_on:
-      download-platform-config:
-        condition: service_completed_successfully
-  generate-kas-rsa-keys:
-    image: alpine/openssl
-    volumes:
-      - keys:/keys
-    entrypoint: ["/bin/sh", "-c"]
-    command:
-      - |
-        echo "Generating RSA keys"
-        openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=kas" -keyout /keys/kas-private.pem -out /keys/kas-cert.pem -days 365
-        chmod 444 /keys/kas-private.pem
-        chmod 444 /keys/kas-cert.pem
-    depends_on:
-      ensure-permissions:
-        condition: service_completed_successfully
-  generate-kas-ec-keys:
-    image: alpine/openssl
-    volumes:
-      - keys:/keys
-    entrypoint: ["/bin/sh", "-c"]
-    command:
-      - |
-        echo "Generating EC keys"
-        openssl ecparam -name secp256r1 -out /keys/secp256r1.pem && \
-        openssl req -x509 -nodes -newkey ec:/keys/secp256r1.pem -subj "/CN=kas" -keyout /keys/kas-ec-private.pem -out /keys/kas-ec-cert.pem -days 365
-        chmod 444 /keys/kas-ec-private.pem
-        chmod 444 /keys/kas-ec-cert.pem
-    depends_on:
-      ensure-permissions:
-        condition: service_completed_successfully
-  platform:
-    image: registry.opentdf.io/platform:nightly
-    volumes:
-      - configs:/configs
-      - keys:/keys
-      - type: volume
-        source: caddy_data
-        target: /etc/ssl/certs
-        volume:
-          subpath: caddy/certificates/local/keycloak.opentdf.local
-    extra_hosts:
-      - "keycloak.opentdf.local:host-gateway"
-    command: ['start','--config-file','/configs/.opentdf.yaml','--config-key','opentdf']
-    restart: always
-    environment:
-      OPENTDF_DB_HOST: platform-db
-      OPENTDF_DB_USER: postgres
-      OPENTDF_DB_PASSWORD: changeme
-    depends_on:
-      keycloak:
-        condition: service_healthy
-        restart: true
-      keycloak-provisioning:
-        condition: service_completed_successfully
-      platform-db:
-        condition: service_healthy
-        restart: true
-      download-platform-config:
-        condition: service_completed_successfully
-      generate-kas-rsa-keys:
-        condition: service_completed_successfully
-      generate-kas-ec-keys:
-        condition: service_completed_successfully
-      modify-platform-config:
-        condition: service_completed_successfully
-      caddy:
-        condition: service_healthy
-      check-certs:
-        condition: service_completed_successfully
-  platform-db:
-    image: postgres:15-alpine
-    restart: unless-stopped
-    environment:
-      POSTGRES_USER: postgres
-      POSTGRES_PASSWORD: changeme
-      POSTGRES_DB: opentdf
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U postgres"]
-      interval: 5s
-      timeout: 5s
-      retries: 10
 
-```
+<CodeBlock language="yaml">{dockerComposeContent}</CodeBlock>
 
 </details>
 

From 4ee684c729ca8fef725a2d908455138e8be0e211 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:27:12 -0500
Subject: [PATCH 3/9] Create a Github Action to deploy the compose stack

---
 .github/workflows/docker-compose-test.yml | 96 +++++++++++++++++++++++
 1 file changed, 96 insertions(+)
 create mode 100644 .github/workflows/docker-compose-test.yml

diff --git a/.github/workflows/docker-compose-test.yml b/.github/workflows/docker-compose-test.yml
new file mode 100644
index 00000000..17afea1c
--- /dev/null
+++ b/.github/workflows/docker-compose-test.yml
@@ -0,0 +1,96 @@
+name: Docker Compose Stack Test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+  workflow_dispatch:
+
+jobs:
+  test-stack:
+    runs-on: ubuntu-latest
+    timeout-minutes: 15
+    
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+      
+      - name: Add hosts entries
+        run: |
+          echo -e "127.0.0.1 platform.opentdf.local\n127.0.0.1 keycloak.opentdf.local" | sudo tee -a /etc/hosts
+      
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      
+      - name: Setup Docker's apt repository
+        run: | 
+            # Add Docker's official GPG key:
+            sudo apt update
+            sudo apt install ca-certificates curl
+            sudo install -m 0755 -d /etc/apt/keyrings
+            sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
+            sudo chmod a+r /etc/apt/keyrings/docker.asc
+
+            # Add the repository to Apt sources:
+            sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
+            Types: deb
+            URIs: https://download.docker.com/linux/ubuntu
+            Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
+            Components: stable
+            Signed-By: /etc/apt/keyrings/docker.asc
+            EOF
+
+            sudo apt update
+
+      - name: Install Docker Compose
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y docker-compose-plugin
+          docker compose version
+      
+      - name: Start Docker Compose stack
+        working-directory: docs/getting-started
+        run: |
+          docker compose up -d
+      
+      - name: Wait for platform health check
+        run: |
+          echo "Waiting for platform to be ready..."
+          timeout=300  # 5 minutes in seconds
+          elapsed=0
+          interval=15
+          
+          while [ $elapsed -lt $timeout ]; do
+            echo "Checking health endpoint (${elapsed}s elapsed)..."
+            
+            # Use -k to allow insecure connections (self-signed cert)
+            # Use -s for silent mode, -w for response code
+            response=$(curl -k -s -w "\n%{http_code}" https://platform.opentdf.local:8443/healthz 2>/dev/null || echo "")
+            
+            if [ -n "$response" ]; then
+              http_code=$(echo "$response" | tail -n1)
+              body=$(echo "$response" | head -n1)
+              
+              echo "HTTP Code: $http_code"
+              echo "Response Body: $body"
+              
+              if [ "$http_code" = "200" ] && echo "$body" | grep -q '"status":"SERVING"'; then
+                echo "✓ Platform is ready!"
+                exit 0
+              fi
+            fi
+            
+            sleep $interval
+            elapsed=$((elapsed + interval))
+          done
+          
+          echo "✗ Timeout waiting for platform to become ready"
+          exit 1
+      
+      - name: Show container logs on failure
+        if: failure()
+        working-directory: docs/getting-started
+        run: |
+          docker compose ps
+          docker compose logs

From ff9af5ac1d430ff92ea11f8fd313e9cd40df010c Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:36:09 -0500
Subject: [PATCH 4/9] improve the compose file

---
 docs/getting-started/docker-compose.yaml | 572 ++++++++++++++++-------
 1 file changed, 412 insertions(+), 160 deletions(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index ccf06763..d070d4a4 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -1,8 +1,7 @@
-name: opentdf
-volumes:
-  configs:
-  keys:
-  caddy_data:
+networks:
+  default:
+    name: opentdf_platform
+
 configs:
   caddy_config:
     content: |
@@ -24,8 +23,8 @@ configs:
                 versions h2c 2 1.1  # Enable gRPC proxying
             }
         }
-
       }
+
 services:
   caddy:
     image: caddy:alpine
@@ -34,16 +33,21 @@ services:
       - source: caddy_config
         target: /etc/caddy/Caddyfile
     ports:
-      - '9443:9443'
-      - '8443:8443'
+      - "8443:8443"
+      - "9443:9443"
+      - "2019:2019"
     volumes:
       - caddy_data:/data
-    restart: unless-stopped
+    depends_on:
+      ensure-permissions:
+        condition: service_completed_successfully
     healthcheck:
-      test: ["CMD-SHELL", "wget -q --server-response --tries=1 http://127.0.0.1:2019/metrics 2>&1 | awk '/^  HTTP/{print $2}' | grep -q '200'"]
+      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:2019/metrics"]
       interval: 5s
       timeout: 5s
       retries: 3
+    restart: unless-stopped
+
   check-certs:
     image: bash:latest
     volumes:
@@ -64,218 +68,466 @@ services:
         condition: service_healthy
       ensure-permissions:
         condition: service_completed_successfully
+    restart: "no"
+
   ensure-permissions:
     image: alpine
     command:
     - 'sh'
     - '-c'
     - |
-      chmod -R 777 /configs
-      ls -alh /configs
-      chmod -R 777 /keys
-      ls -alh /keys
-      chmod -R 777 /data
-      ls -alh /data
+      chmod -R 755 /data
     volumes:
-      - configs:/configs
-      - keys:/keys
       - caddy_data:/data
+    restart: "no"
 
-  #================================================================
-
-# Start Keycloak
-
-  #----------------------------------------------------------------
   keycloak:
+    volumes:
+    - keys:/keys:ro
     image: keycloak/keycloak:25.0
-    restart: unless-stopped
-    command: ['start-dev']
+    restart: always
+    depends_on:
+      fix-keys-permissions:
+        condition: service_completed_successfully
+    command:
+    - "start-dev"
+    - "--verbose"
+    - "-Djavax.net.ssl.trustStorePassword=password"
+    - "-Djavax.net.ssl.HostnameVerifier=AllowAll"
+    - "-Djavax.net.ssl.trustStore=/keys/ca.jks"
+    - "--spi-truststore-file-hostname-verification-policy=ANY"
     environment:
-      KC_DB: postgres
-      KC_DB_URL_HOST: keycloak-db
-      KC_DB_URL_PORT: 5432
-      KC_DB_URL_DATABASE: keycloak
-      KC_DB_USERNAME: postgres
-      KC_DB_PASSWORD: changeme
-      KC_HOSTNAME: 'https://keycloak.opentdf.local:9443'
-      KC_HOSTNAME_ADMIN: 'https://keycloak.opentdf.local:9443'
-      KC_HTTP_ENABLED: 'true'
-      KC_HTTP_PORT: 8888
+      KC_PROXY: edge
+      KC_HTTP_RELATIVE_PATH: /auth
+      KC_HOSTNAME_STRICT: "false"
+      KC_HOSTNAME_STRICT_BACKCHANNEL: "false"
+      KC_HOSTNAME_STRICT_HTTPS: "false"
+      KC_HTTP_ENABLED: "true"
+      KC_HTTP_PORT: "8888"
+      KC_HTTPS_PORT: "8443"
+      KC_HTTP_MANAGEMENT_PORT: "9001"
       KEYCLOAK_ADMIN: admin
       KEYCLOAK_ADMIN_PASSWORD: changeme
-      KC_FEATURES: 'preview,token-exchange'
-      KC_HEALTH_ENABLED: 'true'
-      JAVA_OPTS_APPEND: '${JAVA_OPTS_APPEND:-}'
+      #KC_HOSTNAME_URL: http://localhost:8888/auth
+      KC_FEATURES: "preview,token-exchange"
+      KC_HEALTH_ENABLED: "true"
+      KC_HTTPS_KEY_STORE_PASSWORD: "password"
+      KC_HTTPS_KEY_STORE_FILE: "/keys/ca.jks"
+      KC_HTTPS_CERTIFICATE_FILE: "/keys/localhost.crt"
+      KC_HTTPS_CERTIFICATE_KEY_FILE: "/keys/localhost.key"
+      KC_HTTPS_CLIENT_AUTH: "request"
+      ###
+      # The following environment variable resolves SIGILL with Code 134 when running Java processes on Apple M4 chips
+      # 
+      # On Apple Silicon (M4 chip):
+      #   export JAVA_OPTS_APPEND="-XX:UseSVE=0"
+      #   docker-compose up
+      # 
+      # On other architectures:
+      #   export JAVA_OPTS_APPEND=""
+      #   docker-compose up
+      # 
+      # Or set directly: JAVA_OPTS_APPEND="-XX:UseSVE=0" docker-compose up
+      JAVA_OPTS_APPEND: "${JAVA_OPTS_APPEND:-}"
+      ###
+    # ports:
+    #   - "${KC_EXPOSE_PORT:-8443}:8443"
+    #   - "${KC_EXPOSE_PORT_HTTP:-8888}:8888"
+    #   - "${KC_EXPOSE_PORT_MGMT:-9001}:9001"
     healthcheck:
-      test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java ${JAVA_OPTS_APPEND} /tmp/HealthCheck.java http://localhost:9000/health/ready']
-      interval: 5s
+      test:
+        - CMD-SHELL
+        - |
+          [ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { 
+            public static void main(String[] args) throws java.lang.Throwable { 
+              javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); 
+              javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance(\"SSL\"); 
+              sc.init(null, new javax.net.ssl.TrustManager[]{ 
+                new javax.net.ssl.X509TrustManager() { 
+                  public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } 
+                  public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {} 
+                  public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {} 
+                } 
+              }, new java.security.SecureRandom()); 
+              javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 
+              java.net.HttpURLConnection conn = (java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection(); 
+              System.exit(java.net.HttpURLConnection.HTTP_OK == conn.getResponseCode() ? 0 : 1); 
+            } 
+          }" > /tmp/HealthCheck.java && java ${JAVA_OPTS_APPEND} /tmp/HealthCheck.java http://localhost:8888/auth 2>/dev/null
+      interval: 10s
       timeout: 10s
-      retries: 3
-      start_period: 5m
-    depends_on:
-      keycloak-db:
-        condition: service_healthy
-        restart: true
-  keycloak-db:
+      retries: 10
+      start_period: 3m
+  opentdfdb:
     image: postgres:15-alpine
-    restart: unless-stopped
+    restart: always
+    user: postgres
     environment:
-      POSTGRES_PASSWORD: changeme
       POSTGRES_USER: postgres
-      POSTGRES_DB: keycloak
+      POSTGRES_PASSWORD: changeme
+      POSTGRES_DB: opentdf
     healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U postgres"]
+      test: ["CMD-SHELL", "pg_isready"]
       interval: 5s
       timeout: 5s
       retries: 10
-      start_period: 2m
-  download-keycloak-config:
-    image: curlimages/curl:latest
+    ports:
+      - "${POSTGRES_EXPOSE_PORT:-5432}:5432"
+
+  jaeger:
+    image: jaegertracing/all-in-one:latest
+    environment:
+      COLLECTOR_OTLP_ENABLED: "true"
+    ports:
+      - "16686:16686"   # Web UI
+      - "4317:4317"     # OTLP gRPC
+      - "4318:4318"     # OTLP HTTP
+      - "14250:14250"   # Model/collector gRPC
+    profiles:
+      - tracing
+    restart: always
+
+  # Provision Keycloak with initial configuration
+  platform-provision-keycloak:
+    image: registry.opentdf.io/platform:nightly
+    command: ["provision", "keycloak", "-e", "https://keycloak.opentdf.local:9443/auth", "-f", "/configs/keycloak_data.yaml"]
+    depends_on:
+      keycloak:
+        condition: service_healthy
+      opentdfdb:
+        condition: service_healthy
+      patch-platform-config:
+        condition: service_completed_successfully
+      download-keycloak-data:
+        condition: service_completed_successfully
+      generate-keys:
+        condition: service_completed_successfully
+    volumes:
+      - configs:/configs:ro
+      - keys:/keys:ro
+    environment:
+      - OPENTDF_CONFIG_FILE=/configs/opentdf.yaml
+    restart: "no"
+    extra_hosts:
+      - "keycloak.opentdf.local:host-gateway"
+  # Prepare fixtures directory structure - create symlink to expected location
+  prepare-fixtures:
+    image: alpine:latest
     volumes:
       - configs:/configs
-    command: ['-o', '/configs/keycloak-config.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/cmd/keycloak_data.yaml']
     depends_on:
-      ensure-permissions:
+      download-fixtures:
         condition: service_completed_successfully
-  #================================================================
-
-# Provisioning Keycloak with expected realm, clients, and users
+    command:
+      - sh
+      - -c
+      - |
+        mkdir -p /configs/service/internal/fixtures
+        cd /configs
+        ln -sf /configs/service/internal/fixtures ./service
+    restart: "no"
 
-  #----------------------------------------------------------------
-  keycloak-provisioning:
+  # Add sample attributes and metadata  
+  platform-provision-fixtures:
     image: registry.opentdf.io/platform:nightly
+    command: ["provision", "fixtures", "--config-file", "/configs/opentdf.yaml"]
+    working_dir: /configs
+    depends_on:
+      platform-provision-keycloak:
+        condition: service_completed_successfully
+      opentdfdb:
+        condition: service_healthy
+      prepare-fixtures:
+        condition: service_completed_successfully
+      generate-keys:
+        condition: service_completed_successfully
     volumes:
-      - configs:/configs
+      - configs:/configs:ro
+      - keys:/keys:ro
+    restart: "no"
+
+  # Prepare CA certificates bundle with Caddy cert
+  prepare-ca-certs:
+    image: alpine:latest
+    volumes:
+      - type: volume
+        source: caddy_data
+        target: /caddy-certs
+        read_only: true
+        volume:
+          subpath: caddy/certificates/local/keycloak.opentdf.local
+      - platform_certs:/etc/ssl/certs
+    depends_on:
+      caddy:
+        condition: service_healthy
     command:
-      [
-        'provision',
-        'keycloak',
-        '-e',
-        'http://keycloak:8888',
-        '-f',
-        '/configs/keycloak-config.yaml',
-      ]
+      - sh
+      - -c
+      - |
+        # Install ca-certificates package
+        apk add --no-cache ca-certificates
+        # Copy Caddy certificate to CA bundle
+        cp /caddy-certs/keycloak.opentdf.local.crt /usr/local/share/ca-certificates/
+        update-ca-certificates
+        # Copy the updated CA bundle to shared volume
+        cp -r /etc/ssl/certs/* /etc/ssl/certs/
+        echo "CA certificates prepared successfully"
+    restart: "no"
+
+  # Main OpenTDF Platform server
+  platform:
+    image: registry.opentdf.io/platform:nightly
+    command: ["start", "--config-file", "/configs/opentdf.yaml"]
     depends_on:
+      platform-provision-fixtures:
+        condition: service_completed_successfully
       keycloak:
         condition: service_healthy
-        restart: true
-      download-keycloak-config:
+      opentdfdb:
+        condition: service_healthy
+      generate-keys:
         condition: service_completed_successfully
-        restart: true
-  #================================================================
+      prepare-ca-certs:
+        condition: service_completed_successfully
+    ports:
+      - "8080:8080"
+    volumes:
+      - configs:/configs:ro
+      - keys:/keys:ro
+      - platform_certs:/etc/ssl/certs:ro
+    extra_hosts:
+      - "keycloak.opentdf.local:host-gateway"
+    restart: unless-stopped
 
-# Start the OpenTDF service
+  # Initialize volume permissions
+  init-volumes:
+    image: alpine:latest
+    volumes:
+      - configs:/configs
+      - keys:/keys
+    command:
+      - sh
+      - -c
+      - |
+        chmod 777 /configs /keys
+        mkdir -p /configs/service/internal/fixtures
+        chmod -R 777 /configs
+    restart: "no"
 
-  #----------------------------------------------------------------
+  # Fix keys permissions after generation
+  fix-keys-permissions:
+    image: alpine:latest
+    volumes:
+      - keys:/keys
+    depends_on:
+      generate-keys:
+        condition: service_completed_successfully
+    command:
+      - sh
+      - -c
+      - |
+        chmod -R 755 /keys
+        chmod 644 /keys/*
+    restart: "no"
+
+  # Download platform configuration file
   download-platform-config:
     image: curlimages/curl:latest
     volumes:
       - configs:/configs
-    command: ['-o', '/configs/.opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-dev.yaml']
     depends_on:
-      ensure-permissions:
+      init-volumes:
         condition: service_completed_successfully
-  modify-platform-config:
-    image: bash:latest
+    command: ['-o', '/configs/opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-example.yaml']
+    restart: "no"
+
+  # Patch platform configuration to use keycloak.opentdf.local:9443
+  patch-platform-config:
+    image: alpine:latest
     volumes:
       - configs:/configs
+    depends_on:
+      download-platform-config:
+        condition: service_completed_successfully
     command:
-      - bash
+      - sh
       - -c
       - |
-        echo "Modifying /configs/.opentdf.yaml"
-        echo "$(</configs/.opentdf.yaml )"
-        sed -i 's|kas-private.pem|/keys/kas-private.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-cert.pem|/keys/kas-cert.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-ec-private.pem|/keys/kas-ec-private.pem|g' /configs/.opentdf.yaml
-        sed -i 's|kas-ec-cert.pem|/keys/kas-ec-cert.pem|g' /configs/.opentdf.yaml
-        sed -i 's|# db:|db: |g' /configs/.opentdf.yaml
-        sed -i 's|#   host: localhost|  host: |g' /configs/.opentdf.yaml
-        sed -i 's|issuer: http://localhost:8888/auth/realms/opentdf|issuer: http://keycloak:8888/realms/opentdf|g' /configs/.opentdf.yaml
-        sed -i 's|tokenendpoint: http://localhost:8888/auth/realms/opentdf/protocol/openid-connect/token|tokenendpoint: http://keycloak:8888/realms/opentdf/protocol/openid-connect/token|g' /configs/.opentdf.yaml
-        sed -i 's|url: http://localhost:8888/auth|url: http://keycloak:8888|g' /configs/.opentdf.yaml
-        echo "$(</configs/.opentdf.yaml )"
+        apk add --no-cache sed
+        sed -i 's|http://keycloak:8888|https://keycloak.opentdf.local:9443|g' /configs/opentdf.yaml
+        echo "Patched opentdf.yaml to use keycloak.opentdf.local:9443"
+    restart: "no"
+
+  # Download Keycloak provisioning data
+  download-keycloak-data:
+    image: curlimages/curl:latest
+    volumes:
+      - configs:/configs
     depends_on:
-      download-platform-config:
+      init-volumes:
         condition: service_completed_successfully
-  generate-kas-rsa-keys:
-    image: alpine/openssl
-    volumes:
-      - keys:/keys
-    entrypoint: ["/bin/sh", "-c"]
+    entrypoint: /bin/sh
     command:
+      - -c
       - |
-        echo "Generating RSA keys"
-        openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=kas" -keyout /keys/kas-private.pem -out /keys/kas-cert.pem -days 365
-        chmod 444 /keys/kas-private.pem
-        chmod 444 /keys/kas-cert.pem
+        URL='https://raw.githubusercontent.com/opentdf/platform/main/service/cmd/keycloak_data.yaml'
+        OUTPUT='/configs/keycloak_data.yaml'
+        MAX_ATTEMPTS=3
+        
+        for i in $$(seq 1 $$MAX_ATTEMPTS); do
+          echo "Attempt $$i of $$MAX_ATTEMPTS: Downloading keycloak_data.yaml..."
+          
+          if curl -f -o "$$OUTPUT" "$$URL"; then
+            echo "Download successful"
+            
+            # Validate the downloaded file
+            if [ -f "$$OUTPUT" ] && [ -s "$$OUTPUT" ]; then
+              if head -1 "$$OUTPUT" | grep -q -E '^(---|\w+:)'; then
+                echo "Validation passed: File exists, non-empty, and appears to be valid YAML"
+                exit 0
+              else
+                echo "Validation failed: File does not appear to be valid YAML"
+                rm -f "$$OUTPUT"
+              fi
+            else
+              echo "Validation failed: File is missing or empty"
+            fi
+          else
+            echo "Download failed (attempt $$i)"
+          fi
+          
+          if [ $$i -lt $$MAX_ATTEMPTS ]; then
+            echo "Retrying in 2 seconds..."
+            sleep 2
+          fi
+        done
+        
+        echo "ERROR: Failed to download and validate keycloak_data.yaml after $$MAX_ATTEMPTS attempts"
+        exit 1
+    restart: "no"
+
+  # Download fixtures data
+  download-fixtures:
+    image: curlimages/curl:latest
+    volumes:
+      - configs:/configs
     depends_on:
-      ensure-permissions:
+      init-volumes:
         condition: service_completed_successfully
-  generate-kas-ec-keys:
-    image: alpine/openssl
+    command: ['-o', '/configs/service/internal/fixtures/policy_fixtures.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/internal/fixtures/policy_fixtures.yaml']
+    restart: "no"
+
+  # Download init-temp-keys script
+  download-init-script:
+    image: curlimages/curl:latest
     volumes:
-      - keys:/keys
-    entrypoint: ["/bin/sh", "-c"]
-    command:
-      - |
-        echo "Generating EC keys"
-        openssl ecparam -name secp256r1 -out /keys/secp256r1.pem && \
-        openssl req -x509 -nodes -newkey ec:/keys/secp256r1.pem -subj "/CN=kas" -keyout /keys/kas-ec-private.pem -out /keys/kas-ec-cert.pem -days 365
-        chmod 444 /keys/kas-ec-private.pem
-        chmod 444 /keys/kas-ec-cert.pem
+      - configs:/configs
     depends_on:
-      ensure-permissions:
+      init-volumes:
         condition: service_completed_successfully
-  platform:
-    image: registry.opentdf.io/platform:nightly
+    entrypoint: /bin/sh
+    command:
+      - -c
+      - |
+        URL='https://raw.githubusercontent.com/opentdf/platform/main/.github/scripts/init-temp-keys.sh'
+        OUTPUT='/configs/init-temp-keys.sh'
+        MAX_ATTEMPTS=3
+        
+        for i in $$(seq 1 $$MAX_ATTEMPTS); do
+          echo "Attempt $$i of $$MAX_ATTEMPTS: Downloading init-temp-keys.sh..."
+          
+          if curl -f -o "$$OUTPUT" "$$URL"; then
+            echo "Download successful"
+            
+            # Validate the downloaded file
+            if [ -f "$$OUTPUT" ] && [ -s "$$OUTPUT" ]; then
+              if head -1 "$$OUTPUT" | grep -q '^#!/'; then
+                echo "Validation passed: File exists, non-empty, and appears to be a shell script"
+                exit 0
+              else
+                echo "Validation failed: File does not appear to be a valid shell script"
+                rm -f "$$OUTPUT"
+              fi
+            else
+              echo "Validation failed: File is missing or empty"
+            fi
+          else
+            echo "Download failed (attempt $$i)"
+          fi
+          
+          if [ $$i -lt $$MAX_ATTEMPTS ]; then
+            echo "Retrying in 2 seconds..."
+            sleep 2
+          fi
+        done
+        
+        echo "ERROR: Failed to download and validate init-temp-keys.sh after $$MAX_ATTEMPTS attempts"
+        exit 1
+    restart: "no"
+
+  # Generate keys without Docker dependency
+  generate-keys:
+    image: alpine:latest
     volumes:
       - configs:/configs
       - keys:/keys
-      - type: volume
-        source: caddy_data
-        target: /etc/ssl/certs
-        volume:
-          subpath: caddy/certificates/local/keycloak.opentdf.local
-    extra_hosts:
-      - "keycloak.opentdf.local:host-gateway"
-    command: ['start','--config-file','/configs/.opentdf.yaml','--config-key','opentdf']
-    restart: always
-    environment:
-      OPENTDF_DB_HOST: platform-db
-      OPENTDF_DB_USER: postgres
-      OPENTDF_DB_PASSWORD: changeme
     depends_on:
-      keycloak:
-        condition: service_healthy
-        restart: true
-      keycloak-provisioning:
-        condition: service_completed_successfully
-      platform-db:
-        condition: service_healthy
-        restart: true
-      download-platform-config:
-        condition: service_completed_successfully
-      generate-kas-rsa-keys:
+      download-init-script:
         condition: service_completed_successfully
-      generate-kas-ec-keys:
+      init-volumes:
         condition: service_completed_successfully
-      modify-platform-config:
-        condition: service_completed_successfully
-      caddy:
-        condition: service_healthy
-      check-certs:
-        condition: service_completed_successfully
-  platform-db:
-    image: postgres:15-alpine
-    restart: unless-stopped
+    entrypoint: /bin/sh
+    command:
+      - -c
+      - |
+        apk add --no-cache openssl openjdk11-jre bash
+        cd /keys
+        
+        # Generate KAS RSA private key
+        openssl genpkey -algorithm RSA -out /keys/kas-private.pem -pkeyopt rsa_keygen_bits:2048
+        openssl rsa -in /keys/kas-private.pem -pubout -out /keys/kas-cert.pem
+        
+        # Generate ECC Key
+        openssl ecparam -name prime256v1 > /tmp/ecparams.tmp
+        openssl req -x509 -nodes -newkey ec:/tmp/ecparams.tmp -subj "/CN=kas" -keyout /keys/kas-ec-private.pem -out /keys/kas-ec-cert.pem -days 365
+        
+        # Generate CA
+        openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=ca" -keyout /keys/keycloak-ca-private.pem -out /keys/keycloak-ca.pem -days 365
+        
+        # Generate localhost certificate
+        printf "subjectAltName=DNS:localhost,IP:127.0.0.1" > /tmp/sanX509.conf
+        printf "[req]\ndistinguished_name=req_distinguished_name\n[req_distinguished_name]\n[alt_names]\nDNS.1=localhost\nIP.1=127.0.0.1" > /tmp/req.conf
+        openssl req -new -nodes -newkey rsa:2048 -keyout /keys/localhost.key -out /tmp/localhost.req -batch -subj "/CN=localhost" -config /tmp/req.conf
+        openssl x509 -req -in /tmp/localhost.req -CA /keys/keycloak-ca.pem -CAkey /keys/keycloak-ca-private.pem -CAcreateserial -out /keys/localhost.crt -days 3650 -sha256 -extfile /tmp/sanX509.conf
+        
+        # Generate sample user certificate
+        openssl req -new -nodes -newkey rsa:2048 -keyout /keys/sampleuser.key -out /tmp/sampleuser.req -batch -subj "/CN=sampleuser"
+        openssl x509 -req -in /tmp/sampleuser.req -CA /keys/keycloak-ca.pem -CAkey /keys/keycloak-ca-private.pem -CAcreateserial -out /keys/sampleuser.crt -days 3650
+        
+        # Convert to PKCS12
+        openssl pkcs12 -export -in /keys/keycloak-ca.pem -inkey /keys/keycloak-ca-private.pem -out /keys/ca.p12 -nodes -passout pass:password
+        
+        # Convert PKCS12 to JKS using keytool (no Docker needed)
+        keytool -importkeystore \
+          -srckeystore /keys/ca.p12 \
+          -srcstoretype PKCS12 \
+          -destkeystore /keys/ca.jks \
+          -deststoretype JKS \
+          -srcstorepass "password" \
+          -deststorepass "password" \
+          -noprompt
+        
+        echo "Keys generated successfully"
     environment:
-      POSTGRES_USER: postgres
-      POSTGRES_PASSWORD: changeme
-      POSTGRES_DB: opentdf
-    healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U postgres"]
-      interval: 5s
-      timeout: 5s
-      retries: 10
+      JAVA_OPTS_APPEND: "${JAVA_OPTS_APPEND:-}"
+    restart: "no"
+
+volumes:
+  keys:
+    name: opentdf_keys
+  configs:
+    name: opentdf_configs
+  caddy_data:
+  platform_certs:
+    name: opentdf_platform_certs
 

From 4af75f3a0f5071ef82306f0becb3800c0c9255c1 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Thu, 18 Dec 2025 16:47:25 -0500
Subject: [PATCH 5/9] pin Caddy docker image

---
 docs/getting-started/docker-compose.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index d070d4a4..938f4221 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -27,7 +27,7 @@ configs:
 
 services:
   caddy:
-    image: caddy:alpine
+    image: caddy:2.8.4-alpine
     command: ['caddy','run', '--config', '/etc/caddy/Caddyfile']
     configs:
       - source: caddy_config

From bb959b9b518e52270420d2c5537923c39fdbb42b Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Fri, 19 Dec 2025 09:17:32 -0500
Subject: [PATCH 6/9] pin alpine docker image

---
 docs/getting-started/docker-compose.yaml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index 938f4221..5cdb74f0 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -71,7 +71,7 @@ services:
     restart: "no"
 
   ensure-permissions:
-    image: alpine
+    image: alpine:3.23
     command:
     - 'sh'
     - '-c'
@@ -212,7 +212,7 @@ services:
       - "keycloak.opentdf.local:host-gateway"
   # Prepare fixtures directory structure - create symlink to expected location
   prepare-fixtures:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
@@ -248,7 +248,7 @@ services:
 
   # Prepare CA certificates bundle with Caddy cert
   prepare-ca-certs:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - type: volume
         source: caddy_data
@@ -301,7 +301,7 @@ services:
 
   # Initialize volume permissions
   init-volumes:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
       - keys:/keys
@@ -316,7 +316,7 @@ services:
 
   # Fix keys permissions after generation
   fix-keys-permissions:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - keys:/keys
     depends_on:
@@ -343,7 +343,7 @@ services:
 
   # Patch platform configuration to use keycloak.opentdf.local:9443
   patch-platform-config:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
@@ -467,7 +467,7 @@ services:
 
   # Generate keys without Docker dependency
   generate-keys:
-    image: alpine:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
       - keys:/keys

From 1d744bdff15a60a697b4bcb2cceb078a4945fa82 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Fri, 19 Dec 2025 09:23:37 -0500
Subject: [PATCH 7/9] update check-certs service to use alpine image

---
 docs/getting-started/docker-compose.yaml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index 5cdb74f0..d8db8197 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -49,7 +49,7 @@ services:
     restart: unless-stopped
 
   check-certs:
-    image: bash:latest
+    image: alpine:3.23
     volumes:
       - type: volume
         source: caddy_data
@@ -57,7 +57,7 @@ services:
         volume:
           subpath: caddy/certificates/local/keycloak.opentdf.local/
     command:
-      - bash
+      - sh
       - -c
       - |
         echo "Checking certificates"

From 30bdc3b97caa3b4315fdae32245f10f5261fc833 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Fri, 19 Dec 2025 09:31:08 -0500
Subject: [PATCH 8/9] replace curl with wget and update images to alpine for
 download services

---
 docs/getting-started/docker-compose.yaml | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index d8db8197..32798900 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -332,13 +332,13 @@ services:
 
   # Download platform configuration file
   download-platform-config:
-    image: curlimages/curl:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
       init-volumes:
         condition: service_completed_successfully
-    command: ['-o', '/configs/opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-example.yaml']
+    command: ['wget', '-O', '/configs/opentdf.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/opentdf-example.yaml']
     restart: "no"
 
   # Patch platform configuration to use keycloak.opentdf.local:9443
@@ -360,7 +360,7 @@ services:
 
   # Download Keycloak provisioning data
   download-keycloak-data:
-    image: curlimages/curl:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
@@ -377,7 +377,7 @@ services:
         for i in $$(seq 1 $$MAX_ATTEMPTS); do
           echo "Attempt $$i of $$MAX_ATTEMPTS: Downloading keycloak_data.yaml..."
           
-          if curl -f -o "$$OUTPUT" "$$URL"; then
+          if wget -O "$$OUTPUT" "$$URL"; then
             echo "Download successful"
             
             # Validate the downloaded file
@@ -408,18 +408,18 @@ services:
 
   # Download fixtures data
   download-fixtures:
-    image: curlimages/curl:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
       init-volumes:
         condition: service_completed_successfully
-    command: ['-o', '/configs/service/internal/fixtures/policy_fixtures.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/internal/fixtures/policy_fixtures.yaml']
+    command: ['wget', '-O', '/configs/service/internal/fixtures/policy_fixtures.yaml', 'https://raw.githubusercontent.com/opentdf/platform/main/service/internal/fixtures/policy_fixtures.yaml']
     restart: "no"
 
   # Download init-temp-keys script
   download-init-script:
-    image: curlimages/curl:latest
+    image: alpine:3.23
     volumes:
       - configs:/configs
     depends_on:
@@ -436,7 +436,7 @@ services:
         for i in $$(seq 1 $$MAX_ATTEMPTS); do
           echo "Attempt $$i of $$MAX_ATTEMPTS: Downloading init-temp-keys.sh..."
           
-          if curl -f -o "$$OUTPUT" "$$URL"; then
+          if wget -O "$$OUTPUT" "$$URL"; then
             echo "Download successful"
             
             # Validate the downloaded file

From 36fb4cfbcb0b7f6a7e452b7664a14c8e3752d1e9 Mon Sep 17 00:00:00 2001
From: b-long <b-long@users.noreply.github.com>
Date: Fri, 19 Dec 2025 15:05:23 -0500
Subject: [PATCH 9/9] restrict access to database port

---
 docs/getting-started/docker-compose.yaml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/docs/getting-started/docker-compose.yaml b/docs/getting-started/docker-compose.yaml
index 32798900..39f505dc 100644
--- a/docs/getting-started/docker-compose.yaml
+++ b/docs/getting-started/docker-compose.yaml
@@ -171,8 +171,6 @@ services:
       interval: 5s
       timeout: 5s
       retries: 10
-    ports:
-      - "${POSTGRES_EXPOSE_PORT:-5432}:5432"
 
   jaeger:
     image: jaegertracing/all-in-one:latest
