summaryrefslogtreecommitdiff
path: root/core/utils
diff options
context:
space:
mode:
Diffstat (limited to 'core/utils')
-rwxr-xr-xcore/utils/eg.sh27
-rwxr-xr-xcore/utils/module-docgen.sh69
-rw-r--r--core/utils/module-docgen.tr.en10
-rw-r--r--core/utils/module-docgen.tr.es10
-rwxr-xr-xcore/utils/module-template.sh76
-rw-r--r--core/utils/module-template.tr.en3
-rw-r--r--core/utils/module-template.tr.es3
-rwxr-xr-xcore/utils/shflow-check.sh100
-rw-r--r--core/utils/shflow-check.tr.en12
-rw-r--r--core/utils/shflow-check.tr.es12
-rwxr-xr-xcore/utils/shflow-doc.sh63
-rw-r--r--core/utils/shflow-doc.tr.en9
-rw-r--r--core/utils/shflow-doc.tr.es9
-rwxr-xr-xcore/utils/shflow-ssh-init.sh74
-rw-r--r--core/utils/shflow-ssh-init.tr.en12
-rw-r--r--core/utils/shflow-ssh-init.tr.es12
-rwxr-xr-xcore/utils/shflow-trust.sh90
-rw-r--r--core/utils/shflow-trust.tr.en14
-rw-r--r--core/utils/shflow-trust.tr.es14
-rwxr-xr-xcore/utils/vault-init.sh97
-rw-r--r--core/utils/vault-init.tr.en19
-rw-r--r--core/utils/vault-init.tr.es19
-rwxr-xr-xcore/utils/vault_utils.sh41
-rw-r--r--core/utils/vault_utils.tr.en2
-rw-r--r--core/utils/vault_utils.tr.es2
25 files changed, 799 insertions, 0 deletions
diff --git a/core/utils/eg.sh b/core/utils/eg.sh
new file mode 100755
index 0000000..551c9d0
--- /dev/null
+++ b/core/utils/eg.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+Qes="8J+lmiBIYXMgZW5jb250cmFkbyBlbCBodWV2byBkZSBwYXNjdWEuIMKhQnVlbiBvam8h·4pyoIE5vIHRvZG9zIGxvcyBzY3JpcHRzIHRpZW5lbiBhbG1hLi4uIHBlcm8gZXN0ZSBzw60u·8J+noCBFbCBtZWpvciBidWcgZXMgZWwgcXVlIG51bmNhIGV4aXN0acOzLg==·8J+QoyBTaEZsb3cgdGUgc2FsdWRhIGRlc2RlIGxhcyBzb21icmFzLg==·8J+TnCBMYSBhdXRvbWF0aXphY2nDs24gdGFtYmnDqW4gdGllbmUgcG9lc8OtYS4="
+Qen="8J+lmiBZb3UgZm91bmQgdGhlIEVhc3RlciBlZ2cuIFNoYXJwIGV5ZSE=·4pyoIE5vdCBhbGwgc2NyaXB0cyBoYXZlIHNvdWwuLi4gYnV0IHRoaXMgb25lIGRvZXMu·8J+noCBUaGUgYmVzdCBidWcgaXMgdGhlIG9uZSB0aGF0IG5ldmVyIGV4aXN0ZWQu·8J+QoyBTaEZsb3cgZ3JlZXRzIHlvdSBmcm9tIHRoZSBzaGFkb3dzLg==·8J+TnCBBdXRvbWF0aW9uIGhhcyBwb2V0cnkgdG9vLg=="
+vhs=([0]="448b55f2" [1]="c9f66247" [2]="154f020f" [3]="0e931208" [4]="d2e2fa57")
+
+sheg() {
+ local P="$1" ; P=$((P+1))
+ echo "――――――"
+ echo "$Qes" |awk -F '·' -v p="$P" '{print $p}' | base64 -d; echo
+ echo "$Qen" |awk -F '·' -v p="$P" '{print $p}' | base64 -d; echo
+ echo "――――――"
+}
+
+main() {
+ [[ $# -lt 1 ]] && return 0
+ local input="$1" ; local hsh
+ hsh=$(echo -n "$input" | md5sum | cut -c1-8)
+ for n in "${!vhs[@]}"; do
+ if [[ "$hsh" == "${vhs[$n]}" ]]; then
+ sheg "$n" ; break
+ fi
+ done
+ return 0
+}
+
+main "$@"
diff --git a/core/utils/module-docgen.sh b/core/utils/module-docgen.sh
new file mode 100755
index 0000000..f5461cd
--- /dev/null
+++ b/core/utils/module-docgen.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+# ShFlow Module Documentation Generator
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.4.0
+
+set -euo pipefail
+
+# 🧭 Detección de la raíz del proyecto
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
+OUTPUT="$PROJECT_ROOT/docs/modules-list.md"
+MODULE_DIRS=("$PROJECT_ROOT/core/modules" "$PROJECT_ROOT/user_modules" "$PROJECT_ROOT/community_modules")
+
+export SHFLOW_LANG="${SHFLOW_LANG:-es}"
+
+# 🧩 Cargar render_msg si no está disponible
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+
+trfile="$PROJECT_ROOT/core/utils/module-docgen.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+# 📝 Encabezado del documento
+{
+ echo "${tr[title]:-# 🧩 Módulos en ShFlow}"
+ echo ""
+ echo "**$(render_msg "${tr[generated]}" "date=$(date '+%Y-%m-%d %H:%M:%S')")**"
+ echo ""
+ echo "| ${tr[col_module]:-Módulo} | ${tr[col_desc]:-Descripción} | ${tr[col_type]:-Tipo} | ${tr[col_author]:-Autor} | ${tr[col_version]:-Versión} | ${tr[col_deps]:-Dependencias} |"
+ echo "|--------|-------------|------|-------|---------|--------------|"
+} > "$OUTPUT"
+
+# 🔁 Procesar módulos
+for dir in "${MODULE_DIRS[@]}"; do
+ [ -d "$dir" ] || continue
+ TYPE=$(echo "$dir" | sed "s#$PROJECT_ROOT/##g")
+ while IFS= read -r -d '' file; do
+ name=$(basename "$file" .sh)
+ desc=$(grep -E '^# Description:' "$file" | sed 's/^# Description:[[:space:]]*//')
+ author=$(grep -E '^# Author:' "$file" | sed 's/^# Author:[[:space:]]*//')
+ version=$(grep -E '^# Version:' "$file" | sed 's/^# Version:[[:space:]]*//')
+ deps=$(grep -E '^# Dependencies:' "$file" | sed 's/^# Dependencies:[[:space:]]*//')
+
+ # Asegurar valor minimo
+ name=${name:-""}
+ desc=${desc:-""}
+ author=${author:-""}
+ version=${version:-""}
+ deps=${deps:-""}
+
+ [[ -z "$name" ]] && continue
+
+ echo "| $name | $desc | $TYPE | $author | $version | $deps |" >> "$OUTPUT"
+ done < <(find "$dir" -type f -name "*.sh" -print0)
+done
+
+# 📌 Pie de página
+{
+ echo ""
+ echo "${tr[footer]:-_Para actualizar esta tabla, ejecuta: \`module-docgen\`_}"
+} >> "$OUTPUT"
+
+echo "$(render_msg "${tr[done]}" "path=$OUTPUT")"
diff --git a/core/utils/module-docgen.tr.en b/core/utils/module-docgen.tr.en
new file mode 100644
index 0000000..644b2c3
--- /dev/null
+++ b/core/utils/module-docgen.tr.en
@@ -0,0 +1,10 @@
+title=# 🧩 Modules in ShFlow
+generated=Automatically generated on {date}
+col_module=Module
+col_desc=Description
+col_type=Type
+col_author=Author
+col_version=Version
+col_deps=Dependencies
+footer=_To update this table, run: `module-docgen`_
+done=✅ Documentation generated at {path}
diff --git a/core/utils/module-docgen.tr.es b/core/utils/module-docgen.tr.es
new file mode 100644
index 0000000..14b68fa
--- /dev/null
+++ b/core/utils/module-docgen.tr.es
@@ -0,0 +1,10 @@
+title=# 🧩 Módulos en ShFlow
+generated=Generado automáticamente el {date}
+col_module=Módulo
+col_desc=Descripción
+col_type=Tipo
+col_author=Autor
+col_version=Versión
+col_deps=Dependencias
+footer=_Para actualizar esta tabla, ejecuta: `module-docgen`_
+done=✅ Documentación generada en {path}
diff --git a/core/utils/module-template.sh b/core/utils/module-template.sh
new file mode 100755
index 0000000..a38624b
--- /dev/null
+++ b/core/utils/module-template.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# ShFlow Module Template Generator
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.1.0
+
+set -euo pipefail
+
+# 📁 Rutas defensivas
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
+MODULE_NAME="${1:-}"
+MODULE_DIR="$PROJECT_ROOT/core/modules"
+MODULE_FILE="$MODULE_DIR/$MODULE_NAME.sh"
+
+# 🧩 Cargar render_msg si no está disponible
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+trfile="$PROJECT_ROOT/core/utils/module-template.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+# 🧪 Validar entrada
+if [[ -z "$MODULE_NAME" ]]; then
+ echo "${tr[usage]:-❌ Uso: module-template.sh <nombre_modulo>}"
+ exit 1
+fi
+
+if [[ -f "$MODULE_FILE" ]]; then
+ echo "$(render_msg "${tr[exists]}" "name=$MODULE_NAME" "dir=$MODULE_DIR")"
+ exit 1
+fi
+
+mkdir -p "$MODULE_DIR"
+
+cat > "$MODULE_FILE" <<EOF
+#!/bin/bash
+# Module: $MODULE_NAME
+# Description: <descripción breve del módulo>
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.0
+# Dependencies: <comandos externos si aplica>
+
+${MODULE_NAME}_task() {
+ local host="\$1"; shift
+ declare -A args
+ for arg in "\$@"; do
+ key="\${arg%%=*}"
+ value="\${arg#*=}"
+ args["\$key"]="\$value"
+ done
+
+ echo "🚧 Ejecutando módulo '$MODULE_NAME' en \$host"
+ # Aquí va la lógica principal
+}
+
+check_dependencies_$MODULE_NAME() {
+ # Verifica herramientas necesarias
+ for cmd in <comando1> <comando2>; do
+ if ! command -v "\$cmd" &> /dev/null; then
+ echo " ❌ [$MODULE_NAME] Falta: \$cmd"
+ return 1
+ fi
+ done
+ echo " ✅ [$MODULE_NAME] Dependencias OK"
+ return 0
+}
+EOF
+
+chmod +x "$MODULE_FILE"
+echo "$(render_msg "${tr[created]}" "name=$MODULE_NAME" "path=$MODULE_FILE")"
diff --git a/core/utils/module-template.tr.en b/core/utils/module-template.tr.en
new file mode 100644
index 0000000..bc948d7
--- /dev/null
+++ b/core/utils/module-template.tr.en
@@ -0,0 +1,3 @@
+usage=❌ Usage: module-template.sh <module_name>
+exists=⚠️ Module '{name}' already exists in {dir}
+created=✅ Module '{name}' created at {path}
diff --git a/core/utils/module-template.tr.es b/core/utils/module-template.tr.es
new file mode 100644
index 0000000..4dc6e63
--- /dev/null
+++ b/core/utils/module-template.tr.es
@@ -0,0 +1,3 @@
+usage=❌ Uso: module-template.sh <nombre_modulo>
+exists=⚠️ El módulo '{name}' ya existe en {dir}
+created=✅ Módulo '{name}' creado en {path}
diff --git a/core/utils/shflow-check.sh b/core/utils/shflow-check.sh
new file mode 100755
index 0000000..130eca0
--- /dev/null
+++ b/core/utils/shflow-check.sh
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+# ShFlow Environment Checker
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.5.0
+
+set -e
+
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
+
+MODULE_PATHS=(
+ "$PROJECT_ROOT/core/modules"
+ "$PROJECT_ROOT/user_modules"
+ "$PROJECT_ROOT/community_modules"
+)
+
+# 🧩 Cargar funciones comunes si no están disponibles
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+GLOBAL_TOOLS=("bash" "ssh" "scp" "git" "curl" "jq" "yq" "gpg")
+
+REQUIRED_PATHS=(
+ "$PROJECT_ROOT/core/modules"
+ "$PROJECT_ROOT/core/utils"
+ "$PROJECT_ROOT/core/inventory"
+ "$PROJECT_ROOT/examples"
+ "$PROJECT_ROOT/user_modules"
+ "$PROJECT_ROOT/community_modules"
+ "$PROJECT_ROOT/shflow.sh"
+ "$PROJECT_ROOT/vault.sh"
+)
+
+# 🌐 Cargar traducciones
+lang="${shflow_vars[language]:-es}"
+trfile="$PROJECT_ROOT/core/utils/shflow-check.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+check_global_tools() {
+ echo "${tr[tools_header]:-🔍 Verificando herramientas globales...}"
+ local missing=0
+ for tool in "${GLOBAL_TOOLS[@]}"; do
+ if ! command -v "$tool" &> /dev/null; then
+ echo "$(render_msg "${tr[tool_missing]}" "tool=$tool")"
+ missing=1
+ else
+ echo "$(render_msg "${tr[tool_ok]}" "tool=$tool")"
+ fi
+ done
+ return $missing
+}
+
+check_structure() {
+ echo ""
+ echo "${tr[structure_header]:-📁 Verificando estructura de ShFlow...}"
+ local missing=0
+ for path in "${REQUIRED_PATHS[@]}"; do
+ if [ ! -e "$path" ]; then
+ echo "$(render_msg "${tr[path_missing]}" "path=$path")"
+ missing=1
+ else
+ echo "$(render_msg "${tr[path_ok]}" "path=$path")"
+ fi
+ done
+ return $missing
+}
+
+load_and_check_modules() {
+ echo ""
+ echo "${tr[modules_header]:-🔍 Verificando módulos ShFlow...}"
+ for dir in "${MODULE_PATHS[@]}"; do
+ [ -d "$dir" ] || continue
+ while IFS= read -r -d '' mod; do
+ source "$mod"
+ done < <(find "$dir" -type f -name "*.sh" -print0)
+ done
+
+ for func in $(declare -F | awk '{print $3}' | grep '^check_dependencies_'); do
+ echo ""
+ echo "$(render_msg "${tr[checking_func]}" "func=$func")"
+ $func || echo "$(render_msg "${tr[func_warn]}" "func=$func")"
+ done
+}
+
+main() {
+ echo "${tr[title]:-🧪 ShFlow Environment Check}"
+ echo "${tr[separator]:-=============================}"
+
+ check_global_tools
+ check_structure
+ load_and_check_modules
+
+ echo ""
+ echo "${tr[done]:-✅ Verificación completada.}"
+}
+
+main "$@"
diff --git a/core/utils/shflow-check.tr.en b/core/utils/shflow-check.tr.en
new file mode 100644
index 0000000..6430ffd
--- /dev/null
+++ b/core/utils/shflow-check.tr.en
@@ -0,0 +1,12 @@
+title=🧪 ShFlow Environment Check
+separator=_____________________________
+tools_header=🔍 Checking global tools...
+tool_missing=❌ {tool} not found
+tool_ok=✅ {tool} available
+structure_header=📁 Checking ShFlow structure...
+path_missing=❌ Missing: {path}
+path_ok=✅ Found: {path}
+modules_header=🔍 Checking ShFlow modules...
+checking_func=🔧 Running {func}...
+func_warn=⚠️ Incomplete dependencies in {func}
+done=✅ Check completed.
diff --git a/core/utils/shflow-check.tr.es b/core/utils/shflow-check.tr.es
new file mode 100644
index 0000000..983f318
--- /dev/null
+++ b/core/utils/shflow-check.tr.es
@@ -0,0 +1,12 @@
+title=🧪 ShFlow Environment Check
+separator=_____________________________
+tools_header=🔍 Verificando herramientas globales...
+tool_missing=❌ {tool} no encontrado
+tool_ok=✅ {tool} disponible
+structure_header=📁 Verificando estructura de ShFlow...
+path_missing=❌ Falta: {path}
+path_ok=✅ Encontrado: {path}
+modules_header=🔍 Verificando módulos ShFlow...
+checking_func=🔧 Ejecutando {func}...
+func_warn=⚠️ Dependencias incompletas en {func}
+done=✅ Verificación completada.
diff --git a/core/utils/shflow-doc.sh b/core/utils/shflow-doc.sh
new file mode 100755
index 0000000..861f1b9
--- /dev/null
+++ b/core/utils/shflow-doc.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+# ShFlow Doc Generator
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.1.0
+
+set -e
+
+# ─────────────────────────────────────────────
+# 🧭 Detección de la raíz del proyecto
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
+
+# 🧩 Cargar funciones comunes si no están disponibles
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+# 🌐 Cargar traducciones
+lang="${shflow_vars[language]:-es}"
+trfile="$PROJECT_ROOT/core/utils/shflow-doc.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+MODULE_PATHS=(
+ "$PROJECT_ROOT/core/modules"
+ "$PROJECT_ROOT/user_modules"
+ "$PROJECT_ROOT/community_modules"
+)
+
+extract_metadata() {
+ local file="$1"
+ local module desc author version deps
+
+ module=$(grep -m1 '^# Module:' "$file" | cut -d':' -f2- | xargs)
+ desc=$(grep -m1 '^# Description:' "$file" | cut -d':' -f2- | xargs)
+ author=$(grep -m1 '^# Author:' "$file" | cut -d':' -f2- | xargs)
+ version=$(grep -m1 '^# Version:' "$file" | cut -d':' -f2- | xargs)
+ deps=$(grep -m1 '^# Dependencies:' "$file" | cut -d':' -f2- | xargs)
+
+ echo "$(render_msg "${tr[module]}" "name=$module")"
+ echo "$(render_msg "${tr[desc]}" "desc=$desc")"
+ echo "$(render_msg "${tr[author]}" "author=$author")"
+ echo "$(render_msg "${tr[version]}" "version=$version")"
+ echo "$(render_msg "${tr[deps]}" "deps=$deps")"
+ echo "${tr[separator]:- ————————————————————————}"
+}
+
+main() {
+ echo "${tr[title]:-📚 ShFlow Modules Documentation}"
+ echo "${tr[separator_line]:-=================================}"
+
+ for dir in "${MODULE_PATHS[@]}"; do
+ [ -d "$dir" ] || continue
+ ROUTE=$(echo "$dir" | sed "s#$PROJECT_ROOT/##g")
+ echo -e "\n$(render_msg "${tr[section]}" "type=$ROUTE")"
+ while IFS= read -r -d '' file; do
+ extract_metadata "$file"
+ done < <(find "$dir" -type f -name "*.sh" -print0)
+ done
+}
+
+main "$@"
diff --git a/core/utils/shflow-doc.tr.en b/core/utils/shflow-doc.tr.en
new file mode 100644
index 0000000..5d3167e
--- /dev/null
+++ b/core/utils/shflow-doc.tr.en
@@ -0,0 +1,9 @@
+title=📚 ShFlow Modules Documentation
+separator_line==================================
+section=🗃️ Module Type: {type}
+module= 📦 Module: {name}
+desc= 🔧 Description: {desc}
+author= 👤 Author: {author}
+version= 📌 Version: {version}
+deps= 📎 Dependencies: {deps}
+separator= ————————————————————————
diff --git a/core/utils/shflow-doc.tr.es b/core/utils/shflow-doc.tr.es
new file mode 100644
index 0000000..b1150f7
--- /dev/null
+++ b/core/utils/shflow-doc.tr.es
@@ -0,0 +1,9 @@
+title=📚 ShFlow Modules Documentation
+separator_line==================================
+section=🗃️ Tipo de módulo: {type}
+module= 📦 Módulo: {name}
+desc= 🔧 Descripción: {desc}
+author= 👤 Autor: {author}
+version= 📌 Versión: {version}
+deps= 📎 Dependencias: {deps}
+separator= ————————————————————————
diff --git a/core/utils/shflow-ssh-init.sh b/core/utils/shflow-ssh-init.sh
new file mode 100755
index 0000000..26d5e70
--- /dev/null
+++ b/core/utils/shflow-ssh-init.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+# Utility: shflow-ssh-init
+# Description: Inicializa acceso SSH sin contraseña en los hosts del inventario
+# Author: Luis GuLo
+# Version: 0.2.0
+
+set -euo pipefail
+
+# 📁 Rutas defensivas
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+INVENTORY="$PROJECT_ROOT/core/inventory/hosts.yaml"
+TIMEOUT=5
+USER="${USER:-$(whoami)}"
+KEY="${KEY:-$HOME/.ssh/id_rsa.pub}"
+
+# 🧩 Cargar render_msg si no está disponible
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+export SHFLOW_LANG="${SHFLOW_LANG:-es}"
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+
+trfile="$PROJECT_ROOT/core/utils/shflow-ssh-init.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+echo "$(render_msg "${tr[start]}" "user=$USER")"
+echo "$(render_msg "${tr[inventory]}" "path=$INVENTORY")"
+echo "$(render_msg "${tr[key]}" "key=$KEY")"
+echo ""
+
+# 🧪 Validar dependencias
+for cmd in yq ssh ssh-copy-id; do
+ if ! command -v "$cmd" &>/dev/null; then
+ echo "$(render_msg "${tr[missing_dep]}" "cmd=$cmd")"
+ exit 1
+ fi
+done
+
+# 🔁 Extraer hosts
+HOSTS=()
+HOSTS_RAW=$(yq ".all.hosts | keys | .[]" "$INVENTORY")
+[ -z "$HOSTS_RAW" ] && echo "${tr[no_hosts]:-❌ No se encontraron hosts en el inventario.}" && exit 1
+
+while IFS= read -r line; do
+ HOSTS+=("$(echo "$line" | sed 's/^\"\(.*\)\"$/\1/')") # Eliminar comillas
+done <<< "$HOSTS_RAW"
+
+# 🔍 Evaluar cada host
+for host in "${HOSTS[@]}"; do
+ IP=$(yq -r ".all.hosts.\"$host\".ansible_host" "$INVENTORY")
+ [[ "$IP" == "null" || -z "$IP" ]] && echo "$(render_msg "${tr[missing_ip]}" "host=$host")" && continue
+
+ echo "$(render_msg "${tr[checking]}" "host=$host" "ip=$IP")"
+
+ if ssh -o BatchMode=yes -o ConnectTimeout=$TIMEOUT "$USER@$IP" 'true' &>/dev/null; then
+ echo "${tr[skip]:- 🔁 Inicialización SSH no es necesaria}"
+ continue
+ fi
+
+ echo "$(render_msg "${tr[copying]}" "user=$USER" "ip=$IP")"
+ if ssh-copy-id -i "$KEY" "$USER@$IP"; then
+ echo "${tr[success]:- ✅ Clave pública instalada correctamente}"
+ else
+ echo "${tr[fail]:- ❌ Fallo al instalar clave pública}"
+ fi
+
+ echo ""
+done
+
+echo "${tr[done]:-✅ Proceso de inicialización SSH completado}"
diff --git a/core/utils/shflow-ssh-init.tr.en b/core/utils/shflow-ssh-init.tr.en
new file mode 100644
index 0000000..1005b42
--- /dev/null
+++ b/core/utils/shflow-ssh-init.tr.en
@@ -0,0 +1,12 @@
+start=🔐 Initializing passwordless SSH access for user: {user}
+inventory=📁 Inventory: {path}
+key=🔑 Public key: {key}
+missing_dep=❌ Requires '{cmd}' installed on the system
+no_hosts=❌ No hosts found in inventory.
+missing_ip=⚠️ Host '{host}' has no ansible_host defined
+checking=🖥️ Host: {host} ({ip})
+skip= 🔁 SSH initialization not needed
+copying= 🚀 Running ssh-copy-id for {user}@{ip}
+success= ✅ Public key installed successfully
+fail= ❌ Failed to install public key
+done=✅ SSH initialization process completed
diff --git a/core/utils/shflow-ssh-init.tr.es b/core/utils/shflow-ssh-init.tr.es
new file mode 100644
index 0000000..c693c8f
--- /dev/null
+++ b/core/utils/shflow-ssh-init.tr.es
@@ -0,0 +1,12 @@
+start=🔐 Inicializando acceso SSH sin contraseña para usuario: {user}
+inventory=📁 Inventario: {path}
+key=🔑 Clave pública: {key}
+missing_dep=❌ Requiere '{cmd}' instalado en el sistema
+no_hosts=❌ No se encontraron hosts en el inventario.
+missing_ip=⚠️ Host '{host}' sin ansible_host definido
+checking=🖥️ Host: {host} ({ip})
+skip= 🔁 Inicialización SSH no es necesaria
+copying= 🚀 Ejecutando ssh-copy-id para {user}@{ip}
+success= ✅ Clave pública instalada correctamente
+fail= ❌ Fallo al instalar clave pública
+done=✅ Proceso de inicialización SSH completado
diff --git a/core/utils/shflow-trust.sh b/core/utils/shflow-trust.sh
new file mode 100755
index 0000000..bc27d94
--- /dev/null
+++ b/core/utils/shflow-trust.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+# Utility: shflow-trust
+# Description: Evalúa acceso SSH y privilegios sudo para cada host del inventario
+# Author: Luis GuLo
+# Version: 0.4.0
+
+set -euo pipefail
+
+# 📁 Rutas defensivas
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+INVENTORY="$PROJECT_ROOT/core/inventory/hosts.yaml"
+REPORT="$PROJECT_ROOT/core/inventory/trust_report.yaml"
+TIMEOUT=5
+USER="${USER:-$(whoami)}"
+
+# 🧩 Cargar render_msg si no está disponible
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+export SHFLOW_LANG="${SHFLOW_LANG:-es}"
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+
+trfile="$PROJECT_ROOT/core/utils/shflow-trust.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+echo "$(render_msg "${tr[start]}" "user=$USER")"
+echo "$(render_msg "${tr[inventory]}" "path=$INVENTORY")"
+echo "$(render_msg "${tr[report]}" "path=$REPORT")"
+echo ""
+
+# 🧪 Validar dependencia yq
+if ! command -v yq &>/dev/null; then
+ echo "$(render_msg "${tr[missing_dep]}" "cmd=yq")"
+ exit 1
+fi
+
+# 🧹 Regenerar informe
+{
+ echo "# $(render_msg "${tr[report_title]}")"
+ echo "# $(render_msg "${tr[report_date]}" "date=$(date)")"
+ echo ""
+} > "$REPORT"
+
+# 🔁 Extraer hosts
+HOSTS=()
+HOSTS_RAW=$(yq ".all.hosts | keys | .[]" "$INVENTORY")
+[ -z "$HOSTS_RAW" ] && echo "${tr[no_hosts]:-❌ No se encontraron hosts en el inventario.}" && exit 1
+
+while IFS= read -r line; do
+ HOSTS+=("$(echo "$line" | sed 's/^\"\(.*\)\"$/\1/')") # Eliminar comillas
+done <<< "$HOSTS_RAW"
+
+# 🔍 Evaluar cada host
+for host in "${HOSTS[@]}"; do
+ IP=$(yq -r ".all.hosts.\"$host\".ansible_host" "$INVENTORY")
+ [[ "$IP" == "null" || -z "$IP" ]] && echo "$(render_msg "${tr[missing_ip]}" "host=$host")" && continue
+
+ echo "$(render_msg "${tr[checking]}" "host=$host" "ip=$IP")"
+
+ if ssh -o BatchMode=yes -o ConnectTimeout=$TIMEOUT "$USER@$IP" 'echo ok' &>/dev/null; then
+ echo "${tr[ssh_ok]:- ✅ SSH: ok}"
+ SSH_STATUS="ok"
+
+ if ssh -o BatchMode=yes "$USER@$IP" 'sudo -n true' &>/dev/null; then
+ echo "${tr[sudo_ok]:- ✅ SUDO: ok}"
+ SUDO_STATUS="ok"
+ else
+ echo "${tr[sudo_pw]:- ⚠️ SUDO: requiere contraseña o no permitido}"
+ SUDO_STATUS="password_required"
+ fi
+ else
+ echo "${tr[ssh_fail]:- ❌ SSH: fallo de conexión}"
+ SSH_STATUS="failed"
+ SUDO_STATUS="unknown"
+ fi
+
+ {
+ echo "$host:"
+ echo " ip: $IP"
+ echo " ssh: $SSH_STATUS"
+ echo " sudo: $SUDO_STATUS"
+ echo ""
+ } >> "$REPORT"
+done
+
+echo "$(render_msg "${tr[done]}" "path=$REPORT")"
diff --git a/core/utils/shflow-trust.tr.en b/core/utils/shflow-trust.tr.en
new file mode 100644
index 0000000..5e1209f
--- /dev/null
+++ b/core/utils/shflow-trust.tr.en
@@ -0,0 +1,14 @@
+start=🔍 Evaluating SSH and sudo trust for user: {user}
+inventory=📁 Inventory: {path}
+report=📄 Report: {path}
+missing_dep=❌ Requires '{cmd}' (Go version) to process YAML inventory
+report_title=Trust report generated by shflow-trust
+report_date=Date: {date}
+no_hosts=❌ No hosts found in inventory.
+missing_ip=⚠️ Host '{host}' has no ansible_host defined
+checking=🖥️ Host: {host} ({ip})
+ssh_ok= ✅ SSH: ok
+ssh_fail= ❌ SSH: connection failed
+sudo_ok= ✅ SUDO: ok
+sudo_pw= ⚠️ SUDO: requires password or not allowed
+done=✅ Report completed: {path}
diff --git a/core/utils/shflow-trust.tr.es b/core/utils/shflow-trust.tr.es
new file mode 100644
index 0000000..9e930a9
--- /dev/null
+++ b/core/utils/shflow-trust.tr.es
@@ -0,0 +1,14 @@
+start=🔍 Evaluando confianza SSH y sudo para usuario: {user}
+inventory=📁 Inventario: {path}
+report=📄 Informe: {path}
+missing_dep=❌ Requiere '{cmd}' versión Go para procesar el inventario YAML
+report_title=Informe de confianza generado por shflow-trust
+report_date=Fecha: {date}
+no_hosts=❌ No se encontraron hosts en el inventario.
+missing_ip=⚠️ Host '{host}' sin ansible_host definido
+checking=🖥️ Host: {host} ({ip})
+ssh_ok= ✅ SSH: ok
+ssh_fail= ❌ SSH: fallo de conexión
+sudo_ok= ✅ SUDO: ok
+sudo_pw= ⚠️ SUDO: requiere contraseña o no permitido
+done=✅ Informe completado: {path}
diff --git a/core/utils/vault-init.sh b/core/utils/vault-init.sh
new file mode 100755
index 0000000..3055a10
--- /dev/null
+++ b/core/utils/vault-init.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# ShFlow Vault Initializer
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.3.0
+# Dependencies: gpg
+
+set -euo pipefail
+
+# 📁 Rutas defensivas
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+VAULT_DIR="$PROJECT_ROOT/core/vault"
+VAULT_KEY="${VAULT_KEY:-$HOME/.shflow.key}"
+VAULT_PUBKEY="${VAULT_PUBKEY:-$HOME/.shflow.pub}"
+
+# 🧩 Cargar render_msg si no está disponible
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+trfile="$PROJECT_ROOT/core/utils/vault-init.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+generate_key() {
+ echo "${tr[gen_key]:-🔐 Generando nueva clave simétrica...}"
+ head -c 64 /dev/urandom | base64 > "$VAULT_KEY"
+ chmod 600 "$VAULT_KEY"
+ echo "$(render_msg "${tr[key_created]}" "path=$VAULT_KEY")"
+}
+
+rotate_key() {
+ echo "${tr[rotate_start]:-🔄 Rotando clave y re-cifrando secretos...}"
+ local OLD_KEY="$VAULT_KEY.old"
+
+ cp "$VAULT_KEY" "$OLD_KEY"
+ generate_key
+
+ for file in "$VAULT_DIR"/*.gpg; do
+ key=$(basename "$file" .gpg)
+ echo "$(render_msg "${tr[recrypt]}" "key=$key")"
+ gpg --quiet --batch --yes --passphrase-file "$OLD_KEY" -d "$file" | \
+ gpg --symmetric --batch --yes --passphrase-file "$VAULT_KEY" -o "$VAULT_DIR/$key.gpg.new"
+ mv "$VAULT_DIR/$key.gpg.new" "$VAULT_DIR/$key.gpg"
+ done
+
+ echo "$(render_msg "${tr[rotate_done]}" "path=$OLD_KEY")"
+}
+
+status() {
+ echo "${tr[status_title]:-📊 Estado del Vault}"
+ echo "-------------------"
+ echo "$(render_msg "${tr[sym_key]}" "status=$( [ -f "$VAULT_KEY" ] && echo "${tr[present]}" || echo "${tr[absent]}")")"
+ echo "$(render_msg "${tr[pub_key]}" "status=$( [ -f "$VAULT_PUBKEY" ] && echo "${tr[present]}" || echo "${tr[absent]}")")"
+ echo "$(render_msg "${tr[vault_path]}" "path=$VAULT_DIR")"
+ echo "$(render_msg "${tr[secrets]}" "count=$(ls "$VAULT_DIR"/*.gpg 2>/dev/null | wc -l)")"
+ echo "$(render_msg "${tr[last_mod]}" "date=$(date -r "$VAULT_KEY" '+%Y-%m-%d %H:%M:%S' 2>/dev/null)")"
+}
+
+generate_pubkey() {
+ echo "${tr[asym_start]:-🔐 Configurando cifrado asimétrico...}"
+ echo "${tr[asym_hint]:-⚠️ Se requiere que la clave pública esté exportada previamente.}"
+ echo " gpg --export -a 'usuario@dominio' > $VAULT_PUBKEY"
+ if [ -f "$VAULT_PUBKEY" ]; then
+ echo "$(render_msg "${tr[pubkey_found]}" "path=$VAULT_PUBKEY")"
+ else
+ echo "${tr[pubkey_missing]:-❌ Clave pública no encontrada. Exporta primero con GPG.}"
+ exit 1
+ fi
+}
+
+main() {
+ case "${1:-}" in
+ --rotate)
+ [ -f "$VAULT_KEY" ] || { echo "${tr[no_key]:-❌ No existe clave actual. Ejecuta sin --rotate primero.}"; exit 1; }
+ rotate_key
+ ;;
+ --status)
+ status
+ ;;
+ --asymmetric)
+ generate_pubkey
+ ;;
+ *)
+ if [ -f "$VAULT_KEY" ]; then
+ echo "$(render_msg "${tr[key_exists]}" "path=$VAULT_KEY")"
+ else
+ generate_key
+ fi
+ ;;
+ esac
+}
+
+main "$@"
diff --git a/core/utils/vault-init.tr.en b/core/utils/vault-init.tr.en
new file mode 100644
index 0000000..8004348
--- /dev/null
+++ b/core/utils/vault-init.tr.en
@@ -0,0 +1,19 @@
+gen_key=🔐 Generating new symmetric key...
+key_created=✅ Key created at {path}
+rotate_start=🔄 Rotating key and re-encrypting secrets...
+recrypt=🔁 Re-encrypting '{key}'...
+rotate_done=✅ Rotation completed. Old key saved at {path}
+status_title=📊 Vault Status
+sym_key=🔐 Symmetric key: {status}
+pub_key=🔐 Public key: {status}
+present=✅ present
+absent=❌ absent
+vault_path=📁 Vault path: {path}
+secrets=📦 Secrets: {count}
+last_mod=🕒 Last modified: {date}
+asym_start=🔐 Setting up asymmetric encryption...
+asym_hint=⚠️ Public key must be exported beforehand.
+pubkey_found=✅ Public key found at {path}
+pubkey_missing=❌ Public key not found. Export it first using GPG.
+no_key=❌ No current key found. Run without --rotate first.
+key_exists=🔐 Key already exists at {path}
diff --git a/core/utils/vault-init.tr.es b/core/utils/vault-init.tr.es
new file mode 100644
index 0000000..8ed9644
--- /dev/null
+++ b/core/utils/vault-init.tr.es
@@ -0,0 +1,19 @@
+gen_key=🔐 Generando nueva clave simétrica...
+key_created=✅ Clave creada en {path}
+rotate_start=🔄 Rotando clave y re-cifrando secretos...
+recrypt=🔁 Re-cifrando '{key}'...
+rotate_done=✅ Rotación completada. Clave antigua guardada en {path}
+status_title=📊 Estado del Vault
+sym_key=🔐 Clave simétrica: {status}
+pub_key=🔐 Clave pública: {status}
+present=✅ presente
+absent=❌ ausente
+vault_path=📁 Ruta del vault: {path}
+secrets=📦 Secretos: {count}
+last_mod=🕒 Última modificación: {date}
+asym_start=🔐 Configurando cifrado asimétrico...
+asym_hint=⚠️ Se requiere que la clave pública esté exportada previamente.
+pubkey_found=✅ Clave pública detectada en {path}
+pubkey_missing=❌ Clave pública no encontrada. Exporta primero con GPG.
+no_key=❌ No existe clave actual. Ejecuta sin --rotate primero.
+key_exists=🔐 Clave ya existe en {path}
diff --git a/core/utils/vault_utils.sh b/core/utils/vault_utils.sh
new file mode 100755
index 0000000..702265f
--- /dev/null
+++ b/core/utils/vault_utils.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Utility: vault_utils
+# Description: Funciones para acceso seguro al vault de ShFlow
+# License: GPLv3
+# Author: Luis GuLo
+# Version: 1.1.0
+# Dependencies: gpg
+
+VAULT_DIR="${VAULT_DIR:-core/vault}"
+VAULT_KEY="${VAULT_KEY:-$HOME/.shflow.key}"
+
+# 🧩 Cargar render_msg si no está disponible
+PROJECT_ROOT="${SHFLOW_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
+COMMON_LIB="$PROJECT_ROOT/core/lib/translate_msg.sh"
+if ! declare -f render_msg &>/dev/null; then
+ [[ -f "$COMMON_LIB" ]] && source "$COMMON_LIB"
+fi
+
+# 🌐 Cargar traducciones
+lang="${SHFLOW_LANG:-es}"
+trfile="$PROJECT_ROOT/core/utils/vault_utils.tr.${lang}"
+declare -A tr
+if [[ -f "$trfile" ]]; then while IFS='=' read -r k v; do tr["$k"]="$v"; done < "$trfile"; fi
+
+get_secret() {
+ local key="$1"
+ local value
+
+ if [ ! -f "$VAULT_DIR/$key.gpg" ]; then
+ echo "$(render_msg "${tr[missing]}" "key=$key" "dir=$VAULT_DIR")"
+ return 1
+ fi
+
+ value=$(gpg --quiet --batch --yes --passphrase-file "$VAULT_KEY" -d "$VAULT_DIR/$key.gpg" 2>/dev/null)
+ if [ $? -ne 0 ]; then
+ echo "$(render_msg "${tr[decrypt_fail]}" "key=$key")"
+ return 1
+ fi
+
+ echo "$value"
+}
diff --git a/core/utils/vault_utils.tr.en b/core/utils/vault_utils.tr.en
new file mode 100644
index 0000000..413952e
--- /dev/null
+++ b/core/utils/vault_utils.tr.en
@@ -0,0 +1,2 @@
+missing=❌ [vault] Secret '{key}' not found in {dir}
+decrypt_fail=❌ [vault] Failed to decrypt '{key}'
diff --git a/core/utils/vault_utils.tr.es b/core/utils/vault_utils.tr.es
new file mode 100644
index 0000000..f81ee15
--- /dev/null
+++ b/core/utils/vault_utils.tr.es
@@ -0,0 +1,2 @@
+missing=❌ [vault] Secreto '{key}' no encontrado en {dir}
+decrypt_fail=❌ [vault] Error al descifrar '{key}'