8 min de lectura

Comprobaciones de éxito de comandos SSH: demuestra que el trabajo remoto terminó

Las comprobaciones de éxito de comandos SSH necesitan algo más que un código de retorno cero. Aprende a validar la salida, leer el estado remoto, gestionar tiempos de espera y repetir operaciones de forma segura.

Comprobaciones de éxito de comandos SSH: demuestra que el trabajo remoto terminó

Un comando SSH que termina con el estado 0 solo ha completado una tarea concreta: el programa remoto informó a su shell de que había tenido éxito. Es una prueba útil, pero no demuestra que un despliegue llegara a la versión correcta, que un servicio siguiera funcionando o que un cambio de configuración surtiera efecto.

He visto mucha automatización declarar la victoria porque ssh host command devolvió cero, para descubrir después que el comando había escrito en el directorio equivocado, puesto en cola un trabajo que falló más tarde o reiniciado un servicio que se bloqueó de inmediato. La solución no consiste en añadir registros más optimistas. Trata una modificación remota como completada solo cuando coincidan tres hechos independientes: SSH llegó al programa remoto, el programa devolvió el resultado esperado y una lectura independiente confirma el estado que querías crear.

Un código de salida SSH informa de una sola capa de la operación

Un estado de salida informa de la finalización del proceso, no del resultado operativo completo. Un comando remoto depende de varias condiciones: conectividad DNS y de red, identidad del host, autenticación, comportamiento del shell, análisis del comando, dependencias, permisos remotos y el estado que esperabas cambiar.

El manual de OpenSSH ssh(1) indica que ssh termina con el estado del comando remoto o con 255 si se produce un error. La diferencia importa. Un estado 255 suele indicar que el cliente SSH no pudo establecer o mantener la sesión como era necesario. Un estado 1, 2 u otro valor distinto de cero normalmente procede del comando remoto. El cero también procede de ese comando.

Esa explicación no dice que cero signifique «el cambio en producción es correcto». No puede decirlo. SSH no sabe si /srv/app/current apunta a la versión que querías, si un demonio acepta solicitudes después de systemctl restart o si una migración de base de datos confirmó las filas que necesita tu aplicación.

POSIX define un estado de salida cero como la finalización correcta de un comando. Lo importante de esta definición es su alcance limitado. Describe el contrato del comando. Si tu contrato solo dice «ejecuta esta línea de shell», el cero demuestra muy poco. Dale al comando un contrato más preciso y comprueba el estado fuera de ese comando.

Una forma práctica de pensar en los fallos es separarlos en tres capas:

  • Fallo de SSH: el cliente no pudo conectarse, autenticarse, verificar el host o completar la sesión.
  • Fallo del comando: el proceso remoto detectó un error y devolvió un estado distinto de cero.
  • Fallo del resultado: el proceso devolvió cero, pero el estado remoto esperado no existe, es incorrecto, está incompleto o se revirtió después.

Los equipos suelen mezclar las dos últimas capas. Eso vuelve imprecisos los informes de incidentes y hace peligrosos los reintentos. Si sabes qué capa falló, sabrás si debes revisar las credenciales y la conectividad, corregir el comando o reparar el estado remoto.

El texto de éxito solo sirve como prueba si lo defines con precisión

Las comprobaciones de salida detectan errores que el estado de salida no puede ver, pero solo si la salida tiene un contrato explícito. Buscar palabras como success, complete o deployed en una transcripción orientada a humanos es una prueba débil. Muchas herramientas imprimen esas palabras antes de que falle un comando posterior, y los envoltorios suelen imprimirlas después de limitarse a enviar un trabajo asíncrono.

Haz que el comando remoto emita un único registro con la identidad y el estado esperados. JSON suele resultar práctico, pero una línea con campos delimitados también sirve si controlas los valores. Lo importante es que el script imprima el registro solo después de realizar el trabajo que afirma haber completado.

Por ejemplo, imagina que un script de lanzamiento cambia un enlace simbólico para apuntar a un directorio de versión. Este script remoto emite el destino final en lugar de un mensaje de progreso impreciso:

#!/bin/sh
set -eu

release="$1"
base=/srv/example/releases
link=/srv/example/current

[ -d "$base/$release" ]
ln -sfn "$base/$release" "$link"

actual=$(readlink "$link")
[ "$actual" = "$base/$release" ]
printf 'RELEASE_TARGET=%s\n' "$actual"

