Index: /branches/amp_4_0/platform/tools/container/.env
===================================================================
--- /branches/amp_4_0/platform/tools/container/.env	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/.env	(working copy)
@@ -2,37 +2,25 @@
 # Brand:   Array Networks (AN)
 # Product: Array Management Platform (AMP)
 # Purpose: Environment Configuration for Docker Swarm Stack
+#
+# SECRETS:
+#   All passwords are managed via Docker Swarm secrets.
+#   Run 'manage_amp.sh init-secrets' to create them on first deploy.
+#   This file contains ZERO passwords — it is safe for version control.
 # ------------------------------------------------------------------
 
-# --- OpenSearch (AMP Analytics Store) ---
-OPENSEARCH_INITIAL_ADMIN_PASSWORD=Arr@y2050
-OPENSEARCH_JWT_SECRET="Arr@y2050"
-
-# Memory Allocation:
-# By default, manage_amp.sh calculates this based on 50% of Host RAM.
-# Uncomment to override (e.g. for testing constraints).
-# OPENSEARCH_JAVA_OPTS="-Xms1g -Xmx1g"
-
-# --- TimescaleDB / Postgres (AMP Metrics Store) ---
+# --- Database Configuration ---
 POSTGRES_USER=postgres
-POSTGRES_PASSWORD=Arr@y2050
 POSTGRES_DB=postgres
+POSTGRES_DB_CM=cm
 AMP_DB_NAME=amp_ts
 AMP_DB_USER=amp_ts_user
-AMP_DB_PASSWORD=Array@123$
 
-# --- Grafana (AMP Visualization) ---
+# --- Grafana Configuration ---
 GF_SECURITY_ADMIN_USER=admin
-GF_SECURITY_ADMIN_PASSWORD=GArr@y2050
 GF_SERVER_ROOT_URL=https://localhost/monitoring/
 GF_SERVER_SERVE_FROM_SUB_PATH=true
 
-# --- Logstash (AMP Ingest Pipeline) ---
-# Memory Allocation:
-# By default, manage_amp.sh calculates this based on 25% of Host RAM.
-# Uncomment to override.
-# LOGSTASH_JAVA_OPTS="-Xms1g -Xmx1g"
-
 # --- Ports Configuration ---
 AMP_NGINX_HTTP_PORT=80
 AMP_NGINX_HTTPS_PORT=443
@@ -40,7 +28,6 @@
 AMP_TIMESCALEDB_PORT=5432
 
 # --- Network Configuration ---
-# Host IP for Nginx to reach backend on host
 HOST_BACKEND_IP=host.docker.internal
 
 # --- General Domain/IP Configuration ---
@@ -49,6 +36,16 @@
 # Uncomment to override auto-detection (e.g. for specific domain or IP).
 # AMP_DOMAIN_OR_IP=192.168.162.139
 
+# --- OpenSearch Memory Allocation ---
+# By default, manage_amp.sh calculates this based on 50% of Host RAM.
+# Uncomment to override (e.g. for testing constraints).
+# OPENSEARCH_JAVA_OPTS="-Xms1g -Xmx1g"
+
+# --- Logstash Memory Allocation ---
+# By default, manage_amp.sh calculates this based on 25% of Host RAM.
+# Uncomment to override.
+# LOGSTASH_JAVA_OPTS="-Xms1g -Xmx1g"
+
 # --- Docker Image Versions ---
 # These can be overridden to use different image versions.
 # Defaults are set in manage_amp.sh if not specified here.
@@ -63,4 +60,3 @@
 # TAG_ETCD=v3.5.17
 # TAG_HAPROXY=3.3-alpine
 # TAG_AMP_CORE=1.0.0
-
Index: /branches/amp_4_0/platform/tools/container/.env.example
===================================================================
--- /branches/amp_4_0/platform/tools/container/.env.example	(nonexistent)
+++ /branches/amp_4_0/platform/tools/container/.env.example	(working copy)
@@ -0,0 +1,61 @@
+# ------------------------------------------------------------------
+# Brand:   Array Networks (AN)
+# Product: Array Management Platform (AMP)
+# Purpose: Environment Configuration for Docker Swarm Stack
+#
+# SETUP:
+#   1. Copy this file:  cp .env.example .env
+#   2. Edit .env to match your environment (ports, domain, etc.)
+#   3. Initialize secrets: ./manage_amp.sh init-secrets
+#   4. Deploy:  ./manage_amp.sh deploy --auto
+#
+# SECRETS:
+#   Passwords are NOT stored in this file.
+#   They are managed as Docker Swarm secrets via manage_amp.sh.
+#   Run 'manage_amp.sh init-secrets' to create them interactively,
+#   or they will be auto-generated on first deploy.
+# ------------------------------------------------------------------
+
+# --- Database Configuration ---
+POSTGRES_USER=postgres
+POSTGRES_DB=postgres
+POSTGRES_DB_CM=cm
+AMP_DB_NAME=amp_ts
+AMP_DB_USER=amp_ts_user
+
+# --- Grafana Configuration ---
+GF_SECURITY_ADMIN_USER=admin
+
+# --- Ports Configuration ---
+AMP_NGINX_HTTP_PORT=80
+AMP_NGINX_HTTPS_PORT=443
+AMP_GRAFANA_PORT=3000
+AMP_TIMESCALEDB_PORT=5432
+
+# --- Network Configuration ---
+HOST_BACKEND_IP=host.docker.internal
+
+# --- General Domain/IP Configuration ---
+# The IP address or Domain Name used to access AMP from the browser.
+# This MUST match the URL in your browser to avoid Grafana auth loops.
+# Uncomment to override auto-detection.
+# AMP_DOMAIN_OR_IP=192.168.162.139
+
+# --- Memory Overrides ---
+# Uncomment to override auto-calculated values.
+# OPENSEARCH_JAVA_OPTS="-Xms1g -Xmx1g"
+# LOGSTASH_JAVA_OPTS="-Xms1g -Xmx1g"
+
+# --- Docker Image Versions ---
+# Uncomment to override defaults from manage_amp.sh.
+# TAG_OPENSEARCH=3.4.0
+# TAG_PGBOUNCER=v1.25.1-p0
+# TAG_GRAFANA=11.5.0
+# TAG_TELEGRAF=1.36.4
+# TAG_LOGSTASH=8.9.0
+# TAG_NGINX=1.25.3-alpine
+# TAG_TIMESCALEDB=latest-pg14
+# TAG_OS_DASHBOARDS=3.4.0
+# TAG_ETCD=v3.5.17
+# TAG_HAPROXY=3.3-alpine
+# TAG_AMP_CORE=1.0.0
Index: /branches/amp_4_0/platform/tools/container/.secrets.example
===================================================================
--- /branches/amp_4_0/platform/tools/container/.secrets.example	(nonexistent)
+++ /branches/amp_4_0/platform/tools/container/.secrets.example	(working copy)
@@ -0,0 +1,26 @@
+# ------------------------------------------------------------------
+# AMP Docker Secrets Configuration (TEMPLATE)
+# ------------------------------------------------------------------
+# Copy this file to .secrets and set your passwords:
+#   cp .secrets.example .secrets
+#
+# The .secrets file is NOT tracked in version control.
+# Passwords are read once during first deploy to create Docker
+# Swarm secrets. After creation, they live encrypted in Swarm's
+# Raft log and are mounted at /run/secrets/ inside containers.
+# ------------------------------------------------------------------
+
+# OpenSearch admin password (used for cluster admin access)
+OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_ME
+
+# OpenSearch JWT signing secret (used for dashboard SSO tokens)
+OPENSEARCH_JWT_SECRET=CHANGE_ME
+
+# PostgreSQL superuser password (used by Patroni, Logstash, HAProxy)
+POSTGRES_PASSWORD=CHANGE_ME
+
+# Grafana admin password (also used for Grafana's PostgreSQL database)
+GF_SECURITY_ADMIN_PASSWORD=CHANGE_ME
+
+# AMP application database password (used by amp-core, pgbouncer)
+AMP_DB_PASSWORD=CHANGE_ME
Index: /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md
===================================================================
--- /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/DEPLOYMENT.md	(working copy)
@@ -33,6 +33,28 @@
 
 Use this section if your deployment machine has **internet access**.
 
