Qué es OSC 3008: señalización de contexto de terminal en systemd y solución de salida basura

Explicación de UAPI.15 OSC 3008 Hierarchical Context Signalling: qué hace, qué datos envía, dónde lo usa systemd y cómo diagnosticar o desactivar la salida basura en terminales incompatibles.

OSC 3008, cuyo nombre completo es Hierarchical Context Signalling, es UAPI.15 dentro de las especificaciones de UAPI Group. Define un conjunto de secuencias de control de terminal que permiten a los programas informar al emulador de terminal sobre la jerarquía de contexto actual dentro de una sesión.

En términos sencillos, intenta responder una pregunta cada vez más común en terminales Linux modernos: ¿en qué capa estás realmente?

Por ejemplo, puedes hacer SSH desde tu máquina local a un servidor, entrar en un contenedor y después usar run0 o una herramienta similar para ejecutar un comando con privilegios elevados. Para ti sigue siendo una única ventana de terminal. Para el emulador de terminal, sin una señal adicional, es difícil saber si la salida actual viene de la máquina local, del host remoto, de un contenedor o de un comando elevado.

OSC 3008 está diseñado para este tipo de escenario. La especificación oficial de UAPI indica que permite a los emuladores de terminal rastrear la jerarquía de contexto del contenido actual en pantalla. systemd también ofrece una implementación, normalmente mediante un script llamado 80-systemd-osc-context.sh.

Referencias oficiales:

Por qué hace falta OSC 3008

Los terminales tradicionales muestran la salida de los programas. Como mucho, ayudan a distinguir entornos mediante títulos, colores y prompts. El problema es que las sesiones modernas de terminal suelen estar anidadas:

1
local shell -> SSH remote server -> Docker/Podman/systemd-nspawn container -> run0 elevated command

Si todo depende de PS1, aparecen varios problemas:

  1. El prompt solo describe el shell actual y no expresa bien toda la jerarquía.
  2. Distintos shells, distribuciones y configuraciones de usuario pueden sobrescribirse entre sí.
  3. El emulador de terminal no puede saber de forma fiable a qué contexto pertenece cada salida.
  4. Algunas secuencias de control aparecen como basura visible en terminales incompatibles.

OSC 3008 ofrece un enfoque más estructurado: el programa que entra en un contexto envía una secuencia start, y el programa que sale envía una secuencia end. Tras analizarlas, el emulador de terminal puede saber a qué nodo de contexto pertenece la salida actual.

Qué información envía

OSC 3008 es un conjunto de secuencias de escape OSC. La especificación define dos comandos centrales:

1
2
OSC "3008;start=..." ST
OSC "3008;end=..." ST

start indica que un contexto empieza, se actualiza o vuelve a ser el contexto actual. end indica que un contexto termina.

Cada contexto puede incluir campos de metadatos. Entre los campos comunes están:

  1. type=: tipo de contexto, como shell, command, remote, container, vm o elevate.
  2. user=: usuario UNIX que emite la secuencia.
  3. hostname=: nombre del host.
  4. machineid=: ID de máquina de /etc/machine-id.
  5. bootid=: ID de arranque de /proc/sys/kernel/random/boot_id.
  6. pid=: PID del proceso que emite la secuencia.
  7. comm=: nombre del proceso.
  8. cwd=: directorio de trabajo actual, sobre todo para shell o command.
  9. cmdline=: línea de comandos invocada de forma interactiva.
  10. container= y vm=: nombres de contenedor o máquina virtual.
  11. targetuser= y targethost=: usuario destino u host remoto destino.

Estos campos no están pensados para que el usuario los lea directamente, sino para que los analice el emulador de terminal. Un terminal compatible consume estas secuencias y las usa para mejorar la interfaz.

Qué ventajas puede aportar

Si el emulador de terminal soporta OSC 3008, puede hacer cosas más inteligentes.

Primero, puede marcar salidas de contextos distintos. Por ejemplo, salida de un contenedor, salida de un comando elevado y salida de SSH remoto pueden tener fondos, bordes o avisos diferentes.

Segundo, puede mostrar una ruta jerárquica. El terminal puede saber que la sesión actual está aproximadamente en:

1
local machine -> remote host -> container -> root command

Esto es más estable que depender solo del título de la ventana o de PS1.

Tercero, puede ayudar con títulos de ventana, pestañas y menús contextuales. El terminal puede actualizar el título de la pestaña según el contexto actual, o permitir acciones como abrir un shell en el mismo directorio de una salida concreta.

Cuarto, puede reducir errores. Si estás dentro de un contenedor de producción y ejecutando comandos como root, el terminal puede mostrar un aviso más visible y reducir la probabilidad de ejecutar comandos peligrosos en el entorno equivocado.

Cómo lo integra systemd

La implementación de systemd vive principalmente en 80-systemd-osc-context.sh. Este script se carga mediante el mecanismo de profile y configura funciones y hooks de prompt en entornos Bash interactivos.

Las rutas comunes pueden ser:

1
2
/etc/profile.d/80-systemd-osc-context.sh
/usr/lib/systemd/profile.d/80-systemd-osc-context.sh

La ubicación exacta depende de la distribución. Los comentarios del código fuente de systemd explican que este archivo puede activarse mediante systemd-tmpfiles, que lo enlaza en /etc/profile.d/.

En Bash usa nombres de función como:

1
2
3
__systemd_osc_context_escape
__systemd_osc_context_common
__systemd_osc_context_precmdline

También afecta a PROMPT_COMMAND y PS0. PS0 se expande después de que Bash lea un comando y antes de ejecutarlo. Por eso algunos terminales incompatibles pueden mostrar un texto largo 3008;start=... antes de cada comando.

Por qué algunos terminales muestran basura

Normalmente, un emulador de terminal debería analizar OSC 3008 como secuencia de control y no mostrarlo directamente.

Pero si el terminal no reconoce OSC 3008, o una capa intermedia filtra, escapa o rompe los caracteres de control, el contenido bruto puede aparecer en pantalla. Los casos comunes incluyen:

  1. Emuladores de terminal antiguos.
  2. Clientes SSH que aún no se han adaptado a OSC 3008.
  3. Bastiones web o terminales de navegador, incluidas algunas instalaciones de Apache Guacamole.
  4. Entornos con soporte OSC limitado, como Emacs term.el, terminales serie o minicom.
  5. Reenvíos de varias capas donde una capa intermedia daña la secuencia de control.

En ese caso puedes ver algo parecido a:

1
]3008;start=...;type=command;cwd=...

O cadenas largas con machineid=, bootid=, pid=, comm= y cwd=. No son salida normal de un programa; son señales de contexto que el terminal debía procesar.

Cómo saber si es OSC 3008

Mira tres pistas.

Primero, si la basura incluye 3008;start= o 3008;end=.

Segundo, si aparece antes o después de ejecutar comandos, especialmente cada vez que pulsas Enter.

Tercero, si el sistema actual cargó las funciones OSC de systemd:

1
declare -f __systemd_osc_context_precmdline

Si ves una definición de función, el shell actual cargó esa lógica.

También puedes comprobar el archivo de profile:

1
ls -l /etc/profile.d/80-systemd-osc-context.sh

Si esa ruta existe y tu terminal no soporta OSC 3008, es muy probable que la salida basura venga de ahí.

Solución temporal

Si solo te molesta en la sesión actual, puedes sobrescribir las funciones relacionadas y limpiar PS0:

1
2
3
4
__systemd_osc_context_precmdline() { :; }
__systemd_osc_context_common() { :; }
__systemd_osc_context_escape() { :; }
PS0=""

Si quieres hacerlo automáticamente solo en sesiones SSH, ponlo en ~/.bashrc:

1
2
3
4
5
6
7
8
if [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

Este método tiene poco alcance. No cambia archivos profile del sistema y es fácil de retirar.

Desactivación a nivel de sistema

Si confirmas que el entorno de terminal de esta máquina es generalmente incompatible con OSC 3008, puedes considerar desactivarlo a nivel de sistema. Ten cuidado: afecta a todos los usuarios y sesiones de inicio.

La idea descrita en los comentarios del script de systemd es eliminar el enlace simbólico /etc/profile.d/80-systemd-osc-context.sh y enmascarar el fragmento tmpfiles correspondiente para que no se vuelva a crear.

Puedes usar un comando como este:

1
2
3
test -h /etc/profile.d/80-systemd-osc-context.sh && \
rm -v /etc/profile.d/80-systemd-osc-context.sh && \
ln -s /dev/null /etc/tmpfiles.d/20-systemd-osc-context.conf

Esto es más pesado que cambiar ~/.bashrc. Prueba primero la solución temporal para confirmar que el problema realmente viene de OSC 3008, y luego decide si la desactivación del sistema tiene sentido.

Si solo tu cliente SSH personal es incompatible, no empieces cambiando la configuración global. Es preferible usar tu propio ~/.bashrc o configurar un comando de limpieza posterior al inicio de sesión en el cliente SSH.

Actualizar el terminal o desactivar OSC 3008

Si usas un terminal moderno que ya soporta OSC 3008, es mejor conservar esta capacidad. En el futuro puede hacer más clara la visualización de shells remotos, contenedores, máquinas virtuales y comandos elevados.

Si tu entorno real es un bastión, una consola serie, un terminal antiguo o un Web SSH que no puedes actualizar pronto, suprimirlo es más práctico. Para la mayoría de tareas de operaciones, una salida limpia y legible es más importante que una visualización de contexto mejorada.

Puedes seguir este orden:

  1. Si puedes actualizar el emulador de terminal, hazlo primero.
  2. Si no puedes actualizar y solo te afecta a ti, suprime la salida en ~/.bashrc o en el comando posterior al inicio de sesión del cliente.
  3. Si afecta a toda la máquina, considera eliminar /etc/profile.d/80-systemd-osc-context.sh y enmascarar el fragmento tmpfiles.

Conclusión breve

OSC 3008 no es malware ni salida aleatoria de un programa. Es señalización de contexto de terminal definida por UAPI.15, pensada para que el terminal entienda la jerarquía entre shells, SSH, contenedores, máquinas virtuales y comandos elevados.

El problema real suele ser la compatibilidad. Un terminal compatible lo analiza automáticamente. Uno incompatible puede mostrar la secuencia de control como basura.

Si ves contenido como 3008;start=, machineid=, bootid= o cwd=, primero comprueba si lo inyectó 80-systemd-osc-context.sh de systemd. Después, según el alcance del impacto, elige una solución temporal, una corrección en tu .bashrc personal o una desactivación a nivel de sistema.

Soluciones prácticas para las secuencias OSC 3008 por SSH

Al conectar por SSH a Ubuntu, Kubuntu u otros sistemas Linux recientes basados en systemd, a veces aparecen caracteres de control extraños cerca del prompt. Las pistas habituales incluyen OSC 3008, systemd context y __systemd_osc_context_*. Normalmente no impiden ejecutar comandos, pero ensucian la salida del terminal y pueden colarse al copiar logs.

Este problema no se limita a un único cliente SSH. WindTerm, terminales integrados, emuladores de terminal antiguos o clientes con soporte incompleto de OSC pueden mostrar directamente secuencias de control que deberían ser interpretadas por el terminal.

La causa habitual es que el sistema remoto carga los hooks de contexto OSC de systemd en el entorno Bash, mientras que el cliente SSH actual no procesa correctamente esas secuencias. La solución es directa: confirmar que existen esas funciones hook, sobrescribirlas con funciones vacías y limpiar PS0.

Abajo tienes dos enfoques. El primero va en el ~/.bashrc remoto y sirve cuando quieres tratar todas las sesiones SSH de forma consistente. El segundo va en el comando posterior al inicio de sesión del cliente SSH y es mejor cuando solo quieres afectar a un cliente o una sesión.

Opción 1: interceptar sesiones SSH en .bashrc

La lógica es simple: si la sesión actual es una sesión SSH y realmente existen las funciones OSC inyectadas por systemd, se sobrescriben las funciones relacionadas con no-ops y se limpia PS0.

Añade este bloque al final del ~/.bashrc del servidor remoto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Detectar sesiones SSH y bloquear basura de secuencias de control systemd OSC 3008
if [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    # Reescribir solo si el sistema cargó realmente las funciones OSC de systemd
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

Después de guardar, vuelve a iniciar sesión por SSH, o ejecuta esto en el shell actual:

1
source ~/.bashrc

Si la salida basura venía de los hooks OSC de systemd, normalmente desaparece al entrar en un nuevo shell.

Si también quieres cubrir un cliente específico

Algunos clientes establecen sus propias variables de entorno. Por ejemplo, WindTerm puede definir TERM_PROGRAM=WindTerm. Si quieres conservar esa comprobación, puedes usar una versión algo más amplia:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Detectar WindTerm o sesiones SSH y bloquear basura de secuencias de control systemd OSC 3008
if [[ "$TERM_PROGRAM" == "WindTerm" || -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    # Reescribir solo si el sistema cargó realmente las funciones OSC de systemd
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

Para el uso SSH normal, comprobar SSH_CLIENT, SSH_TTY y SSH_CONNECTION basta. Añadir TERM_PROGRAM solo cubre clientes que exponen su propio identificador.

Por qué este enfoque es relativamente seguro

Esta configuración tiene dos barreras.

La primera es la comprobación de sesión:

1
[[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]

Estas variables suelen aparecer en sesiones de inicio por SSH, por lo que el cambio afecta principalmente a inicios remotos y no modifica sin más el terminal local de escritorio.

La segunda es la comprobación de existencia de la función:

1
declare -f __systemd_osc_context_precmdline >/dev/null

Es el seguro. La sobrescritura solo ocurre si el shell actual ya cargó __systemd_osc_context_precmdline. Si el sistema no tiene esta lógica OSC de systemd, el bloque no hace nada extra.

Lo que se sobrescribe realmente es:

1
2
3
4
__systemd_osc_context_precmdline() { :; }
__systemd_osc_context_common() { :; }
__systemd_osc_context_escape() { :; }
PS0=""

Esto evita que los hooks relacionados emitan contenido. PS0 es una variable de prompt de Bash que se expande después de leer un comando y antes de ejecutarlo. Algunas secuencias OSC se insertan mediante PS0 o mecanismos similares, por eso también se limpia.

Opción 2: usar un comando posterior al inicio de sesión en el cliente SSH

Si no quieres editar el ~/.bashrc del servidor remoto, pon la corrección en el comando posterior al inicio de sesión del cliente SSH. Muchos terminales o clientes SSH tienen una función parecida, con nombres como:

  1. Command executed after authentication
  2. Post-login command
  3. Remote command after login
  4. Comando posterior al inicio de sesión

Usa esta línea:

1
__systemd_osc_context_precmdline() { :; }; __systemd_osc_context_common() { :; }; __systemd_osc_context_escape() { :; }; PS0=""

Si el cliente necesita un salto de línea explícito tras ejecutar automáticamente el comando, añádelo con la sintaxis de ese cliente. Por ejemplo, en Command executed after authentication de WindTerm puedes usar:

1
__systemd_osc_context_precmdline() { :; }; __systemd_osc_context_common() { :; }; __systemd_osc_context_escape() { :; }; PS0="" \n \n

Los dos \n hacen que el cliente ejecute el comando de limpieza y luego pase a una línea de prompt nueva. Esto sirve para clientes como WindTerm que permiten comandos automáticos después de la autenticación, y se ha probado en un entorno Kubuntu 24.04.

Este enfoque tiene menos alcance: la limpieza solo se ejecuta al conectar con ese cliente y esa sesión. No hace falta cambiar la configuración shell del servidor.

Qué opción elegir

Si el servidor es principalmente tuyo, o quieres que todos los clientes SSH eviten esta salida basura, usa la opción 1. Una vez en ~/.bashrc, WindTerm, Windows Terminal, Tabby, Xshell y otros clientes SSH pueden beneficiarse del mismo tratamiento.

Si el servidor es compartido, o solo quieres que un cliente evite el problema, usa la opción 2. No modifica la configuración remota ni afecta a los entornos shell de otros usuarios.

También puedes probar primero con la opción 2. Si confirma el problema, decide después si la opción 1 debe ir en el ~/.bashrc del servidor.

Notas

Ambas opciones solo suprimen los hooks de salida OSC de systemd. No modifican servicios systemd y no afectan al inicio de sesión SSH.

Sin embargo, si usas un terminal moderno compatible con OSC 3008 y dependes de él para mostrar contexto de comandos, directorio de trabajo o estado del sistema, esas indicaciones mejoradas pueden desaparecer al suprimirlo. Para operaciones SSH normales, inicios en máquinas de desarrollo y administración de servidores, suele estar bien.

Además, no empieces escribiendo estas sobrescrituras en el /etc/bash.bashrc global. Salvo que sepas que todos los usuarios de la máquina necesitan este comportamiento, prefiere ~/.bashrc para entornos personales y el comando posterior al inicio de sesión del cliente para correcciones dirigidas.

Conclusión breve

Si aparece basura de systemd OSC 3008 después de conectar a Linux por SSH, sigue este orden:

  1. Si no quieres cambiar el servidor, configura un comando de limpieza posterior al inicio de sesión en el cliente SSH.
  2. Si es tu propio servidor, pon el bloque de interceptación de sesiones SSH en ~/.bashrc.
  3. Si es un servidor compartido, verifica primero con la solución del cliente y luego decide si conviene llevarlo a la configuración del shell.

La idea central es sencilla: solo en sesiones SSH, comprobar si existen las funciones hook OSC de systemd; si existen, sobrescribirlas con funciones vacías y limpiar PS0. Así se suprime la salida basura sin tocar demasiado el entorno normal del terminal.