Exploring the roots and future of Internet Relay Chat

ChatOps en IRC: cuando desplegábamos desde un canal antes de que existiera Slack

En 2012, estaba en un canal de IRC con el equipo de operaciones de una startup italiana. Alguien escribió .deploy api production. El bot respondió con un emoji de cohete (bueno, con texto: [DEPLOY] api -> production - starting...). Dos minutos después: [DEPLOY] api -> production - done. 23 seconds. El deploy a producción se hizo desde un canal IRC. Todo el equipo vio qué se desplegó, quién lo hizo, y cuánto tardó. Sin abrir Jenkins. Sin SSH a ningún servidor. Eso era ChatOps antes de que Slack existiera.

Qué es ChatOps y de dónde viene

ChatOps es la práctica de gestionar operaciones de infraestructura y despliegues a través de un chat grupal, usando bots como interfaz. En vez de abrir una consola SSH, abrir Jenkins, o ejecutar scripts manualmente, escribes un comando en el canal de chat y un bot ejecuta la acción.

Las ventajas son tres. Visibilidad: todo el equipo ve qué está pasando. Cuando alguien despliega, todos lo saben. Cuando un build falla, todos lo ven. No hay acciones ocultas en terminales individuales. Auditoría: el log del canal es un registro completo de todas las operaciones. Quién hizo qué, cuándo, y qué resultado tuvo. Accesibilidad: cualquier miembro del equipo con acceso al canal puede ejecutar operaciones, desde un navegador, un móvil, o un cliente IRC viejo.

El término "ChatOps" lo acuñó GitHub internamente alrededor de 2011-2012. Pero la práctica existía en IRC desde mucho antes. Equipos de sysadmins usaban bots IRC para monitorizar servidores y ejecutar tareas desde los años 2000. Lo que GitHub hizo fue sistematizarlo, darle un nombre, y construir herramientas.

GitHub y el IRC: donde empezó todo

GitHub usó IRC internamente durante sus primeros años. Antes de Slack, antes de Teams, antes de cualquier herramienta de chat empresarial moderna, los ingenieros de GitHub estaban en canales IRC discutiendo código, revisando pull requests y desplegando a producción.

Jesse Newland, ingeniero de operaciones de GitHub, dio una charla en 2013 que definió ChatOps para la industria. Mostró cómo GitHub desplegaba a producción docenas de veces al día desde un canal IRC. El flujo era simple: un ingeniero escribía un comando, Hubot ejecutaba la acción, y el resultado se reportaba al canal.

Lo revolucionario no era la tecnología. Los bots IRC existían desde Eggdrop en 1993. Lo revolucionario era la filosofía: toda operación significativa debe pasar por el chat. No hay acciones en la sombra. No hay "es que lo hice desde mi terminal y se me olvidó avisaros". Si no está en el canal, no pasó.

GitHub eventualmente migró de IRC a Slack (y después construyó herramientas internas propias). Pero la cultura ChatOps que nació en IRC definió cómo la industria piensa sobre operaciones colaborativas. Cada vez que ves una notificación de deploy en un canal de Slack, estás viendo la herencia directa de aquellos canales IRC de GitHub.

Hubot: el bot que cambió las operaciones

Hubot fue el framework de bots que GitHub construyó y publicó como open source en 2011. Estaba escrito en CoffeeScript (sobre Node.js) y tenía adaptadores para IRC, Campfire, y luego Slack. La idea era que escribieras scripts simples que respondieran a patrones de texto en el chat.

# Hubot script para deploy (CoffeeScript)
# scripts/deploy.coffee