El proceso que llama puede exigir una línea de salida exacta:

expected="RELEASE_TARGET=/srv/example/releases/2025.06.14"
output=$(ssh deploy@web-01 '/usr/local/sbin/activate-release 2025.06.14' 2>&1)
status=$?

if [ "$status" -ne 0 ]; then
  printf 'remote command failed, status=%s\n%s\n' "$status" "$output" >&2
  exit "$status"
fi

if ! printf '%s\n' "$output" | grep -Fxq "$expected"; then
  printf 'remote command returned unexpected output:\n%s\n' "$output" >&2
  exit 1
fi

grep -Fxq importa aquí. Comprueba una línea completa y fija. Una expresión poco estricta como grep deployed acepta basura, coincidencias parciales y registros de progreso engañosos. Si la salida contiene campos dinámicos, analiza un documento estructurado con un analizador real en lugar de intentar que una expresión regular entienda datos anidados.

No conviertas la comparación de salida en una segunda copia de todo el comando. Debe responder a una pregunta concreta: ¿el programa remoto dijo que había alcanzado el estado indicado? La lectura posterior responde si esa afirmación sigue siendo cierta donde importa.

Las escrituras remotas necesitan una lectura del sistema dueño del estado

Una lectura posterior ofrece la confirmación más sólida porque consulta al componente que posee el estado modificado. La lectura adecuada depende del cambio y a menudo es distinta del comando que lo realizó.

Para cambiar un enlace simbólico, readlink comprueba el sistema de archivos. Para desplegar un paquete, consulta la versión instalada. Para reiniciar un servicio, pregunta al gestor de servicios si está activo y después realiza una solicitud contra el servicio. Para una modificación de base de datos, lee la fila o usa el endpoint de estado compatible con la aplicación. Para una tarea en cola, consulta el registro de la tarea hasta que alcance un estado terminal.

Este es un patrón problemático habitual:

ssh deploy@web-01 'deploy-release 2025.06.14 && systemctl restart example'

Puede devolver cero mientras el servicio está «activo», pero apunta a una versión antigua porque el script de despliegue escribió en otra ruta. También puede devolver cero cuando el gestor de servicios acepta el reinicio, pero el proceso muere unos instantes después. El shell vio que dos programas devolvieron cero. Los usuarios siguen teniendo un servicio averiado.

Usa una lectura que haga observable la condición deseada. Una comprobación de despliegue podría ser así:

ssh deploy@web-01 '
  test "$(readlink /srv/example/current)" = /srv/example/releases/2025.06.14 &&
  systemctl is-active --quiet example &&
  curl --fail --silent --show-error http://127.0.0.1:8080/healthz
'

Esto es mejor que el primer comando porque comprueba tres afirmaciones que el original solo daba por supuestas. Aun así, no demuestra que todos los usuarios externos puedan llegar al servicio. Si el cambio afecta a un endpoint público, ejecuta una comprobación adecuada desde la posición de red que ocupan realmente los usuarios. Las comprobaciones locales mediante loopback detectan un fallo del proceso, no un error del firewall o del balanceador.

Siempre que sea posible, las comprobaciones de lectura posterior a la escritura deben usar una ruta independiente. Ejecutar una segunda función dentro del mismo script de despliegue es mejor que no hacer nada, pero puede heredar la misma variable equivocada, el host incorrecto o una dependencia simulada. Un comando separado que consulte el sistema de archivos, el gestor de servicios, la API o la base de datos reduce ese fallo compartido.

Un comando debe indicar si es síncrono

Muchos comandos remotos devuelven cero porque han aceptado un trabajo, no porque el trabajo haya terminado. Ese resultado es correcto para enviar una tarea a una cola, iniciar un trabajo en segundo plano, solicitar la recarga de un servicio o llamar a una API de orquestación. Se convierte en un error cuando quien llama interpreta la aceptación como finalización.

Distingue los dos contratos en los nombres y en la salida. submit-backup puede devolver un identificador de trabajo y cero cuando el servidor acepta la solicitud. wait-backup puede devolver cero solo después de que ese trabajo concreto indique que ha terminado. No llames a ambas acciones backup esperando que el operador recuerde qué versión se ejecuta en cada host.

Un script remoto que inicia trabajo en segundo plano requiere especial cuidado. Esta línea de shell devuelve éxito después de iniciar un proceso, aunque ese proceso falle de inmediato:

long-task >/var/log/long-task.log 2>&1 &
printf 'started\n'

