8 min de lectura

Fallos parciales de comandos SSH: evita que los agentes repitan el trabajo

Los fallos parciales de comandos SSH requieren reconciliación, no reintentos ciegos. Registra códigos de salida, puntos de control, recibos y el estado remoto observado para los agentes de IA.

Fallos parciales de comandos SSH: evita que los agentes repitan el trabajo

Un agente de IA debe tratar un comando SSH fallido como una transición de estado desconocida, no como permiso para volver a ejecutar el mismo texto. Un comando puede crear un usuario, recargar un servicio y fallar después al escribir un archivo final. Un reintento ciego puede crear una cuenta duplicada, sobrescribir una configuración editada manualmente o convertir un despliegue recuperable en una interrupción del servicio.

El consejo habitual, «comprueba el código de salida», es necesario, pero no basta. El estado de salida describe cómo terminó un proceso. Para recuperarse hace falta registrar qué completó el proceso, qué estado informa ahora el host y qué operación pretendía realizar el agente. Guarda esos datos en un recibo de comandos persistente y haz que el agente reconcilie el estado antes de volver a actuar.

Un fallo de SSH deja tres incógnitas distintas

Una acción SSH fallida puede significar que falló el shell remoto, que falló la conexión o que el controlador dejó de esperar. Cada caso requiere un tratamiento distinto, pero los agentes suelen reducir los tres a «el comando falló».

Considera un script de despliegue remoto que realiza estas operaciones en orden:

  1. Escribe un nuevo archivo de la aplicación en un directorio de versiones.
  2. Cambia el enlace simbólico current para que apunte a esa versión.
  3. Reinicia el servicio.
  4. Ejecuta una comprobación de salud y termina con un estado distinto de cero porque detecta un error temporal de dependencia.

La versión está activa aunque el comando haya devuelto un fallo. Repetir el script puede ser inofensivo si todas las operaciones toleran la repetición. Con más frecuencia, el script crea un directorio de versión nuevo, trunca un registro, rota una credencial o ejecuta una migración. El código de salida por sí solo no permite saber qué ocurrió.

El segundo caso es peor: una interrupción del transporte SSH. El proceso local puede recibir 255 después de una ruptura de red mientras el shell remoto sigue ejecutándose. Un tiempo de espera del controlador plantea el mismo problema. El controlador solo sabe que no tiene una respuesta final. No sabe si el destino recibió la solicitud, si el shell se inició o si el proceso todavía está realizando cambios.

Esa diferencia cambia la siguiente acción del agente:

  • Una salida remota confirmada con un recibo requiere recuperarse a partir del punto de control fallido.
  • Un fallo de transporte requiere observar el estado antes de cualquier modificación.
  • Un tiempo de espera del controlador requiere observar el estado y, si hace falta, aplicar un procedimiento de cancelación explícito en lugar de enviar una solicitud duplicada.

No llames reintentos a los tres casos. Un reintento es una operación con una regla conocida de repetición segura. Un estado remoto desconocido necesita reconciliación.

El estado de salida informa sobre un proceso, no sobre una transacción

Un código de salida de SSH aporta información útil, pero no es un registro de confirmación de una base de datos. El manual de OpenSSH ssh indica que ssh termina con el estado de salida del comando remoto o con 255 si se produce un error. Esta formulación marca un límite que muchos sistemas de automatización ignoran: un estado de salida remoto describe el comando cuando SSH lo recibe, mientras que 255 cubre la ruta de error del propio SSH.

Un código de salida cero también necesita interpretación. En el shell POSIX, el estado de una lista secuencial simple normalmente procede del último comando. Este script puede informar de éxito después de un fallo importante:

install -m 0644 app.conf /etc/myapp/app.conf
systemctl restart myapp
logger -t deploy "deployment finished"

Si install falla pero systemctl restart y logger devuelven cero, el estado final del script es cero. El agente ve éxito y puede afirmar falsamente que la configuración cambió. Un echo done final produce la misma mentira.

Las canalizaciones añaden otra vía de fallo. En Bash, el Bash Reference Manual documenta que el estado de una canalización es el del último comando, salvo que se active pipefail. Este comando puede producir un resultado cero si la extracción termina correctamente después de no recibir datos útiles:

curl --fail --silent https://example.invalid/build.tar.gz | tar -xz -C /srv/myapp

Usa un intérprete explícito y declara el comportamiento que necesitas:

#!/usr/bin/env bash
set -Eeuo pipefail