module.exports = (robot) ->
  robot.respond /deploy (\w+) to (\w+)/i, (msg) ->
    app = msg.match[1]
    env = msg.match[2]
    user = msg.message.user.name

    msg.send "[DEPLOY] #{app} -> #{env} iniciado por #{user}"

    # Ejecutar el deploy real
    exec = require('child_process').exec
    exec "./bin/deploy #{app} #{env}", (error, stdout, stderr) ->
      if error
        msg.send "[DEPLOY] FALLO: #{stderr}"
      else
        msg.send "[DEPLOY] #{app} -> #{env} completado."

  robot.respond /status (\w+)/i, (msg) ->
    app = msg.match[1]
    msg.send "Consultando estado de #{app}..."
    # Consultar API de monitorización
    robot.http("https://monitor.ejemplo.com/api/#{app}")
      .get() (err, res, body) ->
        data = JSON.parse(body)
        msg.send "#{app}: #{data.status} | CPU: #{data.cpu}% | RAM: #{data.memory}MB"

Lo que hacía poderoso a Hubot era su ecosistema de scripts comunitarios. Había scripts para todo: deploy con Capistrano, consultas a PagerDuty, gráficos de Graphite, búsquedas en Jira, traducciones, calculadoras, hasta generadores de memes. Instalabas un paquete npm y tenías una nueva funcionalidad.

En IRC, Hubot conectaba mediante el adaptador hubot-irc. Configurabas el servidor, el nick, los canales, y listo. El bot aparecía en el canal como un usuario más. Respondía a mensajes que empezaban con su nombre: hubot deploy api to production.

# Configuración de Hubot para IRC
# Variables de entorno

export HUBOT_IRC_SERVER="irc.tudominio.com"
export HUBOT_IRC_PORT=6697
export HUBOT_IRC_ROOMS="#ops,#dev,#alerts"
export HUBOT_IRC_NICK="hubotops"
export HUBOT_IRC_PASSWORD="password_nickserv"
export HUBOT_IRC_USESSL=true
export HUBOT_IRC_UNFLOOD=true          # Rate limiting
export HUBOT_IRC_SEND_NOTICE_MODE=true # Usar NOTICE para respuestas
Consejo: Si montas un Hubot en IRC hoy, activa HUBOT_IRC_UNFLOOD. Sin esta opción, un comando que genere mucha salida puede causar excess flood y desconectar al bot. Con unflood activado, Hubot espacia los mensajes automáticamente para respetar los límites del servidor.

Lita y Errbot: alternativas en Ruby y Python

Hubot no era la única opción. Lita (Ruby) y Errbot (Python) ofrecían funcionalidades similares para equipos que preferían esos lenguajes.

# Errbot – Plugin de deploy en Python
# plugins/deploy/deploy.py

from errbot import BotPlugin, botcmd
import subprocess

class Deploy(BotPlugin):
    @botcmd(split_args_with=' ')
    def deploy(self, msg, args):
        """Despliega una aplicación. Uso: !deploy app entorno"""
        if len(args) != 2:
            return "Uso: !deploy <app> <entorno>"

        app, env = args
        user = msg.frm.nick

        yield f"[DEPLOY] {app} -> {env} iniciado por {user}"

        try:
            result = subprocess.run(
                ["./bin/deploy", app, env],
                capture_output=True, text=True, timeout=120
            )
            if result.returncode == 0:
                yield f"[DEPLOY] {app} -> {env} completado."
            else:
                yield f"[DEPLOY] FALLO: {result.stderr[:200]}"
        except subprocess.TimeoutExpired:
            yield f"[DEPLOY] TIMEOUT: {app} -> {env} excedió 120s"

    @botcmd
    def rollback(self, msg, args):
        """Revierte el último deploy. Uso: !rollback app"""
        if not args:
            return "Uso: !rollback <app>"
        app = args.strip()
        yield f"[ROLLBACK] Revirtiendo {app}..."
        # ... lógica de rollback

Errbot tiene una ventaja sobre Hubot para equipos Python: los plugins son Python puro. Sin CoffeeScript, sin Node.js. Si tu equipo de operaciones ya escribe scripts en Python (y la mayoría lo hace), escribir plugins de Errbot es natural. Además, Errbot tiene un modo de test integrado que permite probar plugins sin conectar a un servidor IRC real.

Lita era similar pero en Ruby. Tenía un sistema de plugins bien estructurado y un adaptador IRC funcional. Desafortunadamente, el desarrollo de Lita se ralentizó y hoy está prácticamente abandonado. Errbot sigue activo.

Desplegar desde IRC: flujo real

Te muestro cómo funcionaba un flujo de deploy real en un equipo con el que trabajé en 2014. Cinco personas, tres microservicios, despliegues a producción tres veces al día.

# Sesión real de IRC (nombres cambiados)

14:32 <marco> bot: lock deploy api
14:32 <deploybot> Deploy lock acquired by marco for api
14:32 <marco> bot: deploy api staging
14:33 <deploybot> [api] deploying commit a3f2c1d to staging...
14:33 <deploybot> [api] staging deploy complete (18s). Running smoke tests...
14:34 <deploybot> [api] smoke tests passed: 12/12
14:34 <marco> bot: deploy api production
14:34 <deploybot> [api] deploying commit a3f2c1d to production...
14:35 <deploybot> [api] production deploy complete (22s)
14:35 <deploybot> [api] health check: OK (response time: 45ms)
14:35 <marco> bot: unlock deploy api
14:35 <deploybot> Deploy lock released for api
14:35 <lucia> marco: ¿qué había en ese deploy?
14:36 <marco> bot: changelog api a3f2c1d
14:36 <deploybot> [api] Changes since last deploy:
14:36 <deploybot>   - Fix: timeout en endpoint /users (lucia)
14:36 <deploybot>   - Add: rate limiting para /search (marco)
14:36 <lucia> perfecto, gracias

El sistema de lock era fundamental. Impedía que dos personas desplegaran la misma aplicación simultáneamente. Si alguien tenía el lock, nadie más podía desplegar hasta que lo liberara. Simple pero efectivo para evitar conflictos.

El flujo staging -> smoke tests -> production era automático pero no automatizado del todo. El bot no desplegaba a production sin orden explícita. Los smoke tests se ejecutaban automáticamente después de staging, pero la decisión de ir a production era humana. Automatización donde tiene sentido, humanos donde se necesita juicio.

CI/CD notificaciones: Jenkins, GitLab, GitHub Actions

Incluso sin desplegar desde IRC, tener notificaciones de CI/CD en el canal es útil. Todo el equipo ve cuándo un build pasa, falla, o se recupera.

# Webhook genérico para enviar a IRC
# Este script recibe webhooks de CI/CD y los reenvía al canal

import socket
import ssl
import json
from http.server import HTTPServer, BaseHTTPRequestHandler

IRC_SERVER = "irc.tudominio.com"
IRC_PORT = 6697
IRC_NICK = "cibot"
IRC_CHANNEL = "#builds"

def enviar_irc(mensaje):
    raw = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ctx = ssl.create_default_context()
    sock = ctx.wrap_socket(raw, server_hostname=IRC_SERVER)
    sock.connect((IRC_SERVER, IRC_PORT))
    sock.send(f"NICK {IRC_NICK}\r\n".encode())
    sock.send(f"USER {IRC_NICK} 0 * :CI Bot\r\n".encode())
    # Esperar welcome
    import time; time.sleep(3)
    sock.send(f"JOIN {IRC_CHANNEL}\r\n".encode())
    time.sleep(1)
    sock.send(f"PRIVMSG {IRC_CHANNEL} :{mensaje}\r\n".encode())
    time.sleep(1)
    sock.send(b"QUIT :done\r\n")
    sock.close()

class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers['Content-Length'])
        data = json.loads(self.rfile.read(length))

        # Formato para GitHub Actions
        if 'workflow_run' in data:
            run = data['workflow_run']
            status = 'OK' if run['conclusion'] == 'success' else 'FALLO'
            msg = f"[CI] {run['name']} #{run['run_number']} - {status}"
            enviar_irc(msg)

        self.send_response(200)
        self.end_headers()