+### Step 0: Secrets Configuration (First Time Only)
+
+Before deploying, you must configure the passwords for the various services. AMP uses Docker Secrets to securely manage passwords.
+
+1. **Initialize the `.secrets` file**:
+
+   ```bash
+   ./manage_amp.sh init-secrets
+   ```
+
+   *This will interactively prompt you for the required passwords and save them to a `.secrets` file (which is git-ignored).*
+
+2. **Manual Configuration (Alternative)**:
+   If you prefer to set them manually, copy the template and edit it:
+
+   ```bash
+   cp .secrets.example .secrets
+   vi .secrets
+   ```
+
+> ⚠️ **IMPORTANT**: The `.secrets` file is used during the first deployment to create the Docker Swarm secrets. If you change passwords later, you must use `./manage_amp.sh rotate-secrets`.
+
 ### Step 1: System Preparation (All Nodes)
 
 Run on **each node** to configure firewall and system settings:
@@ -126,16 +148,11 @@
 Run these commands **once** after first deployment:
 
 ```bash
-# Initialize OpenSearch security
-./manage_amp.sh security_init
-
-# Create Grafana database
-./manage_amp.sh create_grafana_db
-
-# Import dashboards and index templates
-./manage_amp.sh configurator
+./manage_amp.sh post_deploy
 ```
 
+This waits for services to be ready and initializes security, databases, and dashboards.
+
 ### Step 7: Verify Deployment
 
 ```bash
@@ -170,7 +187,7 @@
 3. **Transfer to offline environment:**
 
    Copy these files to your air-gapped target:
-   * `amp_offline_bundle.tar.gz`
+   * `amp_offline_bundle_v1.0.0.tar.gz`
    * `tar-bootstrap.rpm`
 
 ---
@@ -188,16 +205,24 @@
 #### Step 2: Extract Bundle
 
 ```bash
-tar -xf amp_offline_bundle.tar.gz
+tar -xf amp_offline_bundle_v1.0.0.tar.gz
 cd amp_offline_bundle
 ```
 
-#### Step 3: System Preparation (All Nodes)
+#### Step 3: Load Offline Bundle (All Nodes)
 
+Perform this on **each node** to install Docker, system dependencies, and load images.
+
 ```bash
-./manage_amp.sh system_tune
+./manage_amp.sh load_offline
 ```
 
+This command automatically:
+
+* Installs Docker and system dependencies
+* Applies system tuning (firewall, sysctl)
+* Loads Docker images and pushes to local registry
+
 #### Step 4: Docker Swarm Setup
 
 **On Node 1 (Manager):**
@@ -219,36 +244,27 @@
 docker node promote <NODE_3_ID>
 ```
 
-#### Step 5: Load Offline Bundle
+#### Step 5: Configure Virtual IP (Optional)
 
 ```bash
-./manage_amp.sh load_offline
-```
-
-This installs Docker, loads images, and pushes to the local registry.
-
-#### Step 6: Configure Virtual IP (Optional)
-
-```bash
 ./manage_amp.sh vip --vip <VIP_ADDRESS> --priority 101  # Node 1
 ./manage_amp.sh vip --vip <VIP_ADDRESS> --priority 100  # Node 2
 ```
 
-#### Step 7: Deploy the Stack
+#### Step 6: Deploy the Stack
 
 ```bash
 ./manage_amp.sh deploy --auto
 ```
 
-#### Step 8: Post-Deployment Configuration
+#### Step 7: Post-Deployment Configuration
 
 ```bash
-./manage_amp.sh security_init
-./manage_amp.sh create_grafana_db
-./manage_amp.sh configurator
+```bash
+./manage_amp.sh post_deploy
 ```
 
-#### Step 9: Verify Deployment
+#### Step 8: Verify Deployment
 
 ```bash
 docker service ls
@@ -287,7 +303,10 @@
 | Service shows `0/1` replicas | Image not found | Run `./manage_amp.sh build` then `deploy --auto` |
 | `502 Bad Gateway` | Backend service down | Check `docker service logs amp_<service>` |
 | `ZONE_CONFLICT` Docker crash | Firewalld misconfiguration | Remove Docker interfaces from manual zones |
-| OpenSearch security not working | Security not initialized | Run `./manage_amp.sh security_init` |
+| OpenSearch Dashboards "503 Ready" | Basepath or Auth mismatch | Ensure `SERVER_BASEPATH` is `/visualization` and check secret sync |
+| OpenSearch "Auth Failed" | Password/Hash out of sync | Run `./manage_amp.sh security_init` to resync admin hash |
+| Grafana "SSL not enabled" | SSL Mode mismatch | Set `GF_DATABASE_SSL_MODE=disable` if backend is non-SSL |
+| amp-core "FATAL" | Database not ready yet | Check DB logs then `docker service update --force amp_amp-core` |
 
 ### Useful Commands
 
@@ -342,3 +361,27 @@
 | 9200 | OpenSearch | REST API |
 | 9300 | OpenSearch | Internal cluster transport |
 | 9993 | AMP Core | FTP service |
+
+---
+
+## Appendix C: Secrets Management
+
+AMP manages the following sensitive information via Docker Secrets:
+
+| Secret Name | Key in `.secrets` | Description |
+|-------------|--------------------|-------------|
+| `opensearch_initial_admin_password` | `OPENSEARCH_INITIAL_ADMIN_PASSWORD` | Primary admin for OpenSearch and Dashboards |
+| `pg_password` | `POSTGRES_PASSWORD` | Superuser for all PostgreSQL databases |
+| `amp_db_password` | `AMP_DB_PASSWORD` | App-level password for amp-core database |
+| `grafana_admin_password` | `GF_SECURITY_ADMIN_PASSWORD` | Admin UI credentials for Grafana |
+| `opensearch_jwt_secret` | `OPENSEARCH_JWT_SECRET` | Signing key for internal SSO tokens |
+
+### Rotating Passwords
+
+To update passwords on a live cluster:
+
+1. Run `./manage_amp.sh rotate-secrets`
+2. Provide the new values when prompted.
+3. The script will remove the old secrets and create new ones.
+4. Services will automatically restart to pick up the new secret mounts.
+5. **Crucial**: After rotating the OpenSearch password, run `./manage_amp.sh security_init` to update the internal security index hash.
Index: /branches/amp_4_0/platform/tools/container/README.md
===================================================================
--- /branches/amp_4_0/platform/tools/container/README.md	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/README.md	(working copy)
@@ -39,15 +39,23 @@
     ./install_prerequisites.sh
     ```
 
-2. **Initialize Swarm**:
+2. **Initialize Secrets**:
 
     ```bash
+    ./manage_amp.sh init-secrets
+    ```
+
+    *This interactively prompts for passwords and creates the `.secrets` file used by Swarm.*
+
+3. **Initialize Swarm**:
+
+    ```bash
     ./manage_amp.sh init
     ```
 
     *This initializes Swarm and labels the current node as `type=storage` so databases can run on it.*
 
-3. **Generate Certificates**:
+4. **Generate Certificates**:
 
     ```bash
     ./manage_amp.sh setup
@@ -55,7 +63,7 @@
 
     *This generates self-signed certificates and places them in the `certs-vol` volume required by services.*
 
-4. **Build & Deploy**:
+5. **Build & Deploy**:
 
     ```bash
     ./manage_amp.sh build
@@ -67,15 +75,15 @@
     > [!NOTE]
     > **GUI Build Requirement**: The `build` command compiles the Angular GUI from `src/webui/webui/htdocs/new/src/gui/`. Ensure Node.js (npm) is installed on the build machine.
 
-5. **Initialize Security**:
+6. **Initialize Security Index**:
 
     ```bash
     ./manage_amp.sh security_init
     ```
 
-    *This initializes the OpenSearch security index.*
+    *This initializes the OpenSearch security index and synchronizes the admin password from your secrets.*
 
-6. **Configure OpenSearch**:
+7. **Configure OpenSearch**:
 
     ```bash
     ./manage_amp.sh configurator