El código de salida posterior del proceso no está disponible para quien llama mediante SSH. Guárdalo en un lugar persistente y consúltalo después, o mantén abierta la sesión hasta que la tarea alcance un estado significativo. Si separas la tarea, escribe un identificador de operación en un archivo o una base de datos y devuelve ese identificador. El proceso que llama puede consultar el registro o esperar a que termine.

Un registro de finalización útil contiene información suficiente para reconciliar el trabajo:

operation=4f2c1a status=accepted release=2025.06.14

El proceso que llama no debería aceptar ese registro como un despliegue terminado. Debe consultar operation=4f2c1a y exigir un estado terminal como completed, además de comprobar el estado producido. Puede parecer excesivo para un script sencillo. Es mucho menos engorroso que adivinar si es seguro ejecutarlo de nuevo después de un tiempo de espera.

Los tiempos de espera exigen la misma distinción. Un tiempo de espera local indica que quien llama dejó de esperar. No indica que el comando remoto se detuviera. La red puede haberse interrumpido después de que el host remoto confirmara un cambio. Antes de repetir la operación, inspecciona el estado remoto o consulta el identificador de operación. Repetir la activación de una versión puede ser inocuo si es idempotente. Repetir un pago, una rotación de secretos o el envío de un correo puede multiplicar el daño.

La composición del shell oculta fallos si no escribes pensando en ellos

Revoca una sesión arriesgada
Revoca al instante una ejecución de agente desde el diario de sesiones antes de que emita más acciones remotas.

La sintaxis del shell remoto puede convertir un fallo real en un estado de salida correcto. Es una de las formas más antiguas de que una ejecución SSH parezca limpia mientras deja una máquina dañada.

Considera este comando:

ssh ops@db-01 'backup-db; upload-backup; prune-old-backups'

El shell remoto devuelve el estado de prune-old-backups, su último comando. Si la copia de seguridad falla pero la limpieza tiene éxito, el comando SSH completo devuelve cero. La transcripción puede contener un error al principio, mientras que una canalización que solo lee el estado marca la ejecución como correcta.

Usa set -e en un script que controles o conecta los comandos dependientes con && cuando una línea compacta siga siendo legible:

ssh ops@db-01 'backup-db && upload-backup && prune-old-backups'

set -e no hace milagros. Los shells tienen excepciones relacionadas con condicionales, sustituciones de comandos y algunas construcciones compuestas. No escribas una línea remota larga suponiendo que una sola opción hará visibles todos los fallos. Pon el trabajo no trivial en un script remoto, define una condición clara de éxito para cada operación y prueba su comportamiento ante fallos.

Las canalizaciones introducen otra trampa conocida. En muchos shells POSIX, esto tiene éxito si el programa final tiene éxito, aunque falle el productor anterior:

collect-metrics | format-report > /var/tmp/report.txt

Algunos shells ofrecen set -o pipefail, pero /bin/sh puede no admitirlo. Si el entorno remoto necesita portabilidad POSIX, evita usar una canalización como único límite de errores. Escribe los datos intermedios en un archivo temporal, comprueba el estado del productor y luego consúmelos. Otra opción es ejecutar el script con un shell cuyo comportamiento de pipefail exijas explícitamente.

Evita también terminar un comando remoto con una salida de diagnóstico que pueda tener éxito después de que falle el trabajo real:

apply-config
printf 'finished\n'

Sin set -e ni una comprobación explícita, printf se convierte en el estado de salida. Este error aparece en comandos escritos a mano durante incidentes porque alguien quiere mostrar un mensaje final amable. Imprime el mensaje solo después de comprobar la condición o deja que el comando fallido termine el script.

Los errores de comillas pueden hacer que verifiques la máquina equivocada

Un shell local expande las variables sin comillas antes de que SSH envíe el comando. Eso puede hacer que el comando se ejecute con la versión equivocada, compare la salida local en lugar de la remota o exponga valores en la lista de procesos y los registros locales.

Esto es incorrecto cuando quieres que el host remoto evalúe $release:

ssh deploy@web-01 "test \"$(readlink /srv/example/current)\" = \"$release\""

El shell local evalúa $(readlink ...) antes de iniciar SSH. Ahora has comparado el /srv/example/current de tu equipo, si existe, con una variable local y has enviado el texto resultante al shell remoto. El estado de salida puede ser cero. La comprobación no inspeccionó el host de destino.

Mantén entre comillas simples el programa remoto cuando contenga sintaxis de shell que deba ejecutarse remotamente. Pasa los valores dinámicos o no confiables como parámetros posicionales en lugar de construir texto de shell. Por ejemplo:

release='2025.06.14'
ssh deploy@web-01 'sh -s -- "$1"' sh "$release" <<'REMOTE'
set -eu
release=$1
target=$(readlink /srv/example/current)
[ "$target" = "/srv/example/releases/$release" ]
printf 'verified=%s\n' "$target"
REMOTE

El delimitador entre comillas del heredoc impide que el shell local expanda el cuerpo del script. El valor de la versión viaja como argumento de shell, donde el shell remoto puede entrecomillarlo correctamente. Esto no convierte por sí solo una entrada arbitraria en un nombre de versión seguro. Valida los caracteres permitidos y los formatos esperados antes de usar valores controlados por usuarios en rutas, comandos o consultas de base de datos.

Para la automatización, es preferible un script remoto con parámetros a una cadena de comillas anidadas cada vez más larga. Los fallos de comillas son difíciles de detectar en una revisión porque el comando puede parecer razonable a primera vista. Son mucho más fáciles de diagnosticar cuando los registros muestran la versión exacta del script remoto, los valores de parámetros que sea seguro registrar y la salida del propio comando de verificación.

Un contrato SSH útil tiene tres resultados separados

Verifica las pruebas de SSH
Verifica sin conexión el registro de auditoría cifrado y encadenado mediante hashes, incluidas las llamadas SSH y las comprobaciones posteriores.

Trata cada acción SSH importante como un pequeño protocolo con campos independientes para el transporte, el resultado del comando y el estado observado. Quien llama necesita los tres para decidir si puede continuar, repetir la operación o pedir ayuda.

Esta función de Bash muestra la estructura. Captura stderr junto con stdout para que una operación fallida deje pruebas de diagnóstico, y distingue el fallo del transporte SSH de una respuesta correcta inesperada.

run_remote_check() {
  local host=$1
  local expected=$2
  shift 2

  local output status
  output=$(ssh "$host" "$@" 2>&1)
  status=$?

  if [ "$status" -eq 255 ]; then
    printf 'ssh_transport=failed host=%s\n%s\n' "$host" "$output" >&2
    return 255
  fi

  if [ "$status" -ne 0 ]; then
    printf 'remote_command=failed host=%s status=%s\n%s\n' \
      "$host" "$status" "$output" >&2
    return "$status"
  fi

  if ! printf '%s\n' "$output" | grep -Fxq "$expected"; then
    printf 'remote_result=unexpected host=%s expected=%s\n%s\n' \
      "$host" "$expected" "$output" >&2
    return 1
  fi

  printf 'remote_result=confirmed host=%s\n' "$host"
}

Llámala con un comando que emita solo un registro contractual después de sus propias comprobaciones internas:

run_remote_check \
  deploy@web-01 \
  'RELEASE_TARGET=/srv/example/releases/2025.06.14' \
  '/usr/local/sbin/activate-release 2025.06.14'

Después, ejecuta una lectura posterior como acción independiente. Mantén esa lectura separada en los registros y en la comunicación del estado. Si la activación tiene éxito pero falla la comprobación del servicio, los operadores deben ver exactamente ese límite. Un único resultado opaco como deploy failed les obliga a repetir comandos solo para descubrir qué ocurrió.

Para los comandos que devuelven datos estructurados, devuelve un objeto JSON pequeño y analízalo con un analizador JSON. No uses grep contra JSON salvo que la salida sea deliberadamente un indicador de una sola línea y no necesites interpretar sus campos. Comparar cadenas contra JSON arbitrario falla cuando cambian los espacios, el orden o el contenido escapado.

El contrato también debe nombrar el objeto de destino. status=ok por sí solo no distingue la versión 2025.06.14 de la de ayer. Incluye el identificador del despliegue, el nombre del host cuando sea relevante, la versión del objeto o el identificador de operación. Este pequeño detalle evita un falso positivo sorprendentemente habitual: la comprobación verifica que existe algo saludable, pero no que sea aquello que la ejecución acaba de cambiar.

La observabilidad debe conservar el resultado de la verificación

Un registro que solo conserva el comando remoto y el estado de salida deja sin responder la pregunta más importante: ¿quien llamó observó de forma independiente el estado esperado? Registra la acción de verificación y su resultado junto a la modificación.

Para un despliegue, un registro útil puede incluir el host de destino, el identificador de la versión, el estado de SSH, el estado del comando, el registro contractual exacto, el estado del comando posterior y un resultado breve de la lectura de salud o estado. No registres secretos, encabezados de autorización completos ni argumentos privados de comandos solo porque ayuden a depurar. Diseña la interfaz del comando para que las pruebas útiles puedan conservarse de forma segura.

Sallyport mantiene un diario de actividad para acciones individuales y un diario de sesiones para ejecuciones de agentes, ambos proyectados desde un registro de auditoría cifrado y encadenado mediante hashes. Esto ofrece al operador un lugar para distinguir una acción SSH que se ejecutó de una acción de verificación que confirmó un resultado, en vez de tratar una llamada correcta como toda la historia.

Las pruebas de manipulación ayudan a responder si una acción registrada cambió después. No convierten un contrato de comando débil en una prueba de que el resultado sea correcto. sp audit verify puede verificar sin conexión la cadena del registro sobre el texto cifrado, pero el diseño de la acción sigue necesitando una comprobación explícita mediante lectura posterior.

Mantén la verificación lo bastante cerca de la escritura para que otro actor no pueda sustituir silenciosamente el estado deseado durante ese intervalo. No siempre puedes eliminar las condiciones de carrera en un sistema compartido. Puedes reducirlas usando identificadores de versión inmutables, identificadores de operación, comprobaciones de versión y APIs compatibles con actualizaciones condicionales. Si el estado puede cambiar de nuevo, registra la versión o la marca de tiempo observada y haz que la automatización posterior la compruebe antes de actuar.

Los reintentos necesitan conciliación antes de repetir la operación

Inspecciona la ejecución después de un tiempo de espera
Revisa las ejecuciones de los agentes en el diario de sesiones cuando un tiempo de espera deje incierto el resultado remoto.

Un tiempo de espera, una conexión perdida o una ejecución de CI interrumpida crean un resultado desconocido. El host remoto puede haber completado el cambio, seguir ejecutándolo o haber fallado a mitad de camino. Quien llama no lo sabe simplemente porque haya dejado de recibir salida.

No resuelvas esa incertidumbre con un reintento ciego. Ejecuta primero un comando de conciliación de solo lectura que clasifique el estado remoto. Para activar una versión, inspecciona el enlace simbólico actual y la salud del servicio. Para una migración, consulta la tabla de migraciones. Para un recurso creado, consulta usando un identificador de operación generado por quien llama. Para una rotación de secretos, comprueba qué versión usan realmente los consumidores antes de generar otra.

Un buen comando de conciliación devuelve uno de un conjunto pequeño de resultados explícitos:

  • completed: el estado de destino coincide con la operación solicitada.
  • running: la operación todavía controla un trabajo y quien llama debe esperar.
  • absent: no hay pruebas de la operación, por lo que podría ser adecuado repetirla.
  • conflict: existe un estado diferente y una persona o un controlador de nivel superior debe decidir.

No conviertas todos los resultados en éxito o fallo. running y conflict son resultados útiles. Un sistema que los llama fallos suele repetir trabajos que deberían haberse dejado intactos.

La idempotencia reduce el coste de los reintentos legítimos, pero el término se usa mal. Un comando es idempotente cuando repetir la misma solicitud deja el estado deseado sin cambios después de la primera aplicación correcta. ln -sfn puede ser idempotente para un destino concreto de enlace. «Crear una copia de seguridad nueva con la hora actual» no lo es. «Enviar un correo» tampoco, salvo que el sistema posterior elimine duplicados mediante un identificador de mensaje estable.

Construye la comprobación mediante lectura posterior antes de añadir reintentos. Si no puedes explicar cómo identificar una operación completada, no puedes automatizar de forma segura la repetición después de una situación incierta. En ese punto, la automatización SSH deja de ser una simple comodidad del shell y empieza a necesitar un modelo de operaciones.

La primera reparación consiste en auditar tus ejecuciones correctas

Empieza por los comandos remotos que modifican el estado de producción y actualmente solo informan de un código de salida correcto. Para cada uno, anota el objeto remoto que debe cambiar, el estado exacto que cuenta como éxito, el componente que puede leer ese estado y qué incertidumbre deja un tiempo de espera.

Después cambia la interfaz del comando para que emita un registro de resultado exacto después de sus propias comprobaciones. Añade una acción independiente de lectura posterior. Conserva ambos resultados en el registro de la ejecución. Descubrirás comandos que nunca fueron síncronos, scripts cuyo printf final ocultaba un fallo anterior y comprobaciones de despliegue que consultaban la máquina que lanzó el comando en lugar de la máquina que lo recibió.

Un estado de salida cero sigue teniendo su lugar. Es la primera barrera, no el veredicto final. Tratarlo así produce una automatización capaz de explicar lo ocurrido cuando la señal verde aparentemente sencilla resulta ser incorrecta.

FAQ

¿El código de salida 0 significa que un comando SSH funcionó?

No. Un estado de salida cero indica que el proceso remoto informó de éxito a su shell. No demuestra que el proceso modificara el recurso correcto, alcanzara todas las dependencias ni dejara el sistema remoto en el estado deseado.

¿Qué código de salida devuelve SSH cuando falla el comando remoto?

SSH normalmente devuelve el estado de salida del comando remoto. El manual de ssh(1) también reserva 255 para errores del propio SSH, como fallos de conexión o autenticación. Por eso, trata 255 como un fallo de transporte o del cliente, no como un resultado de la aplicación.

¿Cuándo debo validar la salida de un comando SSH?

Usa comprobaciones de salida cuando el comando tenga un resultado de éxito estable y legible por una máquina, y ese resultado aporte información adicional al código de retorno. No busques frases vagas como «hecho». Emite un marcador explícito o un registro estructurado que identifique el objeto y el estado esperados.

¿Por qué debería hacer una lectura posterior después de un cambio remoto?

Una lectura posterior es la comprobación adecuada cuando el comando modifica un estado persistente, como una versión desplegada, el estado de un servicio, una fila de base de datos, el contenido de un archivo o una configuración remota. Consulta el sistema que posee ese estado después de la escritura, en lugar de confiar en la propia descripción del comando de escritura.

¿Cómo distingo un error de transporte SSH de un error de la aplicación?

Comprueba primero el estado propio de SSH antes de interpretar la salida. Si ssh devuelve 255, informa de un fallo de conexión, verificación del host, autenticación o cliente. No lo describas como un despliegue fallido ni como un fallo del comando remoto.

¿Puedo repetir de forma segura un comando SSH después de un tiempo de espera?

Sí, siempre que el comando remoto esté diseñado para repetirse de forma segura. Usa una operación idempotente, verifica el estado final deseado y evita repetir comandos que creen pagos, envíen mensajes, roten secretos o apliquen cambios de base de datos no idempotentes, salvo que tengas un identificador de operación y un proceso de conciliación.

¿La salida del comando es más fiable que un código de salida?

Un comando puede imprimir un mensaje tranquilizador antes de que falle una operación posterior, o imprimir un mensaje de éxito después de limitarse a poner un trabajo en cola. Conserva la salida para el diagnóstico, pero exige un marcador exacto, un valor estructurado o una lectura independiente del estado antes de aceptarla como prueba.

¿Debería poner los comandos SSH complejos en un script remoto?

Usa un script remoto cuando las comillas, la configuración del entorno, la limpieza o varias comprobaciones hagan difícil revisar una línea única. Un script pequeño con set -eu, una salida explícita y comandos de verificación separados es más seguro que un fragmento de shell con muchas comillas que nadie puede inspeccionar con precisión durante un incidente.

¿Cómo deberían los agentes de IA validar las acciones SSH?

Sí. Un agente de programación puede confundir una invocación SSH local correcta con una prueba de que la acción remota logró su objetivo, especialmente si no puede inspeccionar el estado final. Dale un contrato que exija un código de retorno, un resultado esperado y una lectura posterior para las operaciones que modifiquen el estado.

¿Los registros de auditoría SSH demuestran que un cambio remoto tuvo éxito?

Los registros ayudan a reconstruir lo que se ejecutó, pero no demuestran que un comando lograra el efecto operativo o de negocio previsto. Registra el comando, el host, el estado, un resumen criptográfico de la salida y el resultado de la verificación para que un operador pueda identificar qué capa falló.

Sallyport

Sallyport ejecuta llamadas de API y comandos SSH por tu agente de IA. Las claves se quedan en una bóveda local de tu Mac; tú apruebas cada ejecución y cada acción queda en un registro sellado.

© 2026 Sallyport · Código abierto bajo Apache-2.0 · Oleg Sotnikov