HTTPServer(('0.0.0.0', 9090), WebhookHandler).serve_forever()

En la práctica, herramientas como irker o CIA.vc (ahora muerto) hacían esto de forma más elegante. Irker es un daemon que acepta notificaciones JSON y las envía a canales IRC. Muchos proyectos open source lo usan para reportar commits en tiempo real.

Advertencia: Ten cuidado con el volumen de notificaciones. Un proyecto con 50 commits diarios y pipelines de CI generará decenas de mensajes. Si el canal de builds es el mismo que el de conversación, el ruido tapará la señal. Separa los canales: #ops para conversación y operaciones, #builds para notificaciones automáticas, #alerts para alertas de monitorización.

ChatOps hoy: lecciones del IRC para Slack y Discord

ChatOps no murió con la migración de IRC a Slack. Se transformó. Hoy la mayoría de equipos hacen ChatOps en Slack con bots como Slackbot, PagerDuty, o integraciones nativas de GitHub. Pero la filosofía es idéntica a lo que hacíamos en IRC.

Las lecciones que IRC enseñó y que siguen vigentes:

El chat es el registro de operaciones. Si una acción no se refleja en el canal, es como si no hubiera pasado. Esto es más valioso de lo que parece. Cuando a las 3am alguien pregunta "¿quién tocó producción?", la respuesta está en el log del canal.

Los bots deben ser herramientas, no obstáculos. Un bot que requiere tres confirmaciones para hacer un deploy no se usa. Un bot que ejecuta con un comando y reporta el resultado se adopta inmediatamente. La fricción mata la adopción.

Separar señal de ruido. En IRC aprendimos a tener canales separados. Las alertas van a #alerts. Los builds a #builds. Las conversaciones a #ops. En Slack la misma disciplina aplica, pero muchos equipos lo ignoran y acaban con un canal general lleno de notificaciones donde nadie lee nada.

El contexto compartido reduce errores. Cuando todo el equipo ve las mismas notificaciones en el mismo canal, se crea un modelo mental compartido del estado del sistema. "Vi que el build de API falló hace 10 minutos, ¿lo estás mirando?" Esa pregunta solo ocurre si la información es visible para todos.

IRC fue el laboratorio donde ChatOps se inventó. Slack fue la plataforma donde se popularizó. Pero la idea central — operar infraestructura de forma colaborativa a través del chat — nació en canales IRC a principios de los 2000. Y sigue siendo una de las mejores prácticas de operaciones que conozco.

Preguntas frecuentes

¿Tiene sentido usar IRC para ChatOps en 2024?

Si tu equipo ya usa IRC, absolutamente sí. Errbot y Hubot siguen funcionando bien con IRC. La ventaja de IRC es que tienes control total del servidor, sin depender de APIs de terceros ni límites de rate limiting de Slack. Para equipos nuevos que no usan IRC, Slack o Mattermost son probablemente más prácticos por su ecosistema de integraciones.

¿Hubot sigue mantenido?

Hubot tuvo un periodo de mantenimiento reducido, pero la comunidad lo mantiene funcional. El ecosistema de plugins es enorme y la mayoría sigue funcionando. Sin embargo, para proyectos nuevos, Errbot (Python) o Bolt (Slack SDK oficial) pueden ser mejores opciones dependiendo de tu stack. Si necesitas un bot multiplataforma que funcione en IRC y Slack, Errbot tiene adaptadores para ambos.

¿Cómo evito que alguien no autorizado ejecute comandos de deploy desde IRC?

Autenticación por NickServ es el primer paso: el bot verifica que el usuario está identificado antes de ejecutar comandos privilegiados. Puedes añadir una lista de usuarios autorizados en la configuración del bot. Algunos equipos van más allá: requieren autenticación de dos factores (el bot envía un código por otro canal) para operaciones destructivas. También puedes limitar comandos de deploy a un canal específico que tiene modo +s (secreto) y +i (invite only).