curl --fail --silent --show-error "$archive_url" | tar -xz -C "$release_dir"

-e indica a Bash que se detenga ante muchos fallos no controlados, -u rechaza las variables no definidas y pipefail conserva el fallo de los elementos anteriores de la canalización. La opción E permite que una trampa ERR se aplique dentro de funciones y sustituciones de comandos. Estos ajustes mejoran la información sobre los fallos. No hacen que una secuencia sea atómica.

Este último punto importa. set -e actúa después de que una operación devuelva un error. No puede borrar un directorio que creó un comando anterior ni restaurar un servicio que otro comando ya reinició. También tiene excepciones que sorprenden a muchas personas: los comandos comprobados por if, los comandos situados a la izquierda de && o || y varios contextos compuestos no siempre provocan la salida. Escribe comprobaciones explícitas alrededor de las acciones cuyo fallo cambie la recuperación.

Define el límite de la operación antes de escribir el comando

Un agente no puede recuperarse de una instrucción vaga como «despliega el servicio». El comando remoto necesita una operación con nombre y una condición posterior que un observador pueda comprobar.

Para un cambio de versión, la operación podría ser: «Configura /srv/myapp/current para que apunte a la versión 2025-04-18.3 y confirma que el servicio activo informa de esa versión». Para un cambio de base de datos, podría ser: «Aplica la migración add_invoice_index exactamente una vez y confirma que existe su registro de migración». El texto del comando es un detalle de implementación. La operación y su condición posterior determinan si la recuperación puede continuar.

Divide una operación en los límites irreversibles o visibles externamente. Un punto de control útil no tiene que corresponder a cada línea del shell. Registra un punto de control después de un cambio de estado que modifique la siguiente decisión. En un despliegue, puede incluir la verificación del archivo, el llenado del directorio de versión, el cambio del enlace simbólico, el reinicio del servicio y la observación del estado de salud.

Evita la recomendación popular, pero equivocada, de hacer que cada comando remoto sea «idempotente» y después reintentarlo para siempre. La idempotencia se aplica a una operación concreta y a un estado deseado definido. mkdir -p /srv/app puede repetirse. useradd deploy solo se puede repetir de forma segura si el agente comprueba que la cuenta existente tiene el UID, el grupo, el directorio de inicio y el shell esperados. ALTER TABLE puede fallar en una segunda ejecución o, peor aún, una migración mal escrita puede aplicar dos veces un cambio relacionado.

Un comando puede ser seguro de repetir mientras el flujo de trabajo que lo rodea no lo sea. Reiniciar un servicio puede ser repetible, pero reiniciarlo a mitad de una copia de configuración puede exponer un archivo incompleto. Coloca la validación del estado junto a la acción. No pidas al agente que la deduzca a partir de una regla general.

Para cada operación, define cuatro campos antes de conceder acceso:

  • Un ID de operación que se conserve durante la recuperación.
  • Una condición posterior deseada que pueda inspeccionar un comando de solo lectura.
  • Puntos de control que describan los cambios de estado completados.
  • Una acción de recuperación para cada punto de control incompleto.

El ID de operación no es un detalle decorativo. Si el controlador crea un identificador nuevo en cada intento, el destino no puede distinguir una continuación de una solicitud nueva. Así se producen migraciones repetidas y aprovisionamientos duplicados.

Escribe un recibo antes y después de cada cambio de estado

Un recibo persistente convierte un fallo parcial en un evento que se puede inspeccionar. Escríbelo en el host de destino antes de la primera modificación, actualízalo después de cada punto de control importante y haz que las actualizaciones sean atómicas.

El siguiente script de Bash es deliberadamente sencillo. Despliega una versión ya preparada cambiando un enlace simbólico y reiniciando un servicio del sistema. No pretende resolver todos los métodos de despliegue. Muestra la mecánica de recibos que necesita un agente.

#!/usr/bin/env bash
set -Eeuo pipefail

operation_id=${1:?operation ID required}
release=${2:?release path required}
service=${3:?service name required}
state_dir=/var/lib/agent-ops
receipt="$state_dir/$operation_id.receipt"
tmp="$receipt.$$"

mkdir -p "$state_dir"
chmod 0700 "$state_dir"

write_receipt() {
  cat >"$tmp" <<EOF
operation_id=$operation_id
release=$release
service=$service
checkpoint=$1
updated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
EOF
  chmod 0600 "$tmp"
  mv -f "$tmp" "$receipt"
}

fail() {
  status=$?
  write_receipt "failed:$status"
  exit "$status"
}
trap fail ERR