@@ -83,7 +91,7 @@
 
     *This applies security roles and creates index patterns.*
 
-7. **Verify**:
+8. **Verify**:
 
     ```bash
     ./manage_amp.sh status
@@ -506,12 +514,12 @@
 
 Use the `manage_amp.sh` script for common tasks.
 
-| Action | Description |
-| :--- | :--- |
 | `setup` | Generate SSL certificates. |
+| `init-secrets` | Interactively configure passwords and secrets. |
 | `build` | Build images and push to local registry. |
 | `deploy` | Deploy the stack. |
 | `configurator` | Configure OpenSearch roles/indices. |
+| `security_init` | Initialize security index and sync admin password. |
 
 ### Configs & Secrets
 
Index: /branches/amp_4_0/platform/tools/container/manage_amp.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/manage_amp.sh	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/manage_amp.sh	(working copy)
@@ -6,8 +6,6 @@
 ACTION=${1:-status}
 STACK_NAME="amp"
 
-# Global Image Definitions
-
 export TAG_OPENSEARCH="${TAG_OPENSEARCH:-3.4.0}"
 export TAG_PGBOUNCER="${TAG_PGBOUNCER:-v1.25.1-p0}"
 export TAG_GRAFANA="${TAG_GRAFANA:-11.5.0}"
@@ -84,7 +82,6 @@
     echo "⚠️  .env file not found at $ENV_FILE"
 fi
 
-
 # Function: Generate dynamic stack.yml from template and nodes list
 generate_stack_yaml() {
     local nodes_list="$1" # Format: "hostname:ip hostname:ip ..."
@@ -169,9 +166,6 @@
         sed -i "s|\${ENTRYPOINT_CONFIG}|$config_name|g" "$output_file"
     fi
 
-    
-
-
     echo "✅ Generated $output_file"
 }
 
@@ -198,7 +192,6 @@
     local master_nodes=""
     local etcd_hosts=""
     local db_hosts_env=""
-    local db_hosts_env=""
     local db_hosts_csv=""
     local os_hosts_json=""
     local os_url_list=""
@@ -230,7 +223,12 @@
 
         # OpenSearch Dashboards (JSON Array)
         if [ -n "$os_hosts_json" ]; then os_hosts_json+=", "; fi
-        os_hosts_json+="\"https://${node_ip}:9200\""
+
+        if [[ "$OSTYPE" == "darwin"* ]]; then
+            os_hosts_json+="\"https://host.docker.internal:9200\""
+        else
+            os_hosts_json+="\"https://${node_ip}:9200\""
+        fi
 
         # Logstash Output (List injection)
         if [ -n "$os_url_list" ]; then os_url_list+=", "; fi
@@ -415,7 +413,8 @@
     if [ ! -d "$LOG_DIR" ]; then
         echo "Creating log directory: $LOG_DIR"
         sudo mkdir -p "$LOG_DIR"
-        sudo chmod 777 "$LOG_DIR"
+        sudo chown -R 1000:1000 "$LOG_DIR"
+        sudo chmod 0755 "$LOG_DIR"
         echo "✅ Created $LOG_DIR"
     else
         echo "✅ Log directory already exists: $LOG_DIR"
@@ -1088,27 +1087,184 @@
     echo "✅ GUI built and copied to $GUI_DIST"
     cd "$SCRIPT_DIR"
 }
+# Generate a cryptographically random password
+generate_password() {
+    local length="${1:-24}"
+    if command -v openssl &>/dev/null; then
+        openssl rand -base64 "$length" | tr -dc 'A-Za-z0-9@#%^' | head -c "$length"
+    else
+        cat /dev/urandom | tr -dc 'A-Za-z0-9@#%^' | head -c "$length"
+    fi
+}
 
-create_secrets() {
-    echo "--- strict Checking/Creating Secrets ---"
-    
-    # Helper to create secret if missing
-    create_secret_if_missing() {
-        SECRET_NAME=$1
-        SECRET_VALUE=$2
-        if ! docker secret ls | grep -q "$SECRET_NAME"; then
-            printf "$SECRET_VALUE" | docker secret create $SECRET_NAME -
-            echo "Created secret: $SECRET_NAME"
-        else
-            echo "Secret $SECRET_NAME exists."
-        fi
-    }
+# Secret mapping: ENV_VAR_NAME:docker_secret_name:description
+SECRET_MAP=(
+    "OPENSEARCH_INITIAL_ADMIN_PASSWORD:opensearch_initial_admin_password:OpenSearch Admin"
+    "POSTGRES_PASSWORD:pg_password:PostgreSQL Superuser"
+    "OPENSEARCH_JWT_SECRET:opensearch_jwt_secret:OpenSearch JWT Secret"
+    "GF_SECURITY_ADMIN_PASSWORD:grafana_admin_password:Grafana Admin"
+    "AMP_DB_PASSWORD:amp_db_password:AMP Application DB"
+)
 
-    create_secret_if_missing "opensearch_initial_admin_password" "${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-admin}"
-    create_secret_if_missing "pg_password" "${POSTGRES_PASSWORD:-postgres}"
-    create_secret_if_missing "opensearch_jwt_secret" "${OPENSEARCH_JWT_SECRET:-supersecretjwtkey}"
+# Load .secrets file — auto-generates if missing
+load_secrets_file() {
+    local SECRETS_FILE="$SCRIPT_DIR/.secrets"
+    local SECRETS_EXAMPLE="$SCRIPT_DIR/.secrets.example"
+
+    if [ ! -f "$SECRETS_FILE" ]; then
+        echo "⚠️  .secrets file not found. Auto-generating with random passwords..."
+        
+        if [ -f "$SECRETS_EXAMPLE" ]; then
+            cp "$SECRETS_EXAMPLE" "$SECRETS_FILE"
+        else
+            # Create from scratch
+            cat > "$SECRETS_FILE" <<'SECRETS_TEMPLATE'
+# AMP Docker Secrets (auto-generated)
+OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_ME
+OPENSEARCH_JWT_SECRET=CHANGE_ME
+POSTGRES_PASSWORD=CHANGE_ME
+GF_SECURITY_ADMIN_PASSWORD=CHANGE_ME
+AMP_DB_PASSWORD=CHANGE_ME
+SECRETS_TEMPLATE
+        fi
+        
+        # Replace CHANGE_ME with random passwords
+        for entry in "${SECRET_MAP[@]}"; do
+            local env_var="${entry%%:*}"
+            local random_pw=$(generate_password 24)
+            sed -i.bak "s|^${env_var}=CHANGE_ME|${env_var}=${random_pw}|" "$SECRETS_FILE"
+        done
+        rm -f "${SECRETS_FILE}.bak"
+        
+        chmod 600 "$SECRETS_FILE"
+        echo "✅ Generated .secrets with random passwords"
+        echo "   Location: $SECRETS_FILE"
+        echo "   Edit this file to set custom passwords before first deploy."
+        echo ""
+    fi
+
+    # Restrict permissions (owner read/write only)
+    chmod 600 "$SECRETS_FILE"
+    
+    # Source the secrets
+    set -a
+    source "$SECRETS_FILE"
+    set +a
+}
+
+# Create Docker secrets from .secrets file (non-interactive)
+# Called automatically during 'deploy'
+create_secrets() {
+    echo "--- Checking/Creating Docker Secrets ---"
+    
+    load_secrets_file
+    
+    local created=0
+    for entry in "${SECRET_MAP[@]}"; do
+        local env_var="${entry%%:*}"
+        local remainder="${entry#*:}"
+        local secret_name="${remainder%%:*}"
+        local description="${remainder#*:}"
+        local secret_value="${!env_var}"
+        
+        if [ -z "$secret_value" ]; then
+            echo "⚠️  $env_var is empty in .secrets — generating random password"
+            secret_value=$(generate_password 24)
+        fi
+        
+        if ! docker secret ls --format '{{.Name}}' | grep -q "^${secret_name}$"; then
+            printf "%s" "$secret_value" | docker secret create "$secret_name" - >/dev/null
+            echo "✅ Created secret: $secret_name ($description)"
+            created=$((created + 1))
+        else
+            echo "ℹ️  Secret $secret_name already exists ($description)"
+        fi
+    done
+    
+    echo "--- Secrets: $created created, $((${#SECRET_MAP[@]} - created)) existing ---"
 }
 
+# Standalone secret initialization from .secrets file
+# Run: ./manage_amp.sh init-secrets
+init_secrets() {
+    echo "=== Docker Secrets Initialization ==="
+    echo ""
+    
+    check_swarm
+    
+    local SECRETS_FILE="$SCRIPT_DIR/.secrets"
+    
+    # Warn about CHANGE_ME placeholders
+    if [ -f "$SECRETS_FILE" ] && grep -q "CHANGE_ME" "$SECRETS_FILE"; then
+        echo "⚠️  .secrets file contains CHANGE_ME placeholders."
+        echo "   Random passwords will be generated for those entries."
+        echo ""
+    fi
+    
+    create_secrets
+    
+    echo ""
+    echo "Next step: ./manage_amp.sh deploy --auto"
+}
+
+# Rotate Docker secrets from updated .secrets file (non-interactive)
+# Run: ./manage_amp.sh rotate-secrets [secret_name]
+rotate_secrets() {
+    local target_secret="$1"
+    
+    echo "=== Docker Secrets Rotation ==="
+    echo ""
+    echo "Reading new passwords from: $SCRIPT_DIR/.secrets"
+    echo ""
+    
+    check_swarm
+    load_secrets_file
+    
+    # Check if stack is running
+    if docker service ls -q 2>/dev/null | head -1 | grep -q .; then
+        echo "⚠️  Stack appears to be running. Remove it first:"
+        echo "   ./manage_amp.sh rm"
+        echo ""
+        echo "Then re-run: ./manage_amp.sh rotate-secrets"
+        exit 1
+    fi
+    
+    local rotated=0
+    for entry in "${SECRET_MAP[@]}"; do
+        local env_var="${entry%%:*}"
+        local remainder="${entry#*:}"
+        local secret_name="${remainder%%:*}"
+        local description="${remainder#*:}"
+        local secret_value="${!env_var}"
+        
+        # If specific target given, skip others
+        if [ -n "$target_secret" ] && [ "$secret_name" != "$target_secret" ]; then
+            continue
+        fi
+        
+        if [ -z "$secret_value" ]; then
+            echo "⚠️  $env_var is empty in .secrets — skipping $secret_name"
+            continue
+        fi
+        
+        # Remove old secret if it exists
+        if docker secret ls --format '{{.Name}}' | grep -q "^${secret_name}$"; then
+            docker secret rm "$secret_name" >/dev/null 2>&1
+            echo "🗑️  Removed old: $secret_name"
+        fi
+        
+        # Create new
+        printf "%s" "$secret_value" | docker secret create "$secret_name" - >/dev/null
+        echo "✅ Rotated: $secret_name ($description)"
+        rotated=$((rotated + 1))
+    done
+    
+    echo ""
+    echo "--- Rotated $rotated secret(s) ---"
+    echo ""
+    echo "Next step: ./manage_amp.sh deploy --auto"
+}
+
 create_cert_configs() {
     echo "--- Creating Certificate Configs from Volume ---"
     
@@ -1186,7 +1342,6 @@
 
     # 2. Check if Certificates Exist
     echo "Checking for existing certificates..."
-    echo "Checking for existing certificates..."
     if docker run --rm -v certs-vol:/certs ${REGISTRY:-127.0.0.1:5000}/amp/busybox:${TAG_BUSYBOX} sh -c '[ -z "$(ls -A /certs)" ]'; then
         echo "⚠️  Certificates not found in 'certs-vol'. Auto-running setup..."
         run_setup
@@ -1295,8 +1450,6 @@
         -v "security-config-vol:/security-config:rw" \
         -e OPENSEARCH_INITIAL_ADMIN_PASSWORD="${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-admin}" \
         -e OPENSEARCH_JWT_SECRET="${OPENSEARCH_JWT_SECRET:-supersecretjwtkey}" \
-        -e OPENSEARCH_INITIAL_ADMIN_PASSWORD="${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-admin}" \
-        -e OPENSEARCH_JWT_SECRET="${OPENSEARCH_JWT_SECRET:-supersecretjwtkey}" \
         ${REGISTRY:-127.0.0.1:5000}/amp/rocky:${TAG_ROCKY} \
         /bin/sh -c "dnf install -y openssl httpd-tools && chmod +x /setup/setup.sh && /setup/setup.sh /certs /security-config && chown -R 1000:1000 /certs && chown -R 1000:1000 /security-config" &
     
@@ -1309,6 +1462,10 @@
 
 security_init() {
     echo "--- Initializing OpenSearch Security Index ---"
+    
+    # Load secrets to ensure we have the current password
+    load_secrets_file
+    
     echo "Waiting for OpenSearch container to start..."
     local retries=30
     local count=0
@@ -1328,6 +1485,30 @@
         echo "❌ OpenSearch container not found after waiting." 
         echo "Please check 'docker service ls' to ensure 'amp_opensearch' is running."
         exit 1
+    fi
+    
+    # Wait for OpenSearch Port 9200 (Service Ready)
+    echo "Waiting for OpenSearch API to be reachable..."
+    local api_retries=60
+    local api_count=0
+    local api_ready=false
+    
+    until [ "$api_ready" = true ] || [ $api_count -ge $api_retries ]; do
+         # We expect 200 OK or 401 Unauthorized (if security enabled but not init).
+         # Actually securityadmin.sh needs the transport layer, but checking 9200 confirms process is up.
+         # Using insecure curl inside container
+         if docker exec "$CONTAINER_ID" curl -s -k --max-time 2 https://localhost:9200 >/dev/null; then
+             api_ready=true
+         else
+             echo "  [$api_count/$api_retries] Waiting for OpenSearch API..."
+             sleep 5
+             api_count=$((api_count + 1))
+         fi
+    done
+    
+    if [ "$api_ready" != true ]; then
+         echo "❌ OpenSearch API not reachable after waiting."
+         exit 1
     fi
 
     echo "Found OpenSearch container: $CONTAINER_ID"
@@ -1335,12 +1516,26 @@
 
     # 1. Copy default configs to /tmp/sec-config
     # 2. Overwrite with custom configs from setup (config.yml, internal_users.yml)
-    # 3. Run securityadmin.sh
-    docker exec "$CONTAINER_ID" bash -c "
+    # 3. REGENERATE admin hash using current secret to ensure sync
+    # 4. Run securityadmin.sh
+    docker exec -e ADMIN_PASS="${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-admin}" "$CONTAINER_ID" bash -c "
       mkdir -p /tmp/sec-config && \
       cp /usr/share/opensearch/config/opensearch-security/* /tmp/sec-config/ && \
       cp /usr/share/opensearch/config/opensearch-security-mount/* /tmp/sec-config/ && \
+      
+      echo 'Regenerating admin hash...' && \
+      chmod +x /usr/share/opensearch/plugins/opensearch-security/tools/hash.sh && \
+      NEW_HASH=\$(/usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p \"\$ADMIN_PASS\" | tail -1) && \
+      
+      if [ -n \"\$NEW_HASH\" ]; then \
+         sed -i \"s|hash:.*|hash: \\\"\$NEW_HASH\\\"|\" /tmp/sec-config/internal_users.yml && \
+         echo '✅ Updated internal_users.yml with new hash.'; \
+      else \
+         echo '⚠️ Failed to generate hash, using existing config.'; \
+      fi && \
+
       chmod +x /usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh && \
+      unset OPENSEARCH_JAVA_OPTS && \
       /usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \
       -cd /tmp/sec-config \
       -nhnv \
@@ -1409,21 +1604,60 @@
         exit 1
     fi
     
+    # Wait for Postgres Readiness
+    echo "Waiting for Postgres to be ready..."
+    local pg_retries=60
+    local pg_count=0
+    local pg_ready=false
+    
+    until [ "$pg_ready" = true ] || [ $pg_count -ge $pg_retries ]; do
+         # Refresh container ID in case of restart
+         TS_CONTAINER=$(docker ps -q -f name=amp_timescaledb | head -n 1)
+         
+         if [ -n "$TS_CONTAINER" ]; then
+             if docker exec "$TS_CONTAINER" pg_isready -h localhost -U postgres >/dev/null 2>&1; then
+                 pg_ready=true
+                 break
+             fi
+         fi
+         
+         echo "  [$pg_count/$pg_retries] Waiting for Postgres..."
+         sleep 5
+         pg_count=$((pg_count + 1))
+    done
+    
+    if [ "$pg_ready" != true ]; then
+         echo "❌ Postgres not ready after waiting."
+         exit 1
+    fi
+    
     echo "Using container $TS_CONTAINER as psql client..."
     
-    # Get Password
-    if [ -f "services/timescaledb/patroni.yml" ]; then
-         # Try to be smart, otherwise default
-         PG_PASS="Arr@y2050"
-    else
-         PG_PASS="Arr@y2050"
+    # Get Password from .secrets (source of truth)
+    PG_PASS=""
+    if [ -f "$SCRIPT_DIR/.secrets" ]; then
+         # Extract POSTGRES_PASSWORD
+         local secret_pw=$(grep "^POSTGRES_PASSWORD=" "$SCRIPT_DIR/.secrets" | cut -d'=' -f2 | tr -d '\r')
+         if [ -n "$secret_pw" ]; then
+             PG_PASS="$secret_pw"
+         fi
     fi
+
+    if [ -z "$PG_PASS" ]; then
+         echo "❌ Error: POSTGRES_PASSWORD not found in .secrets. Cannot proceed with Grafana DB creation."
+         exit 1
+    fi
     
     # Create User and DB via HAProxy (Leader)
     # We use -h haproxy so we land on the Leader even if this specific container is a Replica.
     
-    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -c "CREATE USER grafana WITH PASSWORD '$PG_PASS';" || echo "User likely exists."
-    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -c "CREATE DATABASE grafana OWNER grafana;" || echo "DB likely exists."
+    # Create User if not exists
+    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -tc "SELECT 1 FROM pg_roles WHERE rolname = 'grafana'" | grep -q 1 || \
+    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -c "CREATE USER grafana WITH PASSWORD '$PG_PASS';"
+
+    # Create DB if not exists
+    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -tc "SELECT 1 FROM pg_database WHERE datname = 'grafana'" | grep -q 1 || \
+    docker exec -e PGPASSWORD=$PG_PASS $TS_CONTAINER psql -U postgres -h ${AMP_DOMAIN_OR_IP} -p 5433 -c "CREATE DATABASE grafana OWNER grafana;"
     
     echo "✅ Grafana Database Initialized."
 }
@@ -1602,13 +1836,24 @@
     # Copy .env if it exists
     if [ -f ".env" ]; then
         cp .env "$BUNDLE_DIR/.env"
-        echo "✅ Copied .env (preserves configuration from build machine)"
+        echo "✅ Copied .env (non-secret configuration)"
     fi
     
-    # Create final archive
-    FINAL_ARCHIVE="amp_offline_bundle.tar.gz"
+    # Copy secrets template and optionally the secrets file
+    if [ -f ".secrets.example" ]; then
+        cp .secrets.example "$BUNDLE_DIR/.secrets.example"
+        echo "✅ Copied .secrets.example (password template)"
+    fi
+    if [ -f ".secrets" ]; then
+        cp .secrets "$BUNDLE_DIR/.secrets"
+        chmod 600 "$BUNDLE_DIR/.secrets"
+        echo "✅ Copied .secrets (passwords from build machine)"
+    fi
+
+    # Create final archive
+    FINAL_ARCHIVE="amp_offline_bundle_v${TAG_AMP_CORE}.tar.gz"
     tar -czf "$FINAL_ARCHIVE" "$BUNDLE_DIR"
-    
+
     # Copy tar RPM alongside bundle for minimal installs (tar needed to extract bundle)
     TAR_RPM=$(find "$BUNDLE_DIR/packages" -name "tar-*.rpm" | head -1)
     if [ -n "$TAR_RPM" ]; then
@@ -1690,8 +1935,11 @@
         echo "⚠️  'packages' directory not found. Using standard install_prerequisites..."
         ./install_prerequisites.sh
     fi
+
+    # 2. Run System Tuning
+    system_tune
     
-    # 2. Load Docker Images
+    # 3. Load Docker Images
     if [ -f "images/amp_images.tar.gz" ]; then
         echo "Loading Docker Images (this may take time)..."
         gunzip -c images/amp_images.tar.gz | docker load
@@ -1776,12 +2024,28 @@
         echo "  1. Initialize Swarm (if not already): ./manage_amp.sh init"
         echo "  2. Deploy with auto-config: ./manage_amp.sh deploy --auto"
         
-    else
-        echo "❌ images/amp_images.tar.gz not found!"
-        exit 1
     fi
 }
 
+post_deploy_config() {
+    echo "========================================================="
+    echo "Executing Post-Deployment Configuration Steps..."
+    echo "========================================================="
+    
+    # 1. Initialize OpenSearch Security (Includes Wait)
+    security_init
+    
+    # 2. Create Grafana Database (Includes Wait)
+    create_grafana_db
+    
+    # 3. Configure OpenSearch Dashboards & Templates
+    run_configurator
+    
+    echo "========================================================="
+    echo "✅ Post-Deployment Configuration Complete!"
+    echo "========================================================="
+}
+
 case $ACTION in
     init)
         init_swarm "$2"  # Pass optional advertise-addr as second argument
@@ -1808,6 +2072,9 @@
     security_init)
         security_init
         ;;
+    post_deploy)
+        post_deploy_config
+        ;;
     deploy)
         if [ "$2" == "--auto" ]; then
              auto_configure
@@ -1831,6 +2098,12 @@
     system_tune)
         system_tune
         ;;
+    init-secrets)
+        init_secrets
+        ;;
+    rotate-secrets)
+        rotate_secrets "$2"
+        ;;
     rm|remove)
         rm_stack
         ;;
@@ -1838,7 +2111,11 @@
         docker stack services $STACK_NAME
         ;;
     *)
-        echo "Usage: $0 {init|build|bundle|load_offline|setup|deploy|security_init|status|configurator|system_tune|rm|vip}"
-        echo "  vip commands: ./manage_amp.sh vip --vip <IP> --priority <INT> [--interface <IFACE>]"
+        echo "Usage: $0 {init|init-secrets|build|bundle|load_offline|setup|deploy|post_deploy|security_init|status|configurator|system_tune|rotate-secrets|rm|vip}"
+        echo "  init-secrets        : Interactive password setup (before first deploy)"
+        echo "  rotate-secrets      : Rotate passwords (after stopping stack)"
+        echo "  deploy --auto       : Auto-configure cluster and deploy"
+        echo "  post_deploy         : Run all post-deployment configuration steps (Wait & Init)"
+        echo "  vip commands        : ./manage_amp.sh vip --vip <IP> --priority <INT> [--interface <IFACE>]"
         exit 1
 esac
Index: /branches/amp_4_0/platform/tools/container/services/amp-core/supervisord.conf
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/amp-core/supervisord.conf	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/amp-core/supervisord.conf	(working copy)
@@ -48,14 +48,14 @@
 ; AMP API Service - Python Backend API via Gunicorn
 ; ============================================================
 [program:amp_api]
-command=bash -c "python3 /ca/bin/wait_for_db.py && gunicorn --bind 0.0.0.0:8000 --workers 1 --threads 4 --timeout 120 djproject.wsgi:application"
+command=bash -c "python3 /ca/bin/wait_for_db.py && sleep 5 && gunicorn --bind 0.0.0.0:8000 --workers 1 --threads 4 --timeout 120 djproject.wsgi:application"
 directory=/ca/webui/htdocs/new/src
 environment=DJANGO_SETTINGS_MODULE="djproject.settings",PYTHONPATH="/ca/webui/htdocs/new/src:/ca/webui/htdocs/new/src/hive"
 autostart=true
 autorestart=true
-startsecs=5
-startretries=3
-stopwaitsecs=30
+startsecs=10
+startretries=20
+stopwaitsecs=60
 stdout_logfile=/var/log/amp/amp_api.log
 stdout_logfile_maxbytes=50MB
 stdout_logfile_backups=3
Index: /branches/amp_4_0/platform/tools/container/services/amp-core/wait_for_db.py
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/amp-core/wait_for_db.py	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/amp-core/wait_for_db.py	(working copy)
@@ -1,27 +1,48 @@
 #!/usr/bin/env python3
-import socket
-import time
 import os
 import sys
+import time
 
-def wait_for_postgres():
-    host = os.environ.get('DB_HOST', 'host.docker.internal')
-    port = int(os.environ.get('DB_PORT', 5432))
+# Ensure project root is in path
+sys.path.append('/ca/webui/htdocs/new/src')
+sys.path.append('/ca/webui/htdocs/new/src/hive')
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djproject.settings")
+
+def wait_for_db():
+    print("--- wait_for_db: Initializing Django ... ---")
+    try:
+        import django
+        from django.db import connections
+        from django.db.utils import OperationalError
+        django.setup()
+    except Exception as e:
+        print(f"FATAL: Failed to setup Django: {e}")
+        sys.exit(1)
+
+    print(f"--- wait_for_db: Checking Connection to {os.environ.get('DB_HOST')} ... ---")
     
-    print(f"Waiting for database at {host}:{port}...")
-    
     start_time = time.time()
     while True:
         try:
-            with socket.create_connection((host, port), timeout=5):
-                print(f"Database {host}:{port} is ready!")
-                return True
-        except (OSError, ConnectionRefusedError):
-            if time.time() - start_time > 60:
-                print(f"Timeout waiting for database {host}:{port}")
-                sys.exit(1)
-            print("Database not ready yet, retrying in 2s...")
-            time.sleep(2)
+            conn = connections['default']
+            with conn.cursor() as cursor:
+                cursor.execute("SELECT 1")
+            print("✅ Database connection successful!")
+            sys.exit(0)
+        except OperationalError as e:
+            error_str = str(e)
+            # Print only periodically or on status change to avoid log spam? 
+            # For debugging now, print usually.
+            print(f"⚠️  Database unavailable: {error_str.splitlines()[0]}")
+        except Exception as e:
+            print(f"❌ Unexpected error: {e}")
+        
+        if time.time() - start_time > 300:
+             print("❌ Timeout waiting for database.")
+             sys.exit(1)
+             
+        time.sleep(2)
 
 if __name__ == "__main__":
-    wait_for_postgres()
+    wait_for_db()
Index: /branches/amp_4_0/platform/tools/container/services/grafana/provisioning/datasources/datasources.yaml
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/grafana/provisioning/datasources/datasources.yaml	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/grafana/provisioning/datasources/datasources.yaml	(working copy)
@@ -6,7 +6,7 @@
     url: ${GF_DATABASE_HOST} # Using shared env var for host:port (via HAProxy at 5432)
     user: ${DS_POSTGRES_USER}
     secureJsonData:
-      password: ${DS_POSTGRES_PASSWORD}
+      password: $__file{/run/secrets/pg_password}
     jsonData:
       database: cm
       sslmode: "disable"
@@ -20,7 +20,7 @@
 
     user: ${DS_AMP_DB_USER}
     secureJsonData:
-      password: ${DS_AMP_DB_PASSWORD}
+      password: $__file{/run/secrets/amp_db_password}
     jsonData:
       database: ${DS_AMP_DB_NAME}
       sslmode: "disable"
@@ -33,7 +33,8 @@
     url: ${DS_OS_URL}
     basicAuth: true
     basicAuthUser: ${DS_OS_USER}
-    basicAuthPassword: ${DS_OS_PASSWORD}
+    secureJsonData:
+      basicAuthPassword: $__file{/run/secrets/opensearch_initial_admin_password}
     jsonData:
       timeField: "@timestamp"
       tlsSkipVerify: true
Index: /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/nginx/conf.d/app.conf	(working copy)
@@ -39,6 +39,11 @@
     ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
 
     add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
+    add_header X-Frame-Options "SAMEORIGIN" always;
+    add_header X-Content-Type-Options "nosniff" always;
+    add_header X-XSS-Protection "1; mode=block" always;
+    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
 
     # Enable access logging for debugging
     access_log /var/log/nginx/access.log;
Index: /branches/amp_4_0/platform/tools/container/services/opensearch/entrypoint_wrapper.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/opensearch/entrypoint_wrapper.sh	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/opensearch/entrypoint_wrapper.sh	(working copy)
@@ -1,8 +1,8 @@
 #!/bin/bash
 set -e
-set -x # Enable Debug Mode to see exactly what fails
+# NOTE: Do NOT use 'set -x' in production — it leaks secrets to container logs
 
-echo "--- Starting OpenSearch Entrypoint Wrapper (Debug v2 - No XARGS) ---"
+echo "--- Starting OpenSearch Entrypoint Wrapper ---"
 echo "Hostname: $(hostname)"
 echo "Path: $PATH"
 
Index: /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/setup/configure_opensearch.sh	(working copy)
@@ -24,11 +24,12 @@
 cat /etc/resolv.conf
 
 # Check loop only for Dashboards (on overlay)
+# Note: nslookup might fail on hostnet environments, so failure is logged but execution continues.
 log "Testing DNS resolution for Dashboards:"
 if nslookup tasks.opensearch-dashboards >/dev/null 2>&1; then
     log "  ✅ nslookup tasks.opensearch-dashboards: SUCCESS"
 else
-    log "  ❌ nslookup tasks.opensearch-dashboards: FAILED (Required for connectivity)"
+    log "  ⚠️  nslookup tasks.opensearch-dashboards: FAILED (Expected if running on hostnet)"
 fi
 # ---------------------
 # ---------------------
@@ -100,9 +101,12 @@
 COUNT=0
 log "Waiting for OpenSearch Dashboards ($OPENSEARCH_DASHBOARDS_URL/visualization/api/status) (timeout: 300s)..."
 while [ $COUNT -lt $MAX_RETRIES ]; do
-  STATUS_RES=$(curl -s -k -u "$ADMIN_USER:$ADMIN_PASS" "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status")
-  if echo "$STATUS_RES" | grep -q '"state":"green"'; then
-      log "✅ Dashboards is Green/Ready."
+  # Capture HTTP Status and body - using -w to separate status code
+  HTTP_CODE=$(curl -s -k -o /tmp/dashboards_status.txt -w "%{http_code}" -u "$ADMIN_USER:$ADMIN_PASS" "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status")
+  STATUS_RES=$(cat /tmp/dashboards_status.txt)
+
+  if [ "$HTTP_CODE" -eq 200 ] && echo "$STATUS_RES" | grep -q '"state":"green"'; then
+      log "✅ Dashboards is Green/Ready (HTTP 200)."
       break
   fi
 
@@ -117,7 +121,8 @@
       fi
       ;;
     *)
-      CURRENT_STATE="Not JSON (Starting...)"
+      # Capture first line of non-JSON response (e.g., HTML title or error) to debug 503s
+      CURRENT_STATE="Not JSON (HTTP $HTTP_CODE): $(head -n 1 /tmp/dashboards_status.txt | cut -c 1-50)..."
       ;;
   esac
 
@@ -172,7 +177,7 @@
 # Using -w %{http_code} to catch errors and -o to avoid dumping large JSON to logs
 # Set longer timeout (300s) for first-time import which can be slow
 log "Testing connectivity to $OPENSEARCH_DASHBOARDS_URL first..."
-curl -v -k -u "$ADMIN_USER:$ADMIN_PASS" --connect-timeout 60 "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status" 2>&1 | tail -5
+curl -s -k -u "$ADMIN_USER:$ADMIN_PASS" -w "%{http_code}\n" -o /dev/null --connect-timeout 60 "$OPENSEARCH_DASHBOARDS_URL/visualization/api/status"
 
 log "Starting import (this may take several minutes)..."
 HTTP_CODE=$(curl -v -k -u "$ADMIN_USER:$ADMIN_PASS" -o /tmp/import_res.json -w "%{http_code}" \
Index: /branches/amp_4_0/platform/tools/container/services/setup/setup.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/setup/setup.sh	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/setup/setup.sh	(working copy)
@@ -97,7 +97,11 @@
 
 # --- 3. Internal Users Generation (internal_users.yml) ---
 USERS_FILE="$CONFIG_DIR/internal_users.yml"
-ADMIN_PASS="${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Arr@y2050}"
+ADMIN_PASS="${OPENSEARCH_INITIAL_ADMIN_PASSWORD}"
+if [ -z "$ADMIN_PASS" ]; then
+    log "❌ OPENSEARCH_INITIAL_ADMIN_PASSWORD not set."
+    exit 1
+fi
 log "Generating internal_users.yml with custom admin password..."
 
 # OpenSearch uses BCrypt hashes. htpasswd -B -C 12 generates this.
Index: /branches/amp_4_0/platform/tools/container/services/telegraf/telegraf.conf
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/telegraf/telegraf.conf	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/telegraf/telegraf.conf	(working copy)
@@ -32,7 +32,7 @@
   percpu = true
   totalcpu = true
 [[inputs.disk]]
-  ignore_fs = ["tmpfs", "devtmpfs", "rootfs"]
+  ignore_fs = ["tmpfs", "devtmpfs", "overlay"]
 [[inputs.mem]]
 [[inputs.diskio]]
 [[inputs.net]]
Index: /branches/amp_4_0/platform/tools/container/services/timescaledb/patroni.yml
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/timescaledb/patroni.yml	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/timescaledb/patroni.yml	(working copy)
@@ -35,8 +35,13 @@
   - encoding: UTF8
   - data-checksums
   pg_hba:
-  - host all all 0.0.0.0/0 md5
-  - host replication replica 0.0.0.0/0 md5
+  - host all all 10.0.0.0/8 scram-sha-256
+  - host all all 172.16.0.0/12 scram-sha-256
+  - host all all 192.168.0.0/16 scram-sha-256
+  - host all all 127.0.0.1/32 scram-sha-256
+  - host replication replica 10.0.0.0/8 md5
+  - host replication replica 172.16.0.0/12 md5
+  - host replication replica 192.168.0.0/16 md5
 
 postgresql:
   listen: 0.0.0.0:5433 # Listen on 5433 for Host Networking
Index: /branches/amp_4_0/platform/tools/container/services/timescaledb/post_bootstrap.sh
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/timescaledb/post_bootstrap.sh	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/services/timescaledb/post_bootstrap.sh	(working copy)
@@ -5,15 +5,26 @@
 # It expects standard PG environment variables or variables passed via proper application config.
 export PGPORT=5433
 export PGHOST=127.0.0.1
-# PGPASSWORD is implicitly used if set in environment, or we specifically set it below
-export PGPASSWORD="${PATRONI_SUPERUSER_PASSWORD}"
 
+# Read passwords from Docker secrets (preferred) or fall back to environment variables
+if [ -f /run/secrets/pg_password ]; then
+    export PGPASSWORD="$(cat /run/secrets/pg_password)"
+else
+    export PGPASSWORD="${PATRONI_SUPERUSER_PASSWORD}"
+fi
+
 echo "--- Post-Bootstrap: User Creation Started ---"
 
 # 1. Create Grafana User and Database
 GRAFANA_USER="grafana"
-# Use provided password or default
-GRAFANA_PASSWORD="${GRAFANA_PASSWORD:-GArr@y2050}"
+# Read from Docker secret (preferred) or environment variable
+if [ -f /run/secrets/grafana_admin_password ]; then
+    GRAFANA_PASSWORD="$(cat /run/secrets/grafana_admin_password)"
+else
+if [ -z "$GRAFANA_PASSWORD" ]; then
+    echo "❌ GRAFANA_PASSWORD not set (check secrets)."
+    exit 1
+fi
 GRAFANA_DB="grafana"
 
 # Check if user exists (idempotency mainly for manual reruns, normally bootstrap runs once)
@@ -21,7 +32,9 @@
     echo "User $GRAFANA_USER already exists."
 else
     echo "Creating user $GRAFANA_USER..."
-    psql -U postgres -c "CREATE USER $GRAFANA_USER WITH PASSWORD '$GRAFANA_PASSWORD';"
+    # Use standard shell interpolation for password with escaping for safety
+    SAFE_GRAFANA_PASSWORD=$(echo "$GRAFANA_PASSWORD" | sed "s/'/''/g")
+    psql -U postgres -c "CREATE USER $GRAFANA_USER WITH PASSWORD '$SAFE_GRAFANA_PASSWORD';"
 fi
 
 if psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$GRAFANA_DB'" | grep -q 1; then
@@ -35,14 +48,21 @@
 # 2. Create AMP Application User and Database
 # These variables should be passed into the container
 AMP_USER="${AMP_DB_USER:-amp_ts_user}"
-AMP_PASS="${AMP_DB_PASSWORD:-Array@123\$}"
+if [ -f /run/secrets/amp_db_password ]; then
+    AMP_PASS="$(cat /run/secrets/amp_db_password)"
+else
+if [ -z "$AMP_PASS" ]; then
+    echo "❌ AMP_DB_PASSWORD not set (check secrets)."
+    exit 1
+fi
 AMP_DB="${AMP_DB_NAME:-amp_ts}"
 
 if psql -U postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='$AMP_USER'" | grep -q 1; then
     echo "User $AMP_USER already exists."
 else
     echo "Creating user $AMP_USER..."
-    psql -U postgres -c "CREATE USER $AMP_USER WITH PASSWORD '$AMP_PASS';"
+    SAFE_AMP_PASS=$(echo "$AMP_PASS" | sed "s/'/''/g")
+    psql -U postgres -c "CREATE USER $AMP_USER WITH PASSWORD '$SAFE_AMP_PASS';"
 fi
 
 if psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$AMP_DB'" | grep -q 1; then
@@ -55,14 +75,24 @@
 # 3. Create Admin User and CM Database
 # Corresponds to pgbouncer configuration
 ADMIN_USER="amp_admin"
-ADMIN_PASS="${PATRONI_SUPERUSER_PASSWORD}" # Same as postgres superuser for simplicity in this setup
+if [ -f /run/secrets/pg_password ]; then
+    ADMIN_PASS="$(cat /run/secrets/pg_password)"
+else
+    ADMIN_PASS="${PATRONI_SUPERUSER_PASSWORD}" # Must be provided
+fi
+
+if [ -z "$ADMIN_PASS" ]; then
+    echo "❌ PATRONI_SUPERUSER_PASSWORD not set."
+    exit 1
+fi
 CM_DB="cm"
 
 if psql -U postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='$ADMIN_USER'" | grep -q 1; then
     echo "User $ADMIN_USER already exists."
 else
     echo "Creating user $ADMIN_USER..."
-    psql -U postgres -c "CREATE USER $ADMIN_USER WITH PASSWORD '$ADMIN_PASS';"
+    SAFE_ADMIN_PASS=$(echo "$ADMIN_PASS" | sed "s/'/''/g")
+    psql -U postgres -c "CREATE USER $ADMIN_USER WITH PASSWORD '$SAFE_ADMIN_PASS';"
 fi
 
 if psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$CM_DB'" | grep -q 1; then
Index: /branches/amp_4_0/platform/tools/container/stack.yml.template
===================================================================
--- /branches/amp_4_0/platform/tools/container/stack.yml.template	(revision 2933)
+++ /branches/amp_4_0/platform/tools/container/stack.yml.template	(working copy)
@@ -58,6 +58,10 @@
     external: true
   opensearch_jwt_secret:
     external: true
+  grafana_admin_password:
+    external: true
+  amp_db_password:
+    external: true
 
 services:
   # --- Core Storage (Pinned to Storage Node) ---
@@ -134,7 +138,10 @@
       - -c
       - |
         umask 0077
-        export MY_IP=$$(ip route get 1.1.1.1 | awk '{print $$7}')
+        export MY_IP="${POD_IP}"
+        if [ -z "$MY_IP" ]; then
+            export MY_IP=$$(ip route get 1.1.1.1 | awk '{print $$7}')
+        fi
         echo "✅ Detected Host IP: $$MY_IP"
         # Fix permissions for the data directory (since volume is root-owned)
         mkdir -p /var/lib/postgresql/data/db 
@@ -147,6 +154,12 @@
         export PATRONI_RESTAPI_CONNECT_ADDRESS="$$MY_IP:8008"
         export POD_IP="$$MY_IP"
         
+        # Read passwords from Docker secrets
+        export PATRONI_SUPERUSER_PASSWORD=$$(cat /run/secrets/pg_password)
+        export PATRONI_REPLICATION_PASSWORD=$$(cat /run/secrets/pg_password)
+        export AMP_DB_PASSWORD=$$(cat /run/secrets/amp_db_password)
+        export GRAFANA_PASSWORD=$$(cat /run/secrets/grafana_admin_password)
+        
         # Ensure config is readable by postgres user
         chown postgres:postgres /tmp/patroni.yml
         
@@ -157,18 +170,16 @@
       - POSTGRES_USER=${POSTGRES_USER}
       - POSTGRES_DB=${POSTGRES_DB}
       - AMP_DB_USER=${AMP_DB_USER}
-      - AMP_DB_PASSWORD=${AMP_DB_PASSWORD}
       - AMP_DB_NAME=${AMP_DB_NAME}
-      - GRAFANA_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-GArr@y2050}
-      # Patroni Config
-      - PATRONI_REPLICATION_PASSWORD=${POSTGRES_PASSWORD}
-      - PATRONI_SUPERUSER_PASSWORD=${POSTGRES_PASSWORD}
+      # Patroni reads superuser password from pg_password secret
+      # AMP_DB_PASSWORD and GRAFANA_PASSWORD read from secrets in entrypoint
       - PATRONI_NAME={{.Task.Name}}
       - POD_NAME={{.Task.Name}}
       # POD_IP removed (calculated in command)
       - PATRONI_POSTGRESQL_LISTEN=0.0.0.0:5433
       - PATRONI_RESTAPI_LISTEN=0.0.0.0:8008
       - PATRONI_ETCD3_HOSTS=${PATRONI_ETCD3_HOSTS}
+      - POD_IP=${AMP_DOMAIN_OR_IP}
     configs:
       - source: patroni_yml
         target: /etc/patroni_template.yml
@@ -183,6 +194,8 @@
         target: /docker-entrypoint-initdb.d/03_docker_metrics.sql
     secrets:
       - pg_password
+      - grafana_admin_password
+      - amp_db_password
     volumes:
       # Local volume for each instance
       - timescaledb-data:/var/lib/postgresql/data
@@ -216,9 +229,11 @@
       - HOST_ETC=/host/etc
       - AMP_DB_NAME=${AMP_DB_NAME:-amp_ts}
       - AMP_DB_USER=${AMP_DB_USER:-amp_ts_user}
-      - AMP_DB_PASSWORD=${AMP_DB_PASSWORD:-Array@123$}
       - DB_HOST=127.0.0.1
       - DB_NAME=cm
+    secrets:
+      - amp_db_password
+      - pg_password
     configs:
       - source: pgbouncer_ini
         target: /etc/pgbouncer/pgbouncer.ini
@@ -279,6 +294,8 @@
       SERVER_SSL_CERTIFICATE: /usr/share/opensearch-dashboards/config/certs/node.pem
       SERVER_BASEPATH: /visualization
       SERVER_REWRITEBASEPATH: "true"
+    extra_hosts:
+      - "host.docker.internal:host-gateway"
     configs:
       - source: cert_root-ca_pem
         target: /usr/share/opensearch-dashboards/config/certs/root-ca.pem
@@ -316,9 +333,9 @@
       - GF_DATABASE_HOST=host.docker.internal:5432 # Connects to Local Host's HAProxy via host-gateway
       - GF_DATABASE_NAME=grafana
       - GF_DATABASE_USER=grafana
-      - GF_DATABASE_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-GArr@y2050}
+      - GF_DATABASE_PASSWORD__FILE=/run/secrets/grafana_admin_password
       - GF_DATABASE_SSL_MODE=disable
-      - GF_LOG_LEVEL=debug
+      - GF_LOG_LEVEL=warn
       - GF_SERVER_ROOT_URL=https://${AMP_DOMAIN_OR_IP:-localhost}/monitoring/
       - GF_SERVER_SERVE_FROM_SUB_PATH=true
       - GF_SECURITY_COOKIE_SECURE=true
@@ -330,18 +347,20 @@
       - GF_AUTH_LOGIN_MAXIMUM_INACTIVE_LIFETIME_DAYS=30
       - GF_AUTH_LOGIN_MAXIMUM_LIFETIME_DAYS=30
       - GF_SECURITY_ADMIN_USER=admin
-      - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-GArr@y2050}
+      - GF_SECURITY_ADMIN_PASSWORD__FILE=/run/secrets/grafana_admin_password
       - GF_INSTALL_PLUGINS=grafana-opensearch-datasource
       - DS_POSTGRES_USER=${POSTGRES_USER:-postgres}
-      - DS_POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-Arr@y2050}
       - DS_AMP_DB_NAME=${AMP_DB_NAME:-amp_ts}
       - DS_AMP_DB_USER=${AMP_DB_USER:-amp_ts_user}
-      - DS_AMP_DB_PASSWORD=${AMP_DB_PASSWORD:-Array@123$}
       - DS_OS_USER=admin
-      - DS_OS_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Arr@y2050}
       - DS_OS_URL=https://${AMP_DOMAIN_OR_IP}:9200
     extra_hosts:
       - "host.docker.internal:host-gateway"
+    secrets:
+      - grafana_admin_password
+      - pg_password
+      - amp_db_password
+      - opensearch_initial_admin_password
     configs:
       - source: grafana_datasources
         target: /etc/grafana/provisioning/datasources/datasources.yaml
@@ -434,8 +453,9 @@
       mode: global  # Run on every node
       restart_policy:
         condition: on-failure
-        delay: 5s
-        max_attempts: 3
+        delay: 15s
+        window: 120s
+        max_attempts: 10
     healthcheck:
       # Check only webui_agent - backend daemonizes and exits from supervisor's view
       test: ["CMD", "supervisorctl", "status", "webui_agent"]
@@ -448,8 +468,8 @@
       - DB_PORT=5432
       - DB_NAME=${POSTGRES_DB_CM:-cm}
       - DB_USER=${POSTGRES_USER:-postgres}
-      - DB_PASSWORD=${POSTGRES_PASSWORD:-Arr@y2050}
-      - OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD:-Arr@y2050}
+      - DB_PASSWORD_FILE=/run/secrets/pg_password
+      - OPENSEARCH_PASSWORD_FILE=/run/secrets/opensearch_initial_admin_password
       - DJANGO_SETTINGS_MODULE=djproject.settings
       - PYTHONPATH=/ca/webui/htdocs/new/src
     volumes:
@@ -488,6 +508,8 @@
       - "host.docker.internal:host-gateway"
     secrets:
       - opensearch_jwt_secret
+      - pg_password
+      - opensearch_initial_admin_password
     networks:
       - hostnet
     ports:
