Proyecto en Python para preparar, clasificar, revisar y publicar contenido institucional en X, con control operativo por Telegram. Usa exclusivamente las API oficiales de X, Telegram y MiniMax; nunca automatiza la interfaz web de X.
El proyecto nace en modo seguro. En human_review, toda salida de IA es un borrador y una persona
distinta del autor debe aprobar el snapshot exacto. En direct, la ejecución usa identidades de
servicio separadas y la publicación real exige dos seguros de entorno más --live. El webhook de
Telegram puede consultar y registrar decisiones, pero no crea usuarios ni llama a X.
- Publicaciones originales de texto, programadas en YAML.
- Plantillas Jinja reutilizables y vista previa del texto final.
- Aprobación humana auditada y ligada al hash exacto del texto y la hora revisados.
- Estimación conservadora de longitud ponderada y controles preventivos de URL, cashtags y duplicados exactos.
- Cola SQLite heredada para un host persistente y store SQLAlchemy para SQLite local o PostgreSQL en despliegues serverless.
- Autenticación OAuth 1.0a de usuario para una cuenta de Colmat.
- Modo simulación, diagnóstico, reintentos controlados y conciliación de resultados ambiguos.
- RBAC explícito para
owner,admin,editor,reviewer,publisher,scheduleryauditor, con separación entre autoría, aprobación y publicación. - Webhook de Telegram autenticado, deduplicación de
update_idy callbacks de un solo uso ligados a persona, chat, revisión y snapshot. - MiniMax para proponer borradores e imágenes en memoria, con validación local cerrada y sin capacidad de aprobar, programar o publicar.
- Agenda diaria persistida, ejecución idempotente por slot y modos
human_reviewydirect. - Workers
automation-run,generation-runypublication-run, invocables por command jobs deterministas de un OpenClaw aislado en un host persistente. - Carga de hasta cuatro imágenes en X, texto alternativo obligatorio y marca
made_with_aipara media generada con IA. - Panel FastAPI desplegable en Vercel con acceso passwordless por Telegram, gestión de equipo,
agenda, generación y revisión; además de
/api/health,/api/readyy el webhook.
No automatiza respuestas, mensajes directos, likes, follows, tendencias, scraping ni segmentación de personas. Tampoco promete viralidad: la rúbrica de engagement compara claridad, cifra temprana, atribución y legibilidad, pero exige verificación editorial y no autoriza una publicación.
Requiere Python 3.11 o posterior. Desde la raíz del proyecto:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
install -m 0600 .env.example .env
colmat-x doctorLa base .state/colmat.db se crea al primer uso con permisos locales restrictivos y nunca se
incluye en Git. En sistemas sin install, copia el archivo y aplica permisos equivalentes para
que solo su propietario pueda leer .env.
La fuente de control es el PDF de Drive con ID
1S_870mC8iixpNRv2FYtnnLZauO0eDGww. La política local fija tanto el ID como la huella SHA-256 del
texto extraído para contrastarla en cada nueva auditoría. Sus reglas principales están codificadas en
config/editorial-policy.yaml:
dato_semana,ficha_territorio,laminaycorreccion_publicason la taxonomía cerrada.- Toda pieza incluye literalmente una cifra y una fuente que el equipo debe verificar.
- COLMAT es doctrina, la Escuela Colombiana de Filosofía es escuela y Tierra Firme es partido; el generador no puede confundir sus funciones.
- Las láminas exigen serie completa y eje no truncado; las correcciones no admiten atenuantes.
- La IA no reconstruye el emblema ni el Nudo del Macizo, no usa retratos de personas vivas y solo propone colores generativos de la paleta permitida. Los activos oficiales se incorporan después.
colmat-x ai-draft devuelve JSON validado con status=draft, una evaluación explicable y
publication_authorized=false:
colmat-x ai-draft "Cifra y fuente ya verificadas para el dato semanal" \
--category dato_semana \
--institution escuela_colombiana_de_filosofiaLa clave se recibe solo por MINIMAX_API_KEY. El texto usa por defecto
MINIMAX_API_STYLE=anthropic contra el endpoint fijo oficial
https://api.minimax.io/anthropic/v1/messages; openai queda disponible solo como compatibilidad
explícita. La selección es una lista cerrada y no permite inyectar endpoints. La generación de
imagen conserva su API propia. Usa MiniMax-M2.7 para texto e image-01 para imagen salvo que la
documentación oficial y las pruebas justifiquen otro modelo. Nunca subas una clave a Git; si
apareció en un chat, log o captura, rótala antes de usar producción.
SirHegel es el owner canónico del workspace. En una base nueva y vacía, se crea una sola vez
con su correo real; el comando no genera ni solicita contraseñas:
colmat-x team-bootstrap --email CORREO_REAL --display "SirHegel" --username sirhegelNo ejecutes team-bootstrap sobre una base existente ni inventes correos, contraseñas o cuentas
compartidas. Cada integrante debe tener identidad propia y, si usa el bot, su from.id real:
colmat-x team-add --actor-id OWNER_ID --email CORREO_REAL_DEL_MIEMBRO \
--display "Nombre real" --username USUARIO --role editor
colmat-x team-list --actor-id OWNER_ID
colmat-x telegram-bind --actor-id OWNER_ID --user-id USER_ID \
--telegram-user-id FROM_ID --chat-id CHAT_ID --purpose controlLos roles no son contraseñas ni una escala numérica; son funciones explícitas:
| Rol | Alcance principal |
|---|---|
owner |
Autoridad total y único rol que puede delegar owner o admin. |
admin |
Administra equipo, integraciones y operación; no delega owner ni admin. |
editor |
Crea, edita y envía borradores; no los aprueba. |
reviewer |
Aprueba o rechaza; debe ser distinto del autor. |
publisher |
Publica el snapshot aprobado, sin editarlo ni aprobarlo. |
scheduler |
Sincroniza y opera la agenda; no cambia a direct ni publica por su rol. |
auditor |
Consulta workspace, borradores, automatización, Telegram y auditoría. |
El worker usa IDs estables de cuentas de servicio de mínimo privilegio: scheduler, autor,
revisor y publicador. No se deben rellenar esos IDs con el owner SirHegel ni con nombres
mutables. La identidad autora y la revisora siempre deben ser distintas. Los identificadores con
dominio reservado @colmat.internal son principals técnicos: no tienen inbox, contraseña, sesión
interactiva ni acceso por Telegram. Los emails reales y únicos siguen siendo obligatorios para
integrantes humanos.
Telegram autentica por from.id, nunca por username, y exige además el chat autorizado. Expone:
| Comando | Efecto |
|---|---|
/start |
Confirma la conexión. |
/estado |
Consulta el estado editorial y operativo. |
/equipo |
Muestra integrantes y roles visibles. |
/usuarios |
Lista user_id, rol y vínculos numéricos; solo owner y chat privado. |
/invitar <telegram_id> <rol> <correo> <nombre> |
Crea atómicamente un miembro y su vínculo privado; solo owner. |
/vincular <telegram_id> <user_id> |
Vincula un miembro existente por IDs reales; solo owner y chat privado. |
/linea |
Consulta la línea editorial del mes; owner o editor. |
/linea <AAAA-MM> <texto> |
Crea o versiona la línea mensual; solo owner y chat privado. |
/patrones |
Explica el patrón cerrado y los límites de la investigación asistida. |
/investigar <tema> |
Encola una síntesis sin imagen según la línea mensual; consulta solo URLs oficiales autorizadas y nunca publica. |
/calendario [días] |
Consulta entre 1 y 31 días de agenda persistida. |
/modo [human_review|direct] |
Consulta o solicita un modo, según RBAC y seguros. |
/generar <brief> |
Encola según la línea mensual; el webhook no llama a MiniMax. |
/publicar <id> |
Encola durablemente un borrador ya aprobado; el webhook no lo envía a X. |
/ayuda |
Muestra la guía integrada. |
Los callbacks de aprobación o rechazo usan nonces expirables y de un solo uso. Editar texto, fecha, evidencia o imagen invalida la aprobación anterior. Ni los comandos ni los callbacks del webhook llaman directamente a X: solo consultan o mutan el estado transaccional. Cualquier acceso a X queda fuera del proceso web y bajo los seguros del worker persistente.
Las altas administrativas no confían en @username: el owner entrega el from.id decimal y el
bot crea o vincula únicamente un chat privado cuyo chat_id coincide con ese ID. Los roles que se
pueden dar de alta por Telegram son editor, reviewer, publisher, scheduler y auditor; cada
uno conserva sus permisos separados. Repetir exactamente un alta es idempotente y toda creación,
membresía, vínculo y cambio de línea deja auditoría.
/investigar aplica siempre este patrón: pregunta o tesis; hechos y cifras solo de fuentes
aportadas o del registro canónico; separación entre hecho e inferencia; contraste o posible
refutación; síntesis futura para X; y prohibición de fabricar citas. Antes de delegar la síntesis,
el worker recupera como máximo tres URLs HTTPS de una lista institucional cerrada, rechaza
redirecciones a otro host y limita tanto el cuerpo como el texto entregado al modelo. MiniMax solo
produce una síntesis exploratoria: no hace navegación web abierta. El registro canónico contiene 44 títulos
o series documentados y una atribución editorial disputada; el selector envía como máximo tres
coincidencias fuertes por título, cuya pertinencia debe comprobarse. La atribución de 1962 solo se
incluye ante una consulta explícita y se presenta junto con la fuente donde Bueno negó su
participación. Si no hay coincidencia, se incluyen únicamente las URLs maestras y el brief exige
marcar las inferencias POR VERIFICAR. La evidencia durable conserva las URLs finales y el
SHA-256 de cada respuesta, nunca el cuerpo descargado; la verificación editorial humana sigue
siendo obligatoria.
El worker deriva del prefijo interno —no de la salida de MiniMax— la evidencia
research_only=true. Esa síntesis se entrega sin botones de aprobación o publicación y queda
bloqueada al encolar, reclamar o crear directamente un intento de publicación, incluso para filas
antiguas. Tampoco puede quitarse la marca mediante una revisión: tras verificar los hallazgos, un
owner o editor debe copiarlos deliberadamente a /generar, que crea un draft separado, exige y
antepone la línea vigente con su versión y vuelve a pasar por revisión humana. Si línea más encargo
exceden 1000 caracteres, la solicitud se rechaza en vez de omitir la directriz. /publicar no
reescribe nada: solo encola un snapshot publicable que ya fue aprobado por un rol publisher.
generation-run consume la cola de /generar: OpenClaw asigna el command job, MiniMax propone
texto y, opcionalmente, imagen, y Colmat crea siempre un draft in_review. Una outbox durable
entrega la revisión y sus botones al binding activo de un usuario con permiso de revisión; quien
solicita puede ser editor sin recibir permiso para aprobar. Los nonces se emiten atómicamente al
preparar la entrega y solo existen en memoria y en el mensaje de Telegram; la base conserva sus
hashes.
publication-run consume por separado la cola aprobada de /publicar con lease, fence,
idempotencia y los gates de X. El webhook no contiene credenciales de MiniMax ni de X.
El panel web usa el mismo binding control para enviar un código de ocho dígitos por Telegram.
Solo conserva HMAC de códigos, sesiones y CSRF; los códigos duran cinco minutos y las sesiones se
revalidan contra usuario activo, membresía, rol y binding en cada solicitud. SirHegel puede crear
cuentas desde /app; cada nueva identidad debe vincularse a su propio Telegram antes de entrar.
config/automation.yaml es una entrada revisable para validar y sincronizar la agenda; no es
la fuente de verdad durante una ejecución. automation-run lee exclusivamente automation_settings
en la base de datos. Por tanto, editar o desplegar el YAML no cambia el runtime hasta ejecutar
automation-sync con la versión CAS vigente.
Cuando un slot queda en revisión, automation-run confirma AWAITING_REVIEW y una fila de
automation_review_notifications en la misma transacción. El worker drena esa outbox antes de
evaluar nuevos slots, incluso si la agenda está pausada, por lo que un reinicio después del commit
no regenera el borrador ni pierde silenciosamente el aviso. Los callbacks se crean al reclamar la
entrega y sus nonces solo viven en memoria. Como Telegram no ofrece clave de idempotencia para
sendMessage/sendPhoto, una respuesta de transporte o protocolo ambigua queda unknown y nunca
se reenvía automáticamente; un rechazo explícito queda failed para intervención humana.
Flujo de configuración seguro:
colmat-x automation-validate
colmat-x automation-calendar --days 7
colmat-x automation-status --actor-id OWNER_ID
colmat-x automation-sync --actor-id SCHEDULER_ID --expected-version VERSION_1
colmat-x automation-status --actor-id OWNER_ID
colmat-x automation-mode human_review --actor-id OWNER_ID --expected-version VERSION_2
colmat-x automation-status --actor-id OWNER_IDautomation-calendar previsualiza el YAML local; /calendario y automation-status consultan la
configuración que ya fue persistida.
Si un run termina en failed antes de persistir un borrador, el scheduler seguirá devolviendo un
resultado no saludable para ese slot en vez de ocultarlo como un duplicado exitoso. Un owner,
admin o scheduler puede solicitar exactamente un nuevo intento sobre el mismo run:
colmat-x automation-retry RUN_ID --actor-id SCHEDULER_IDLa orden solo deja la solicitud durable y auditada. El siguiente automation-run escanea retries
pendientes de cualquier fecha local, reclama exactamente el run_id original, incrementa
attempt_count y conserva la misma clave de idempotencia y el mismo cupo diario. Nunca crea un run
retroactivo nuevo. Si la agenda dejó de corresponder, retira y audita el flag en esa ejecución para
que no quede varado al cruzar otra medianoche. No admite runs unknown ni runs que ya tengan un
draft: esos casos requieren conciliación para no duplicar snapshots o efectos externos.
Antes de llamar a MiniMax, cada slot autónomo resuelve la línea editorial del mes de su fecha
programada y la antepone al brief con su versión. Mes, versión y SHA-256 quedan en la evidencia del
draft. Si la línea falta o es inválida, el run termina en failed antes de generar o publicar.
Cada slot puede declarar weekdays con una lista no vacía de valores canónicos: lunes,
martes, miércoles, jueves, viernes, sábado y domingo. Omitirla conserva la ejecución
diaria. La agenda canónica limita dato-manana a los lunes, como exige dato_semana; tanto el
calendario como el claim idempotente usan la fecha local de Bogotá y rechazan los demás días.
Cada escritura usa compare-and-swap: vuelve a consultar automation-status y sustituye cada
VERSION_* por la versión realmente devuelta. automation-sync preserva el modo persistido,
alinea a ese modo todos los slots y no activa ni ejecuta nada. automation-mode actualiza
atómicamente el modo global y el de los slots persistidos. Tras el aprovisionamiento, la agenda y
los tres jobs de OpenClaw permanecen deshabilitados; automation-enable es un paso operativo
posterior y deliberado.
En human_review, el worker genera el texto con MiniMax, puede generar la imagen cuando el slot lo
indica, guarda ambos como borrador y los somete a aprobación. Un slot con imagen generada permanece
en human_review: requiere inspección visual y aprobación humana del snapshot exacto antes de
entrar en publication-run. El modo direct queda reservado a piezas sin media generada y solo
continúa si la evidencia editorial está verificada y ligada a la cifra y fuente esperadas, la
rúbrica supera el umbral configurado, se respetan los roles separados y todos los seguros están
activos. ficha_territorio también conserva revisión humana para comprobar la autoría de la célula
local exigida por el manual. La puntuación ayuda a priorizar calidad; no demuestra ni garantiza
alcance viral.
El worker se invoca siempre con una identidad scheduler fija, nunca con un actor elegido por cada ejecución:
# human_review: genera, persiste y notifica; no necesita --live
colmat-x automation-run --env-file /ruta/segura/automation-worker.env
# direct: además exige ambos gates de entorno y --live
colmat-x automation-run --env-file /ruta/segura/automation-worker.env --liveEl archivo indicado debe ser regular, pertenecer al usuario del worker y tener modo 0600.
Contiene los IDs fijos COLMAT_AUTOMATION_SCHEDULER_ID, COLMAT_AUTOMATION_AUTHOR_ID,
COLMAT_AUTOMATION_REVIEWER_ID y COLMAT_AUTOMATION_PUBLISHER_ID, además de las integraciones
necesarias. Los valores presentes en ese archivo prevalecen sobre un entorno heredado. No pases
secretos en argumentos ni los guardes en OpenClaw.
Los tres comandos worker crean el esquema únicamente cuando usan SQLite local. Con PostgreSQL
operan sin DDL; aplica deploy/postgres.sql con una identidad de migración antes de arrancarlos.
Usa tres archivos distintos, modo 0600 y fuera del checkout: automation-worker.env puede
acceder a MiniMax y X; generation-worker.env accede a MiniMax pero rechaza cualquier variable
X_*; publication-worker.env accede a X y nunca recibe MiniMax. Guarda la clave una sola vez en
un archivo 0600 que contenga únicamente MINIMAX_API_KEY=...; los dos primeros envs declaran
COLMAT_MINIMAX_ENV_FILE=/ruta/absoluta/minimax.env y el cargador la incorpora sin interpolar ni
copiarla a la definición del job. Una ruta heredada que no figure en el env primario se ignora.
COLMAT_AUTOMATION_MEDIA_ROOT y
COLMAT_GENERATION_MEDIA_ROOT deben apuntar ambos a /var/lib/colmat-x/media, para que el
publicador lea media de las dos colas. Una imagen mayor de 5 MiB se descarta antes de crear el
draft.
La raíz no es una ruta libre: el worker acepta únicamente los perfiles exactos project, user
o system (respectivamente .state/media, ~/.local/share/colmat/media y
/var/lib/colmat-x/media), además de esas rutas canónicas exactas. Los alias locales heredados
.state/media/automation y .state/media/generation convergen en .state/media; valores con
.., subdirectorios o prefijos parecidos se rechazan antes de tocar el sistema de archivos.
Este flujo manual usa content/posts/*.yaml, sync y run-due; es distinto de la agenda de
plataforma anterior. No ejecutes ambos schedulers sobre la misma cuenta sin una decisión operativa
explícita.
-
Duplica
content/posts/001-bienvenida.yamly asigna unidúnico. -
Escoge una plantilla de
content/templates/y completadata. -
Ejecuta
colmat-x validateycolmat-x preview --id TU_ID. -
Revisa texto, hora y el hash
snapshot de aprobaciónque muestrapreview. -
Sincroniza con
colmat-x sync. -
Aprueba exactamente ese snapshot:
colmat-x approve TU_ID --by "Equipo editorial" --snapshot HASH_COMPLETO -
Comprueba lo vencido con
colmat-x run-due; esto no llama a X. -
Solo cuando todo esté listo, habilita y ejecuta la publicación real.
La aprobación vive en SQLite, no en el YAML. Cualquier cambio de texto o de hora invalida el
snapshot y devuelve la pieza a draft; hay que previsualizarla y aprobarla de nuevo. --by es
metadato de auditoría, no autentica por sí solo a una persona: restringe también el acceso al host
y a la cuenta de servicio.
Si eliminas de content/posts/ un YAML que estaba en borrador o programado, la siguiente
sincronización lo cancela en SQLite para que no pueda publicarse desde estado antiguo. Si repones
un archivo con el mismo ID, ejecuta colmat-x restore TU_ID y vuelve a aprobarlo. Puede contener
texto u hora nuevos: restore carga ese snapshot como draft y nunca conserva la aprobación
anterior. Eliminar todos los YAML es válido y cancela toda pieza activa.
Ejemplo de contenido:
id: colmat-pregunta-001
template: idea
publish_at: "2026-09-02T09:00:00-05:00"
data:
titulo: "¿Qué pregunta necesitamos formular mejor?"
desarrollo: "Las ideas ganan claridad cuando se contrastan con otras perspectivas."
cierre: "Colmat: pensamiento que se convierte en conversación."publish_at siempre debe incluir el desplazamiento horario. El proyecto guarda UTC y muestra
las horas en America/Bogota, configurada en config/colmat.yaml.
colmat-x validate
colmat-x preview
colmat-x preview --id colmat-pregunta-001
colmat-x sync
colmat-x approve colmat-pregunta-001 --by "Equipo editorial" --snapshot HASH
colmat-x withdraw colmat-pregunta-001
colmat-x restore colmat-pregunta-001
colmat-x status
colmat-x status --state scheduled
colmat-x run-due # simulación, nunca llama a X
colmat-x doctor
colmat-x doctor --credentials # comprueba secretos sin habilitar publicación
colmat-x x-whoami --expected-username CUENTA_INSTITUCIONAL
colmat-x team-list --actor-id USER_ID
colmat-x automation-validate
colmat-x automation-calendar --days 7
colmat-x automation-status --actor-id USER_ID
colmat-x automation-sync --actor-id SCHEDULER_ID --expected-version VERSION
colmat-x automation-mode human_review --actor-id OWNER_ID --expected-version VERSION
colmat-x automation-enable --actor-id SCHEDULER_ID --expected-version VERSION
colmat-x automation-disable --actor-id SCHEDULER_ID --expected-version VERSION
colmat-x automation-run --env-file /ruta/segura/automation-worker.env
colmat-x generation-run --env-file /var/lib/colmat-x/generation-worker.env --limit 5
colmat-x publication-run --env-file /var/lib/colmat-x/publication-worker.env --live --limit 5run-due en simulación no llama a X, pero sí sincroniza YAML y puede crear, actualizar o cancelar
filas de SQLite. Un YAML inválido detiene toda la sincronización y, por seguridad, no se publica
ninguna otra pieza en esa ejecución. Aprobar una fecha ya vencida la deja lista para el siguiente
sondeo.
Campos de data requeridos por las plantillas incluidas:
| Plantilla | Campos |
|---|---|
idea |
titulo, desarrollo, cierre |
anuncio |
encabezado, detalle, llamado |
reflexion |
reflexion, etiquetas |
Si un cierre, llamado o etiqueta no aplica, conserva la clave con texto vacío.
Los topes están en config/colmat.yaml. Por defecto se procesa una publicación por ejecución y
como máximo dos por día. Las URL también están desactivadas allí porque actualmente tienen un
costo de API mucho mayor que una publicación sin URL. El límite diario es atómico para procesos
que comparten esta SQLite, pero no conoce publicaciones manuales ni otra instalación con una base
distinta.
En X Developer Console crea o configura una app con permisos de lectura y escritura y genera las credenciales OAuth 1.0a de usuario para la cuenta de Colmat. Si cambias los permisos de una app existente, vuelve a generar sus access tokens. La cuenta de desarrollador necesita crédito disponible: una integración correcta no evita que X rechace una publicación por saldo insuficiente.
La clave MiniMax compartida previamente debe considerarse expuesta: rótala y configura solo la
nueva clave en el gestor de secretos. Verifica también que la cuenta MiniMax tenga cuota para texto
y, si hay slots con generate_image: true, para imagen. MiniMax es exclusivamente el generador;
no recibe permisos de owner, reviewer, scheduler o publisher.
Completa el entorno sin compartirlo ni subirlo a Git y conserva inicialmente ambos bloqueos:
X_CONSUMER_KEY=...
X_CONSUMER_SECRET=...
X_ACCESS_TOKEN=...
X_ACCESS_TOKEN_SECRET=...
COLMAT_LIVE_ENABLED=false
COLMAT_DIRECT_PUBLISH_ENABLED=falseDespués ejecuta colmat-x doctor --credentials y colmat-x x-whoami con el usuario o ID esperado.
El primero valida presencia local; el segundo contacta X y bloquea una cuenta distinta. Ninguno
confirma saldo, precios o capacidad de escritura, que deben revisarse en la consola. Realiza la
primera prueba con una cuenta controlada y una sola pieza. El flujo YAML heredado usa solo
COLMAT_LIVE_ENABLED; el worker nuevo en modo direct exige simultáneamente los dos gates y
--live en esa invocación:
# En el entorno seguro del worker:
# COLMAT_LIVE_ENABLED=true
# COLMAT_DIRECT_PUBLISH_ENABLED=true
colmat-x automation-run --env-file /ruta/segura/automation-worker.env --live
# /generar: OpenClaw reclama, MiniMax redacta y Telegram recibe revisión; nunca X
colmat-x generation-run --env-file /var/lib/colmat-x/generation-worker.env --limit 5
# /publicar: solo snapshots ya aprobados y con los gates de X abiertos
colmat-x publication-run --env-file /var/lib/colmat-x/publication-worker.env --live --limit 5El cliente envía POST https://api.x.com/2/tweets con contexto de usuario. Un Bearer Token
app-only no sirve para crear publicaciones. El proyecto usa OAuth 1.0a porque esta versión opera
una sola cuenta desatendida; X también permite OAuth 2.0 Authorization Code con PKCE.
Un .env con modo 0600 es la protección local mínima. Para producción, usa el gestor de
secretos del entorno y cifrado en reposo; rota de inmediato cualquier credencial expuesta.
Cuando se selecciona otro proyecto mediante --config o COLMAT_CONFIG, solo se carga el .env
de ese proyecto; un .env del directorio de trabajo no puede sustituir sus credenciales. La ruta
de configuración debe permanecer bajo el directorio de trabajo o el perfil del usuario. Las rutas
de contenido y plantillas permanecen dentro del proyecto; la base admite además el destino de
servicio dedicado /var/lib/colmat-x/. La normalización previa rechaza escapes con .., prefijos
hermanos y enlaces simbólicos.
Solo .env.example, con nombres y valores secretos vacíos, pertenece al repositorio. .env,
.state/, bases SQLite, logs, entornos virtuales, cobertura y artefactos de construcción están
excluidos por .gitignore. No fuerces su inclusión con git add -f.
OpenClaw se usa como orquestador aislado en un host persistente: asigna periódicamente tres
command jobs fijos, observa sus códigos de salida y alerta. No genera contenido por sí mismo, no
decide roles o aprobaciones y no sustituye la base, el RBAC ni la máquina de estados de Colmat.
MiniMax opera la generación exclusivamente dentro de automation-run y generation-run, donde
quedan aplicadas la política editorial, la validación de media, la auditoría y las colas durables;
nunca programa, aprueba ni publica en X.
Mantén el gateway de OpenClaw en loopback, con un perfil dedicado a Colmat y sin exponerlo a Internet. Cada job debe ejecutar rutas absolutas y una identidad de sistema fija:
/opt/colmat-x-automation/.venv/bin/colmat-x automation-run \
--env-file /var/lib/colmat-x/automation-worker.env
/opt/colmat-x-automation/.venv/bin/colmat-x generation-run \
--env-file /var/lib/colmat-x/generation-worker.env --limit 5
/opt/colmat-x-automation/.venv/bin/colmat-x publication-run \
--env-file /var/lib/colmat-x/publication-worker.env --live --limit 5
automation-run puede recibir MiniMax y X porque cubre el pipeline diario completo.
generation-run recibe MiniMax para /generar y rechaza cualquier credencial X_*;
publication-run es el consumer de /publicar, recibe X y rechaza una clave MiniMax no vacía.
Mantén los tres archivos 0600 fuera del checkout y los tres jobs inicialmente deshabilitados.
MINIMAX_API_KEY vive en un dotenv exclusivo 0600, regular y del mismo usuario. Declara su ruta
como COLMAT_MINIMAX_ENV_FILE dentro de los envs primarios de automation y generation; el archivo
referido admite solo esa clave y un máximo de 4 KiB. No copies la clave al repositorio, a OpenClaw
ni al env de publication.
Los tres procesos comparten /var/lib/colmat-x/media: configura con ese mismo valor
COLMAT_AUTOMATION_MEDIA_ROOT en automation/publication y COLMAT_GENERATION_MEDIA_ROOT en
generation. Separar esas rutas impediría que publication-run verificara y leyera una imagen
creada por la otra cola.
Configura el directorio de trabajo en la raíz del repositorio. No añadas --live al job de
automation-run mientras la agenda esté en human_review; para direct, solo añádelo después de
la autorización humana, la rotación de MiniMax, la verificación de identidad/crédito de X y la
activación deliberada de ambos gates. publication-run exige su propio --live, pero sigue sin
contactar X mientras COLMAT_LIVE_ENABLED=false.
Deja el job deshabilitado durante el aprovisionamiento. Antes de activarlo, ejecuta manualmente el mismo comando con el mismo usuario del sistema y confirma el JSON de salida, la auditoría y las notificaciones. No configures además cron, systemd timer o Vercel Cron para el mismo worker: un único orquestador facilita la operación y la investigación de resultados ambiguos.
Las unidades deploy/systemd/ pertenecen al flujo YAML heredado run-due; no invocan
automation-run. Consulta deploy/README.md para la topología nueva y la parada de emergencia.
SQLite no es almacenamiento persistente válido en Vercel. Conecta un PostgreSQL serverless (por
ejemplo Neon) y define DATABASE_URL, TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET y un
WEB_AUTH_PEPPER aleatorio de al menos 32 caracteres como secretos del proyecto. No definas
MINIMAX_API_KEY ni credenciales X_* en Vercel: /generar
solo encola y los workers persistentes atienden ambas integraciones. Vercel no crea ni migra el
esquema: aplícalo de forma explícita antes del despliegue y usa readiness para verificar que la
versión requerida ya existe. Despliega después de ejecutar la suite:
pytest -q
vercel link
vercel --prod
curl -fsS https://TU_DOMINIO/api/health
curl -fsS https://TU_DOMINIO/api/readyLa raíz redirige a /login; después del código de Telegram, /app muestra el centro de
operaciones. Todas las mutaciones exigen cookie segura, CSRF, origen del mismo host y permisos
RBAC; FastAPI solo encola trabajo y nunca llama a MiniMax o X durante la solicitud.
Solo cuando ready responda 200, registra en BotFather/API de Telegram la URL HTTPS
https://TU_DOMINIO/api/telegram/webhook con el mismo secreto. Vercel aloja el plano de control y
el webhook; no ejecuta workers persistentes. OpenClaw, en el host aislado, invoca los tres
consumers contra la misma PostgreSQL. El webhook nunca genera ni publica y no recibe credenciales
de MiniMax o X.
Para desarrollo local:
uvicorn colmat_x.web:create_app --factory --host 127.0.0.1 --port 8000El endpoint de salud no inicializa integraciones ni devuelve secretos. Readiness solo informa
ok, missing o error. El webhook limita el cuerpo a 1 MiB, exige JSON y el header
X-Telegram-Bot-Api-Secret-Token, y responde sin exponer errores internos.
El workflow de GitHub incluido ejecuta calidad y pruebas, pero no publica. Una tarea programada de GitHub Actions no conserva SQLite de forma fiable entre ejecuciones y podría romper la protección local contra duplicados.
La automatización de plataforma registra cada claim y su snapshot de agenda. En human_review,
un run termina en awaiting_review; en direct puede avanzar por publishing hasta succeeded.
Los cierres seguros son failed y unknown; este último nunca se reintenta automáticamente. El
resultado operativo también distingue direct_blocked, duplicados y límite diario. Consulta estos
registros con automation-status.
La cola YAML heredada usa estos estados:
draft --approve--> scheduled --claim--> publishing --éxito--> published
^ | |----error claro--> failed
| | +----ambiguo------> unknown
+------withdraw-----+
draft/scheduled --archivo ausente--> cancelled --restore--> draft
failed significa que X rechazó claramente la solicitud; puede revisarse y reencolarse:
colmat-x retry TU_IDunknown significa que la conexión terminó sin confirmar si X publicó. Nunca se reintenta de
forma automática y bloquea toda publicación real hasta su conciliación. Primero revisa
manualmente la cuenta:
# Si sí aparece en X:
colmat-x reconcile-published TU_ID ID_DEL_POST_EN_X
# Si comprobaste que no aparece:
colmat-x retry TU_ID --confirm-not-publishedSi un lease vence, run-due convierte primero ese intento a unknown y termina sin sincronizar
ni publicar nada más. Antes de confirmar que no se publicó, verifica también que el proceso o
servicio anterior ya terminó: el control local impide que un callback tardío cambie un intento
nuevo, pero no puede cancelar una solicitud externa que todavía esté en vuelo.
Esta precaución existe porque el endpoint de creación no documenta una clave de idempotencia: repetir a ciegas una solicitud ambigua puede duplicar el contenido.
Información verificada el 15 de agosto de 2026; revisa siempre la consola antes de activar el servicio:
Configura créditos prepago, límite de gasto y alertas en la consola. El control de duplicados del proyecto compara texto normalizado dentro de su propia SQLite; la revisión editorial todavía debe detectar contenido sustancialmente similar o ya publicado fuera de esta instalación.
- Crear una publicación: endpoint, cuerpo y respuesta oficial.
- Autenticación de API v2: crear contenido requiere contexto de usuario.
- Conteo de caracteres: 280 caracteres ponderados para una publicación estándar; las URL cuentan como 23.
- Límites de uso: consulta también los
encabezados
x-rate-limit-*de cada respuesta. - Precios: en la fecha indicada, crear una publicación costaba USD 0,015 y hacerlo con URL USD 0,200. Los precios pueden cambiar.
- Reglas de automatización: prohíben, entre otros comportamientos, spam, duplicados y automatizar la interfaz web.
- Etiqueta de cuenta automatizada: Colmat debe evaluar si la naturaleza de su operación requiere identificar y vincular la cuenta automatizada.
La validación Python normaliza Unicode y aplica una estimación segura para texto latino y los
casos habituales, pero no es la implementación oficial completa de twitter-text: algunas
secuencias complejas de emoji y rutas de URL pueden contarse de más. El detector cubre esquemas,
www, dominios comunes e internacionales, pero X conserva la validación definitiva. Si el equipo
necesita coincidencia exacta antes del envío, debe integrar el paquete oficial twitter-text de
JavaScript y su suite de conformidad.
ruff format --check .
ruff check .
pytest --cov --cov-report=term-missingLa integración real nunca corre en CI. Haz la primera prueba con una cuenta controlada, un solo
snapshot aprobado y el límite diario en 1.