if [[ -f "$receipt" ]]; then
  . "$receipt"
  case "$checkpoint" in
    complete)
      printf 'operation already complete: %s\n' "$operation_id"
      exit 0
      ;;
    switched|restarted)
      printf 'operation requires reconciliation: %s\n' "$checkpoint" >&2
      exit 75
      ;;
  esac
fi

[[ -d "$release" ]]
write_receipt "release_verified"

ln -sfn "$release" /srv/myapp/current
write_receipt "switched"

systemctl restart "$service"
write_receipt "restarted"

active_target=$(readlink -f /srv/myapp/current)
[[ "$active_target" == "$release" ]]
systemctl is-active --quiet "$service"
write_receipt "complete"
printf 'operation complete: %s\n' "$operation_id"

El archivo temporal y mv son importantes. En un mismo sistema de archivos, el cambio de nombre reemplaza el recibo en una sola operación, de modo que quien lo lea obtiene el recibo completo anterior o el nuevo, no un archivo a medias. Haz que el directorio de estado solo sea escribible por la cuenta propietaria de la operación. Si un usuario no confiable puede editar los recibos, la lógica de recuperación del agente aceptará una ficción como prueba.

La trampa ERR registra el estado de salida cuando Bash gestiona un fallo. No puede ejecutarse si la máquina pierde alimentación, recibe una señal que no se puede capturar o muere de forma abrupta. Por eso el script registra el progreso después de cada cambio de estado completado en lugar de depender únicamente de una trampa final.

No cargues formatos de recibo arbitrarios como hace este pequeño ejemplo, salvo que el directorio tenga propiedad y permisos estrictos. En producción, prefiere JSON analizado por un parser conocido o un formato de líneas fijo que rechace campos inesperados. El ejemplo carga únicamente un archivo que acaba de crear en un directorio protegido para mantener legible el código del shell.

El recibo debe indicar hechos observados, no intenciones optimistas. checkpoint=switched significa que el comando del enlace simbólico terminó correctamente. No significa que el servicio haya cargado la nueva versión. complete aparece después de las comprobaciones explícitas de la condición posterior. Esta diferencia evita que un agente trate un comando escrito como una operación completada.

Haz que el agente solicite reconciliación, no un comando nuevo

Verifica el rastro de recuperación
Verifica la cadena de auditoría de Sallyport sin conexión con sp audit verify, sin necesitar la clave de la bóveda.

Después de un resultado distinto de cero, el agente debe conservar el ID de operación original y ejecutar primero comprobaciones de solo lectura. No debe regenerar el comando de despliegue con pequeños cambios de redacción. Una redacción nueva no crea una transición de estado nueva.

Para el recibo de despliegue anterior, un comando de reconciliación puede inspeccionar tanto el registro persistente como la condición posterior activa:

operation_id='release-7f3b'
cat "/var/lib/agent-ops/$operation_id.receipt"
printf 'current='
readlink -f /srv/myapp/current
systemctl is-active myapp
systemctl show myapp --property=ActiveState --property=SubState --no-pager

La salida tiene una estructura que el agente puede analizar sin fingir que la prosa es una prueba:

operation_id=release-7f3b
release=/srv/myapp/releases/2025-04-18.3
service=myapp
checkpoint=restarted
updated_at=2025-04-18T14:05:12Z
current=/srv/myapp/releases/2025-04-18.3
active
ActiveState=active
SubState=running

Aquí, el enlace simbólico apunta a la versión solicitada y el servicio está activo, pero el recibo se detuvo en restarted. El proceso remoto puede haber muerto después de reiniciar el servicio y antes de escribir complete. El procedimiento de recuperación puede ejecutar de nuevo las comprobaciones de la condición posterior y, si se superan, escribir un recibo de finalización mediante un comando de recuperación limitado. No debe reiniciar el despliegue desde el principio.

Un protocolo útil del controlador mantiene separadas la solicitud, el resultado y la recuperación. Por ejemplo:

{
  "operation_id": "release-7f3b",
  "action": "deploy_release",
  "target": "app-01",
  "arguments": {
    "release": "/srv/myapp/releases/2025-04-18.3",
    "service": "myapp"
  },
  "mode": "reconcile"
}

El destino debe aceptar mode: reconcile solo para inspecciones de solo lectura o para una ruta de finalización escrita previamente que verifique la condición posterior. No permitas que el agente envíe una cadena de shell arbitraria marcada como reconcile. Esa etiqueta no tiene ningún significado de seguridad si el comando puede modificar cualquier cosa.

El código de salida 75 del ejemplo es una señal deliberada de fallo temporal. El número exacto importa menos que un contrato documentado: el agente entiende que debe observar el estado, no enviar un reintento automático. Reserva resultados distintos del controlador para completed, reconcile_required, rejected_before_start y transport_unknown. Un único campo booleano success destruye la información que necesita la recuperación.

Los tiempos de espera y las desconexiones exigen pruebas del estado remoto

Un tiempo de espera es una observación local. El cliente local dejó de esperar, pero no necesariamente detuvo el comando remoto. Tratar un tiempo de espera como una cancelación es una de las formas más rápidas de duplicar una acción remota.

El controlador puede reducir la ambigüedad haciendo que las operaciones sean de ejecución única. Antes de una modificación, el script remoto crea un bloqueo exclusivo asociado a su ID de operación. Un intento posterior detecta el bloqueo y elige entre esperar, inspeccionar el proceso o informar de que la recuperación requiere una decisión humana.

Para un bloqueo sencillo a nivel de host, flock suele ser suficiente:

exec 9>/var/lib/agent-ops/deploy.lock
if ! flock -n 9; then
  printf 'another deployment operation is active\n' >&2
  exit 75
fi

Esto solo protege a los procesos que respetan el mismo bloqueo. No protege frente a un administrador que ejecute un procedimiento de despliegue independiente ni corrige todos los errores de diseño de los scripts. Para una migración de base de datos, usa el bloqueo consultivo o de migraciones de la propia base de datos cuando esté disponible. Para una acción de API, usa un token de idempotencia compatible con esa API. El bloqueo debe estar junto al estado que protege.

Cuando el controlador se vuelva a conectar después de un tiempo de espera, inspecciona en este orden:

  1. Lee el recibo del ID de operación original.
  2. Comprueba si el proceso original sigue ejecutándose, si la operación tiene un marcador de proceso fiable.
  3. Comprueba la condición posterior de la operación mediante comandos de solo lectura.
  4. Elige una acción de recuperación explícita o escala el caso cuando las observaciones no coincidan.

No uses la presencia del proceso como única señal. Un proceso puede existir mientras espera una dependencia externa, y que no exista dice poco sobre lo que cambió antes de terminar. El recibo y la condición posterior ofrecen pruebas más sólidas.

La multiplexación de SSH requiere el mismo cuidado. Una conexión maestra puede ocultar el fallo de un comando individual detrás de un transporte compartido, y un controlador puede confundir un canal cerrado con una operación fallida. Captura como un único registro de acción la salida estándar remota, la salida de error, el estado de salida SSH sin procesar, la hora de inicio y la hora de finalización. Conserva stderr aunque el agente lo resuma. Los datos sin procesar suelen mostrar si Bash rechazó una variable no definida, si un comando remoto terminó con 75 o si SSH devolvió 255.

Algunos cambios necesitan compensación, no reintentos

Descubre qué proceso continúa
Aprueba un nuevo proceso del agente una vez y revoca su sesión de inmediato si la recuperación parece incorrecta.

Muchas operaciones no se pueden hacer repetibles de forma segura después de que hayan empezado. La rotación de credenciales, la limpieza destructiva, las llamadas de API parecidas a pagos y las migraciones de esquemas necesitan un procedimiento compensatorio o una decisión del operador.

Considera un flujo de rotación de credenciales. El comando puede crear una credencial nueva, actualizar un servicio, verificar el acceso y después revocar la credencial anterior. Si falla después de crearla, pero antes de actualizar el servicio, reintentar puede crear otra credencial y dejar varios secretos activos. El recibo debe registrar inmediatamente el identificador de la credencial recién creada. La recuperación puede comprobar qué credencial usa el servicio y decidir si actualiza, revoca o conserva la nueva.

No introduzcas material secreto en el recibo, la salida estándar ni el contexto del agente. Guarda únicamente un identificador o una huella no secreta, siempre que ese identificador no conceda acceso por sí mismo. El proceso de recuperación necesita saber qué objeto existe, no conocer su valor privado.

Las migraciones de bases de datos presentan otra trampa. La tabla de historial de un framework de migraciones puede indicar que una migración con nombre terminó, pero quizá no describa una actualización masiva de datos interrumpida que se ejecutó fuera de la transacción del framework. Escribe las migraciones de modo que el cambio de esquema, el progreso de la actualización y el marcador de finalización tengan comprobaciones separadas. Si la base de datos admite DDL transaccional para tu operación, úsalo, pero no supongas que todas las instrucciones DDL o los efectos externos se revierten con la transacción.

Para las acciones visibles externamente, prefiere un token de idempotencia de API en lugar de intentar deducir el estado mediante SSH. Si el host remoto llama a una API que acepta una clave de idempotencia, guarda ese token en el recibo antes de realizar la solicitud. Durante la recuperación, consulta la API con el mismo token o vuelve a enviar la solicitud usando el mismo token, según la semántica documentada de la API. Generar un token nuevo en cada intento del agente anula esta función.

La regla es directa: si no puedes explicar cómo determinar si una acción ocurrió, no permitas que un agente autónomo la reintente. Pide a una persona que inspeccione el destino o rediseña la operación alrededor de un registro de estado persistente.

Los scripts de shell necesitan un contrato que el agente pueda aplicar

Un script destinado a un agente debe exponer un contrato limitado y legible por máquinas. A los agentes les cuesta reconstruir el estado a partir de registros verbosos, terminales con colores y una mezcla de advertencias y mensajes de éxito.

Usa categorías de salida estables, un único objeto de resultado e IDs de operación explícitos. Por ejemplo, escribe una línea JSON final solo después de que el comando conozca su resultado:

{"operation_id":"release-7f3b","outcome":"reconcile_required","checkpoint":"switched","exit_code":75}

Mantén la salida de diagnóstico habitual en stderr y reserva stdout para el registro de resultados si tu controlador puede aplicar esta convención. Un comando que imprime banners, barras de progreso y JSON en stdout invita a que fallen los parsers. Si el comando transmite información de progreso útil, escribe primero el recibo persistente y permite que el controlador trate el registro estructurado final como una comodidad, no como el único registro.

No permitas que el agente elija nombres de puntos de control, rutas de recibos, nombres de servicios o intérpretes arbitrarios. Expón un comando revisado con argumentos limitados. Un wrapper que acepta un shell libre después de -- solo ha trasladado el problema detrás de una etiqueta más limpia.

El contrato del comando también debe indicar qué fallos se pueden reintentar de forma segura. Por ejemplo, una descarga de paquetes puede devolver un error de red reintentable antes de modificar el host. Un cambio de enlace simbólico seguido de una conexión perdida no se puede reintentar hasta que la reconciliación verifique el destino del enlace. Esta clasificación corresponde a quien diseñó la operación y conoce sus efectos, no a un modelo que intenta adivinarlos a partir de stderr.

Usa un host de prueba e interrumpe el script en cada punto de control. Envía una señal de terminación durante la extracción del archivo, después del cambio del enlace simbólico, durante el reinicio y después de la comprobación de salud final. Después ejecuta la ruta de reconciliación y comprueba si llega a la decisión correcta. Si nunca has interrumpido una operación a propósito, no sabes si su comportamiento de reintento es seguro.

La autorización y los registros de auditoría deben conservar la historia de recuperación

Aprueba el proceso real del agente
Usa la autorización por sesión para identificar un nuevo proceso del agente antes de que inicie la recuperación mediante SSH.

Un agente necesita autoridad para realizar una lectura de recuperación, pero una lectura que conduzca a una nueva modificación debe seguir la frontera normal de autorización. No ocultes un segundo despliegue dentro de un comando llamado status.

Sallyport puede mantener las credenciales SSH fuera del agente y registrar tanto la ejecución del agente como cada acción, lo que ayuda a conservar quién autorizó un intento de recuperación y qué comando envió. Su registro de auditoría no sustituye al recibo del destino: el registro de auditoría puede demostrar que se produjo una solicitud de acción, mientras que el recibo y la condición posterior explican el estado resultante del destino.

Mantén estos registros separados en tus notas del incidente. La identidad de la sesión responde a qué proceso del agente tenía permiso. El registro de la acción individual responde a qué comando se ejecutó contra qué host y qué devolvió. El recibo del destino responde hasta dónde llegó esa operación. Cuando un despliegue sale mal, reunir estos hechos en una sola transcripción de chat desperdicia las pruebas que necesitas.

Solicita confirmación por llamada para las operaciones con efectos irreversibles, especialmente cuando la reconciliación pueda llevar a eliminar credenciales, reparar un esquema o realizar una limpieza. La aprobación adicional es útil cuando las pruebas del agente entran en conflicto. Por ejemplo, el recibo indica que el cambio se completó, pero el servicio activo sigue informando de una versión antigua. Ese es un punto de decisión, no un reintento rutinario.

Un buen diseño de recuperación hace que la acción conservadora resulte sencilla. Proporciona al agente un comando de reconciliación de solo lectura, un ID de operación persistente y un resultado de escalado definido. Así, una desconexión produce un registro inspeccionable en lugar de un segundo intento que vuelva a modificar el sistema.

Empieza por corregir el comando que el equipo ya reintenta

Busca el comando SSH que tu equipo vuelve a ejecutar después de un tiempo de espera o de un mensaje de despliegue en rojo. Añade un ID de operación, un recibo protegido y una comprobación de solo lectura de la condición posterior antes de cambiar cualquier otra cosa.

Después, interrúmpelo a propósito. Si la ruta de recuperación no puede indicar si el primer intento cambió el estado, todavía no está preparada para un agente autónomo. Reescribe la operación hasta que la respuesta proceda del host de destino y no de la confianza en un código de salida.

FAQ

¿Qué le indica realmente a un agente de IA el código de salida de SSH?

SSH normalmente devuelve el código de salida del comando remoto. Esto indica si el shell informó de éxito, pero no demuestra que todas las operaciones anteriores hayan dejado el host sin cambios. Trata un estado distinto de cero como una señal para inspeccionar el estado registrado antes de reintentar.

¿Cuándo es seguro reintentar automáticamente un comando SSH fallido?

Solo cuando todas las operaciones anteriores al fallo son idempotentes y puedes verificar su condición posterior. La actualización de metadatos de paquetes y el reinicio de un servicio concreto pueden cumplir estos requisitos. La creación de cuentas, los cambios en el cortafuegos y las migraciones de datos suelen requerir una reconciliación previa.

¿`set -e` evita los fallos parciales de comandos SSH?

No. set -e detiene el shell en muchas condiciones de error, pero tiene excepciones deliberadas para condicionales, canalizaciones y sustituciones de comandos. Además, no puede deshacer una operación que terminó antes de que fallara un comando posterior.

¿Por qué los scripts remotos deben usar pipefail?

Usa set -o pipefail en Bash cuando un fallo en cualquier elemento de una canalización deba hacer que falle el script. Sin esta opción, curl | tar puede devolver el estado de tar aunque curl haya fallado, lo que proporciona al agente una visión peligrosamente incompleta de la ejecución.

¿Qué debe contener el recibo de un comando SSH?

Guarda un identificador de ejecución, la operación prevista, cada punto de control completado, el estado de salida y suficiente información observada para permitir la recuperación. Conserva el recibo en el host remoto y también en el registro de acciones del agente, porque una conexión SSH interrumpida puede impedir que la salida llegue al controlador.

¿Cómo evito que un reintento del agente ejecute dos veces el mismo cambio?

Usa un identificador de ejecución estable proporcionado por el controlador, escribe los puntos de control de forma atómica en un directorio propiedad de root y haz que el comando cargue cualquier recibo anterior antes de actuar. Un identificador aleatorio nuevo en cada reintento no puede distinguir un reintento de una solicitud nueva.

¿Qué debe hacer un agente después de un tiempo de espera de SSH?

Un tiempo de espera significa que el controlador dejó de esperar, no que el proceso remoto se haya detenido. Antes de reintentar, inspecciona un recibo persistente, el estado del proceso, el archivo de bloqueo, el estado del servicio o el registro de transacciones en el host de destino.

¿Qué significa el código de salida 255 de SSH?

OpenSSH usa el estado de salida del comando remoto cuando logra recibirlo y normalmente devuelve 255 para sus propios errores. Los agentes deben conservar el código de salida sin procesar y stderr, y después distinguir un fallo del script remoto de un fallo de transporte antes de elegir una recuperación.

¿Cómo puede un agente saber si un cambio remoto ya se realizó?

No decidas solo por el código de salida. Comprueba la condición posterior relevante: consulta el gestor de paquetes, inspecciona el servicio del sistema, lee la tabla de migraciones o compara la configuración deseada con la activa. La operación de recuperación correcta depende del estado observado.

¿Las acciones SSH de un agente de IA deben tener registros separados para la sesión y para los comandos?

Registra tanto la identidad de la sesión como cada acción SSH individual. La aprobación de la sesión indica qué proceso del agente recibió autorización, mientras que el registro de la llamada indica qué comando se ejecutó, contra qué destino y con qué resultado. Mezclar ambos registros dificulta reconstruir un incidente.

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