Celmis
Dieciocho herramientas sobre un grafo de símbolos de tus repositorios: búsqueda de símbolos entre repositorios, consumidores, superficies de API, hallazgos de dependencias y revisiones, bajo las mismas reglas de acceso por repositorio que el resto del producto. Autoalojado, AGPL-3.0.
Documentación
Celmis
Inteligencia de código autoalojada: consulta tus bases de código, revisa solicitudes de extracción y produce la evidencia que un auditor pide
celmis-labs.github.io · Documentación · Inicio rápido · Resultados
Celmis lee tus repositorios una vez y mantiene un grafo de símbolos de ellos. Todo lo demás —preguntas, revisiones, auditorías de dependencias, documentación generada— es una forma distinta de leer ese grafo. Se ejecuta en una sola máquina bajo docker compose, con el proveedor de modelos que elijas detrás, y nada sale de tu red excepto las llamadas que configures.
En la narración más antigua, Kelmis era el fundidor —uno de los tres Dáctilos Ideos, junto a Damnameneo el martillo y Acmón el yunque, a quienes se atribuía el trabajo del hierro. El índice hace aquí la reducción; las superficies son las que trabajan el resultado.
Lo que esto te da que una herramienta solo de diff no puede
Haz una pregunta que abarque dos repositorios, y la respuesta cita ambos:

Eso no es un resultado de búsqueda. La pasarela y el servicio de pagos son repositorios separados sin código compartido, y la respuesta traza la cadena de llamadas entre ellos —luego nota, sin que se lo pidan, que el nombre del tema de Kafka está codificado en ambos y que cambiar uno rompe silenciosamente el otro.
Un revisor que solo lee el diff estructuralmente no puede decir eso. Nunca tuvo el otro repositorio abierto.
Nueve cosas que la gente hace con él
| Eres un PM, un líder de entrega o el cliente y quieres saber en qué estado está un grupo de proyectos, o cómo funciona algo realmente | Pregúntale. Desde cualquier dispositivo, en cualquier lugar, sin reservar tiempo de un ingeniero y sin una reunión cuyo único resultado sea un párrafo → Pregunta al código |
| Un ingeniero nuevo tiene una pregunta que un senior tendría que responder | Cada una de esas saca a alguien experimentado de su flujo, justo en el momento en que ya está cubriendo. La base de código responde en su lugar, con citas archivo:línea → Pregunta al código |
| Dos equipos comparten una integración y ninguno puede leer el repositorio del otro | Cárgalo, concede el derecho a preguntar y deniega las rutas que deben permanecer privadas. Ellos obtienen respuestas; las credenciales se rechazan en el origen → Quién puede ver qué |
| Un cliente o un auditor pide tu SBOM | Un botón, CycloneDX, más un paquete de evidencia cuyo manifiesto les permite verificarlo sin confiar en ti → Dependencias, SBOM y el paquete de evidencia |
| Una vulnerabilidad llega a una dependencia | Fix with Claude entrega a una sesión integrada el repositorio, el paquete y el hallazgo. Edita, el runner empuja una rama y abre un PR → Fix with Claude |
| Una solicitud de extracción necesita revisión | Los agentes leen el diff —y, donde el grafo está construido, quién más llama a lo que se está cambiando, incluso desde otro repositorio → Revisión de solicitudes de extracción |
| Cuarenta servicios necesitan que se les haga lo mismo | Escribe la frase. Celmis muestra a qué repositorios resuelve y espera una segunda pulsación, en lugar de encontrarlos entre cuarenta y pulsar un botón cuarenta veces → Pide trabajo entre repositorios |
| Una alerta se dispara a las 02:00 y no estás en un escritorio | Llega a Celmis, una notificación web llega a tu teléfono, y Fix with Claude abre una sesión que ya tiene la alerta. El runner abre la solicitud de extracción → Alertas y arreglos desde el teléfono |
| Tu propio agente o editor necesita entender la base de código | Apúntalo a /mcp/. Dieciocho herramientas sobre el mismo índice, bajo las mismas reglas de acceso —sin segunda copia de tu código en ningún sitio → Conecta Claude Code y otros clientes MCP |
Las tres primeras son las que una herramienta de revisión de código no hace en absoluto, y son la razón por la que esto es una plataforma en lugar de un revisor: indexa una vez, luego lee ese índice desde el lado del trabajo en el que estés.
Tres números
| 197 segundos | desde git clone hasta seis servicios saludables, medido en un servidor limpio |
| $0.118 | por solicitud de extracción revisada, con el modelo con el que se envía |
| 17 de 50 | en el conjunto offline de Martian Code Review Bench, bajo los tres jueces |
Ese último es deliberadamente poco halagador, y se queda. Mide una de las superficies de abajo —revisión de solicitudes de extracción en PRs aislados de un solo repositorio— y ese conjunto no tiene un servicio hermano para que un símbolo tenga consumidores, así que lo que este producto construye no está en el número en absoluto. La tabla, la auditoría de cada hallazgo que puntuó como falso, y el comando que reproduce ambos están en Resultados.
Tabla de contenidos
- Lo que esto te da que una herramienta solo de diff no puede
- Nueve cosas que la gente hace con él
- Tres números
- Inicio rápido
- Primer usuario y administrador
- Conecta un repositorio
- Pregunta al código
- Revisión de solicitudes de extracción
- Dependencias, SBOM y el paquete de evidencia
- Fix with Claude
- Alertas y arreglos desde el teléfono
- Pide trabajo entre repositorios
- Quién puede ver qué
- Lenguajes y formatos
- Comprobaciones deterministas —sin modelo, sin falsos positivos
- Conecta Claude Code y otros clientes MCP
- Resultados
- Auditoría de los falsos positivos
- Repositorios de prueba
- Configuración
- Operaciones
- Desarrollo local
- Referencia de CLI
- Arquitectura
- Solución de problemas
- Estructura del proyecto
- Procedencia y derechos
Inicio rápido
Lo que necesitas
| Docker | 24+ con Compose v2 | Docker Desktop en macOS/Windows, el motor nativo en Linux |
| Una clave de API de modelo | uno de | Google Gemini, Anthropic, OpenAI, OpenRouter, Groq o Mistral. Una clave gratuita de Gemini es suficiente para evaluar: https://aistudio.google.com/app/apikey |
| RAM | ~4 GB libres | Medido en una ejecución de indexación real: 1.1 GB pico en los cinco contenedores, 565 MB en reposo |
Postgres y Qdrant están incluidos —sin clúster externo que aprovisionar. No se necesita instalar Python ni Node.js para el flujo con Docker.
Inícialo
git clone <your-fork-url> celmis
cd celmis
# Generates .env and fills every secret in the format each one needs.
# Idempotent: run it again after a pull and it fills only the new blanks.
./scripts/init-env.sh
docker compose --env-file .env up -d
# Wait for healthy — first boot pulls three images and applies migrations
docker compose ps
Abre http://localhost.
Aquí no se construye nada. Las tres imágenes se extraen del registro nombrado por CELMIS_REGISTRY en la etiqueta de CELMIS_TAG, para linux/amd64 y linux/arm64 —Apple Silicon y un servidor ARM obtienen ambos una imagen nativa. Construirlas en la máquina que las ejecuta se midió en 485 segundos y 4.2 GB de disco solo para api, que es por lo que instalar ya no significa compilar.
Puerto 80, no 3000: un proxy inverso pone la aplicación y su API en un solo origen y sirve la API bajo /backend. Eso no es una preferencia de despliegue —el paquete del navegador pide una ruta relativa, que es la única forma en que una imagen publicada puede servir cada instalación en lugar de solo aquella en la que se construyó.
Para trabajar EN Celmis en lugar de ejecutarlo, añade la superposición de desarrollo y recuperas las compilaciones locales:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
init-env.sh --check informa de lo que aún está vacío sin escribir nada.
Eso es una representación de la sesión capturada, no una grabación de pantalla —las cifras son las que la ejecución produjo el 26 de agosto de 2026, y la salida de compose es verbatim de logs/03-up.log en el informe de instalación. Está dibujada en lugar de fotografiada porque no se puede levantar una segunda pila junto a una en ejecución: docker-compose.yml fija container_name, así que los nombres chocan.
Detener
docker compose down # stop, keep your data
docker compose down -v # stop and DELETE every volume
Primer usuario y administrador
El formulario de registro en /login funciona en cuanto la pila está saludable. Esa cuenta es un usuario normal —registrarse no concede derechos de administrador, ni siquiera a la primera persona que entre.
El administrador global viene del entorno en su lugar: inicia sesión con CELMIS_MASTER_EMAIL y CELMIS_MASTER_KEY (como contraseña), ambos en .env. Quien ejecuta la caja es el administrador, que es el modelo que una instalación autoalojada quiere en lugar de quien llegó primero al formulario. La ruta no existe a menos que ambas variables estén configuradas, y cada uso de ella se escribe en el registro de auditoría.
Para promover una cuenta normal:
docker compose exec api analyzer auth make-admin you@example.com
Conecta un repositorio
- Configuración → Configuración de LLM — pega una clave de proveedor. Se cifra con
CREDENTIAL_MASTER_KEYantes de tocar la base de datos, y la interfaz solo te vuelve a mostrar el primer y el último cuatro caracteres. - Conexiones — añade un token de GitHub, GitLab o Bitbucket. Usa una cuenta de máquina, no la tuya: un token personal llega a cada repositorio que puedes ver, y los tokens terminan en copias de seguridad, registros y capturas de pantalla.
- Repositorios → Añadir — elige repositorios del proveedor, o pega una URL de clonación. La indexación se pone en cola; el trabajo se muestra en la misma página.
La indexación construye dos cosas desde el mismo checkout: un grafo de símbolos (definiciones, llamadas, importaciones —sobre lo que razonan los agentes de revisión) y embeddings en Qdrant (lo que recupera Q&A). Un repositorio de 120k símbolos tarda aproximadamente un minuto en cuatro núcleos.
Veintitrés lenguajes se analizan en el grafo. Un archivo en un lenguaje sin analizador se dice en voz alta en lugar de omitirse silenciosamente —analyzer graph-stats lista lo que se leyó y lo que no.
Pregunta al código
Una pregunta en un chat, respondida con citas archivo:línea de tantos repositorios como le señales. Las respuestas se transmiten mientras se escriben.
Agrupa repositorios en un proyecto, y la pregunta se hace al grupo:

Las respuestas citan código real, y solo el código que el que pregunta tiene permitido ver —que es lo que hace seguro entregar la pregunta a alguien fuera del equipo que posee el repositorio. Ver Quién puede ver qué.
Revisión de solicitudes de extracción
Los agentes leen el diff y publican hallazgos en GitHub, GitLab o Bitbucket. En lugar de mostrar eso en una captura de esta interfaz, las revisiones se dejan donde se publicaron —cincuenta solicitudes de extracción en proyectos reales, con los comentarios aún adjuntos a las líneas sobre las que se escribieron. Están listadas bajo Repositorios de prueba, y la salida allí no está editada, incluidos los hallazgos que la auditoría de abajo marca como incorrectos.
Donde el grafo está construido, la revisión también lleva lo que el diff no muestra: quién más llama al símbolo que se está cambiando, incluso desde otro repositorio. Donde no está construido, la revisión aún se ejecuta —solo responde la pregunta más estrecha, que es lo que midió el benchmark.
Cada hallazgo que el benchmark puntuó como falso se abrió en la fuente y se publicó con un veredicto. Treinta y tres de setenta y nueve resultaron ser defectos reales que el conjunto dorado no contiene. Ese trabajo está en Auditoría de los falsos positivos, con el código y un enlace permanente para cada uno, para que puedas discrepar de cualquiera de ellos.
Dependencias, SBOM y el paquete de evidencia
La auditoría de dependencias es determinista: auditores nativos donde la herramienta está instalada, OSV en cualquier otro caso, sin modelo involucrado. Un modelo de lenguaje, si le das una clave, escribe el resumen —no decide qué es vulnerable.

Dos archivos salen de cada auditoría, y ninguno necesita una clave de LLM:
- SBOM — un inventario CycloneDX de cada dependencia, su versión, URL del paquete y las vulnerabilidades conocidas contra ella. Este es el archivo que la gente quiere decir cuando dice "envíanos tu SBOM".
- Paquete de evidencia — la auditoría como presentación: cada SBOM, cada hallazgo, la cronología de ejecuciones anteriores y un sha256 de cada archivo, para que un tercero pueda verificar que nada fue editado después sin tener que confiar en nosotros. Una carpeta cuyo contenido puede cambiarse más tarde no prueba nada; el manifiesto es lo que la convierte en evidencia.
Junto a ellos, la documentación técnica generada — PRDs de módulos, documentos de características y guías de integración escritas desde el código — que es tuya para conservar y sigue funcionando después de que termine cualquier suscripción.
Por qué existe esto ahora. A partir del 11 de septiembre de 2026, la Ley de Resiliencia Cibernética de la UE requiere que un fabricante informe una vulnerabilidad explotada activamente a ENISA dentro de las 24 horas. El mandato formal de SBOM llega en diciembre de 2027, pero no puedes responder la pregunta de las 24 horas sin visibilidad a nivel de componentes primero — para informar qué está afectado, tienes que saber qué hay dentro.
Celmis no reclama cumplimiento, y no lo hará. Produce los artefactos que una presentación necesita. Si una presentación es adecuada es un juicio de abogados, y una herramienta que implique lo contrario está vendiendo una falsa sensación de seguridad.
Una cosa más que la página de auditoría dice en voz alta, porque es el fallo que nadie busca: un ecosistema que nadie escaneó reporta cero vulnerabilidades exactamente igual que uno limpio. La cobertura se muestra junto a los hallazgos — qué auditor produjo cada resultado y, más útilmente, qué quedó sin verificar y por qué.
Corregir con Claude
Encontrar algo es la mitad de un bucle. Una sesión integrada de Claude Code se ejecuta dentro de la instalación, edita el checkout, y el ejecutor hace commit, empuja una rama y abre una solicitud de extracción.
Se ejecuta con tu suscripción, en tu máquina
No hay clave que comprarnos ni modelo incluido. Conectas tu propia suscripción
de Claude, como lo hace Cursor: ejecuta claude setup-token una vez en tu propio
portátil y pega el token en Configuración. Se almacena cifrado en el almacén de
credenciales, y la sesión que se ejecuta después lo usa.
Dos ranuras, resueltas en ese orden:
| Personal | tu propia suscripción, invisible para nadie más |
| Espacio de trabajo | una suscripción que un administrador comparte con el espacio de trabajo — aceptación explícita |
La ranura del espacio de trabajo está desactivada a menos que alguien la active, y la interfaz dice por qué antes de guardar: compartir la suscripción de una persona entre varias personas puede violar los términos de consumo de Anthropic. Esa es una decisión para quien tenga la suscripción, y el producto no la tomará en silencio.
Y la sesión no se ejecuta en la nube de alguien. Se ejecuta en tu propia instalación, en un espacio de trabajo aislado por sesión. Lo que eso te compra no es "acceso a la nube" — es que la máquina que contiene tu código es una que controlas, y la alcanzas desde un portátil en una oficina o un teléfono en un tren por la misma razón que alcanzas cualquier servicio que ejecutas: porque es tuyo y está activo.
Eso es todo de Alertas, y corregir desde un teléfono — llega una alerta, y la corrección comienza desde dondequiera que la leíste.
Una vulnerabilidad en la auditoría de dependencias lleva un botón Corregir con Claude. No abre un chat vacío — entrega a la sesión el repositorio, el paquete, ambas versiones y los límites del trabajo, ya escritos:

Aquí hay uno de esos bucles, de principio a fin, sobre un hallazgo real — lodash 4.17.11 con una
vulnerabilidad conocida contra él. 220 segundos desde Iniciar sesión hasta una solicitud
de extracción abierta, en cinco turnos:
Read package.json
→ "Only package.json has lodash; no requirements.txt/pyproject/go.mod exist here."
Edit package.json: "lodash": "4.17.11" → "4.18.0"
mcp__exec__run: cat package.json | grep -A2 lodash; ls
→ "Confirmed no other manifest files exist, so no other changes were needed."
La rama que empujó y la solicitud de extracción que abrió, en GitHub:

Mira lo que no está en ese diff. axios 0.21.1, minimist 1.2.0,
node-fetch 2.6.0 se encuentran en las líneas directamente arriba y abajo — todas desactualizadas, todas
señaladas en la misma auditoría — y todas intactas. La tarea decía solo manifiestos, y
un agente que hubiera ordenado tres más de paso habría sido un peor resultado
de revisar, no uno mejor.
Es una solicitud de extracción en vivo, no una captura de pantalla:
celmis-demo-gateway#6
— rama celmis-agent/b8960e01, un commit, +1/-1.

Dos detalles en esa transcripción valen más que el diff. El agente no asumió que no había otros manifiestos — ejecutó un comando en el sandbox para verificarlo. Y la tarea decía "solo manifiestos, no toques dependencias no relacionadas", así que el cambio es exactamente una línea.
Lo que el ejecutor permite y lo que no
Esto lo decide el ejecutor, no el prompt — que es la parte que vale la pena leer antes de concederle algo a un agente:
- Sin shell propio.
Bash,WebFetch,WebSearchy la edición de notebooks están deshabilitadas. Los comandos se ejecutan a través del contenedor sandbox, que es un servicio separado con su propio uid y un sistema de archivos raíz de solo lectura. - Git es trabajo del ejecutor. El agente nunca hace commit ni empuja. Cuando el trabajo está hecho — o cuando presionas Finalizar y empujar — el ejecutor hace el commit, empuja la rama y abre el PR. Nunca a la rama predeterminada.
- Un límite del proveedor es una pausa, no una pérdida. El primer intento de la ejecución
anterior alcanzó un límite semanal de cuenta a mitad de sesión. La sesión no murió: se movió a
paused, mantuvo su trabajo reanudable durante catorce días y mostró el mensaje del proveedor en lugar de un fallo genérico. Una segunda clave lo terminó. - La sesión es observable. La salida se transmite por SSE con reproducción, así que una reconexión retoma donde quedó en lugar de comenzar en blanco.
La conexión es un token de configuración, mantenido por usuario o por espacio de trabajo. La API nunca lo devuelve una vez guardado — solo si está allí y si sigue funcionando.
Alertas, y corregir desde un teléfono
El bucle anterior comienza desde un hallazgo de dependencia. También comienza desde producción.
Apunta cualquier sistema de monitoreo a la URL de ingesta del espacio de trabajo y sus alertas llegan a Celmis:
POST /webhook/alerts/{workspace_id}.{secret}
El webhook de alertas unificadas de Grafana se analiza tal cual; cualquier otra cosa puede publicar
{"title", "body", "severity", "repo"}. La mitad secreta se almacena cifrada con Fernet
por espacio de trabajo y se compara en tiempo constante. El endpoint está sin autenticación por
diseño — los sistemas de monitoreo no pueden hacer OAuth — y limitado al inquilino por
construcción: un token solo puede escribir en el espacio de trabajo al que pertenece.
Lo que eso compra es la ruta sin portátil:
- Se dispara una alerta. Llega a Celmis y una notificación push web llega a tu teléfono — push web real, VAPID y un service worker, así que llega esté o no la pestaña abierta.
- La abres. La alerta lleva su repositorio, porque
route_incidentpuede tomar un stack trace y decir a qué repositorio y a qué propietario pertenece. - Presionas Corregir con Claude. La sesión se abre ya teniendo la alerta — no un chat vacío.
- El ejecutor hace commit, empuja una rama y abre la solicitud de extracción.

Ninguno de esos cuatro pasos necesita un checkout, una terminal o una máquina en la que confíes. El trabajo ocurre dentro de tu propia instalación; el teléfono es una pantalla para ello.
La misma página lista cada alerta que el espacio de trabajo ha recibido, así que una alerta que nadie atendió es visible en lugar de perderse en un canal.
Lo que se registra mientras esto ocurre. Dos registros separados, y responden preguntas diferentes:
- El rastro de auditoría — JSONL de solo añadir, rotado por tamaño, filtrable por tiempo, modo, operación y repositorio, exportable como CSV. Responde quién hizo qué, a qué repositorio, cuándo. La retención predeterminada es de 90 días y el archivo activo nunca se elimina, solo se rotan los archivos.
- Historial de recursos — muestras y agregados sobre la instalación misma, exportable como CSV para una hoja de dimensionamiento. Responde cuánto costó ejecutar esto.
Una sesión de agente que edita un repositorio a las dos de la mañana desde el teléfono de alguien es exactamente el tipo de evento que tiene que poder reconstruirse después. Lo es.
Pedir trabajo entre repositorios
Cada superficie anterior actúa sobre una cosa a la vez: este repositorio, esta solicitud de extracción, este hallazgo. Esa es la forma correcta para un botón, y la forma incorrecta para un conjunto definido por una condición.
Generar documentación para cada servicio que no tenga ninguna es una oración. A través
de la interfaz, es encontrarlos entre cuarenta y presionar un botón cuarenta veces.
Auditar todo bajo acme-ai que no se haya auditado en treinta días necesita
filtros, selecciones guardadas y operaciones masivas — un subsistema — o necesita una
oración.
Así que hay una caja de oraciones y un catálogo deliberadamente corto de verbos detrás de ella. El trabajo de un solo objeto permanece en los botones, donde pertenece.
Nada se ejecuta en la primera pulsación. La interpretación es una suposición, y estos verbos cuestan dinero y horas — una compilación de vault, una flota de agentes de revisión, una auditoría en un grupo. Así que la respuesta a una oración no es el trabajo; es a qué repositorios esto se resuelve, listado, con un segundo botón debajo. Confirmas el conjunto, no la intención.
El alcance se vuelve a verificar en el momento en que confirmas, no cuando preguntaste. Un repositorio registrado en los segundos entre la pregunta y la pulsación no puede unirse silenciosamente a un conjunto que dijo "todo". La misma regla que gobierna el resto del producto — la verificación se ejecuta donde ocurre el trabajo — gobierna esto.
Un llamador automatizado nunca puede poner en cola una expansión ilimitada. Los verbos están limitados, con alcance al espacio de trabajo y toman un actor explícito: nada aquí lee un contexto de solicitud ambiental, porque un conector que procesa una cola no tiene solicitud, y una función que adivina un espacio de trabajo es la forma en que la automatización de un inquilino alcanza los repositorios de otro inquilino.
Tres llamadores convergen en estos mismos verbos — un agente externo sobre MCP, el agente integrado y un conector de tickets que convierte auditar estos cuatro servicios en trabajo y publica el resultado de vuelta. Comparten una implementación a propósito: un segundo "iniciar una auditoría" es un segundo conjunto de reglas sobre ejecuciones en vivo, deduplicación y reinicios forzados, y la copia que nadie mantiene es la que corrompe la cola.
Quién puede ver qué
El acceso se resuelve por repositorio, por equipo, y gobierna cada superficie a la vez — Q&A, gráfico, búsqueda, MCP:
| configuración | efecto |
|---|---|
visibility: none | el repositorio no existe para la investigación |
visibility: metadata | solo documentación y notas de arquitectura |
visibility: code | el código fuente es legible |
deny_globs | gana incluso en code — credenciales, criptografía, conexiones de base de datos, verificación de secretos |
allow_globs | una lista de permitidos cuando se establece; denegar aún resta de ella |
Esto es lo que hace que el caso del equipo vecino funcione en lugar de ser una promesa: carga el repositorio, concede al otro equipo el derecho de preguntar y deniega las rutas que no deben leerse. Obtienen respuestas; esos archivos se rechazan en la fuente, no se filtran de una respuesta que ya los contenía.
Idiomas y formatos
Diecisiete módulos de gráfico, más una ruta genérica a través de consultas de etiquetas tree-sitter para idiomas sin uno:
Código — Python, TypeScript, JavaScript, Go, Java, C#, C++, PHP, Vue y más a través de la ruta genérica.
Infraestructura — Dockerfile, docker-compose, Helm, manifiestos de Kubernetes, Terraform y flujos de trabajo de CI. Esta es la parte que la mayoría de las herramientas de inteligencia de código omiten, y es por qué una pregunta puede cruzar de una función a la definición de servicio que la ejecuta.
Verificaciones deterministas — sin modelo, sin falsos positivos
Cada verificación a continuación se decide leyendo archivos. Ningún modelo de lenguaje participa en decidir que algo está mal, así que la tasa de falsos positivos es cero por construcción en lugar de por ajuste.
Esa distinción es el punto. Alrededor del veinte por ciento de falsos positivos es donde los desarrolladores dejan de leer los comentarios de una herramienta por completo — uno cuesta segundos de atención, mil te cuestan un equipo que ha aprendido a saltarse todo lo que la herramienta dice. Un modelo se usa aquí para explicar y priorizar, nunca para detectar.
| Verificación | Lecturas | Detecta |
|---|---|---|
install_script | Hooks del ciclo de vida de package.json | una dependencia que ejecuta código en el momento de la instalación |
python_build_hooks | pyproject.toml / setup.py | ejecución de código en tiempo de compilación en un paquete de Python |
cargo_build_script | Cargo.toml | una crate con un build.rs |
non_registry | manifiestos y archivos de bloqueo | una dependencia obtenida de una URL de git o un tarball en lugar de un registro |
suspect_name | la lista de dependencias | typosquats: un nombre a una edición de distancia de un paquete popular |
lock_drift | manifiesto vs. archivo de bloqueo | un archivo de bloqueo que ya no coincide con lo que declara el manifiesto |
cross_repo_drift | el diff del PR y luego los repositorios hermanos | una constante cambiada en un repositorio y olvidada en los demás |
El escaneo ordinario de CVE está deliberadamente no en esa lista. OSV-Scanner
ya lo hace, es gratuito y es el estándar de facto: Celmis lo ejecuta (además del
auditor propio de cada ecosistema: pip-audit, npm audit, govulncheck,
cargo audit) y trata el resultado como una entrada, no como una característica.
Sobre el cumplimiento. Celmis produce los artefactos que una auditoría solicita: un SBOM CycloneDX, un inventario de dependencias, un historial de hallazgos con marcas de tiempo y la evidencia en la que se basa cada hallazgo. No afirma que su presentación sea adecuada, y ninguna herramienta puede hacerlo honestamente: lo que un auditor acepta depende de su sector, su jurisdicción y sus propios controles. Produzca los artefactos; deje que las personas cuyo trabajo es evaluarlos lo hagan.
Conecte Claude Code y otros clientes MCP
Celmis expone su índice a través de MCP, para que un agente pueda buscar símbolos, leer superficies de API y encontrar consumidores en lugar de hacer grep en un checkout que no tiene.
A través de HTTP (la pila en ejecución lo sirve en /mcp/):
# Mint a token (or issue one from Settings → MCP in the UI)
docker compose exec api analyzer mcp issue-token \
--scopes "read:graph read:groups" --duration 86400
// ~/.claude.json (or .mcp.json in a project)
{
"mcpServers": {
"celmis": {
"type": "http",
"url": "http://localhost:8000/mcp/",
"headers": { "Authorization": "Bearer <the token you just minted>" }
}
}
}
A través de stdio, sin el salto HTTP:
{
"mcpServers": {
"celmis": {
"command": "docker",
"args": ["compose", "exec", "-T", "api", "analyzer", "mcp", "serve"]
}
}
}
Lo que un agente puede preguntar
El montaje HTTP sirve 18 herramientas. Responden a las preguntas que un grep no puede:
list_workspace_repos | qué repositorios existen, indexados, documentados, con auto-revisión activada |
search_symbols | dónde se define una función o endpoint, en todo un proyecto |
find_consumers | qué repositorios llaman a un símbolo, incluidos los que nunca clonó |
get_api_surface | los manejadores HTTP que un servicio realmente expone |
get_owner · list_deprecations | quién es dueño de un archivo; qué está en camino de salir y quién aún lo usa |
route_incident | dado un stack trace, a qué repositorio y propietario pertenece |
bootstrap_client · start_integration_walk | lo que un cliente necesita para llamar al servicio de otro equipo |
get_dep_audit · list_dep_findings | la última auditoría y sus hallazgos, de peor a mejor |
get_review · get_review_policy | la revisión más reciente de un PR y qué agentes se ejecutan dónde |
Los dos transportes no son el mismo conjunto. analyzer mcp serve a través de stdio
sirve 13 herramientas más antiguas, con forma de grafo (find_symbol, find_callers,
query_graph); el montaje HTTP sirve las 18 anteriores. Ninguno es un subconjunto del
otro: elija el transporte según las herramientas que desee.
Una guía paso a paso, con los alcances que necesita cada herramienta y los modos de
fallo, está en .claude/skills/celmis-mcp/SKILL.md.
Claude Code la toma automáticamente cuando este repositorio está abierto.
Lo que el agente puede pedir
Una llamada a search_symbols, un símbolo de contrato, y regresa desde dos
repositorios en dos idiomas, para un cliente que no ha hecho checkout de ninguno. La
frontera que un diff nunca cruza es la que esto hace ordinaria.
Dieciocho herramientas, servidas a través de Streamable HTTP en /mcp/ y autenticadas con el
mismo token de portador que /api/:
| Herramienta | Responde |
|---|---|
list_repos | qué repositorios están indexados y qué tan fresco está cada índice |
list_groups | qué repositorios están agrupados, para que las preguntas entre repositorios tengan un alcance |
find_symbol | dónde se define un nombre, en todos los repositorios indexados |
get_symbol | la definición en sí, con su archivo y rango de líneas |
find_callers | qué llama a esto: la pregunta que un grep responde mal y un grafo responde con exactitud |
find_callees | qué llama esto, un salto hacia afuera |
cross_repo_edges | llamadas que cruzan un límite de repositorio |
query_graph | Cypher de solo lectura, para preguntas que las siete anteriores no forman |
cross_repo_edges es la que vale la pena entender, porque es la razón por la que este
producto lleva un grafo de símbolos. Un revisor solo con diff (cada herramienta en la
tabla de referencia anterior, incluida esta cuando el grafo está vacío) puede decirle
que una firma de función cambió. No puede decirle que un servicio en un
repositorio diferente aún llama a la forma antigua, porque nunca tuvo ese
repositorio abierto. Agrupe los repositorios una vez, y esa pregunta se vuelve
respondible:
> which services outside this repo call PaymentGateway.charge?
Esta es también la razón por la que nuestra posición en la referencia subestima el producto en lugar de describirlo: el conjunto de referencia son pull requests aislados de un solo repositorio, por lo que no hay un repositorio hermano para que un borde lo cruce. La capacidad es real y la referencia no puede verla, lo cual es una declaración sobre la referencia, no una afirmación que deba tomar por fe. Apunte un cliente MCP a su propio grupo y compruébelo.
Resultados
Celmis se ejecutó en el conjunto fuera de línea de Martian Code Review Bench:
50 pull requests seleccionados, 173 comentarios dorados escritos por humanos, puntuados
contra el conjunto dorado por un juez LLM. Medido en e0db376 con
gemini-3.6-flash a temperatura 0.1, sin tokens de razonamiento.
| Juez | F1 | Precisión | Recuperación | Rango |
|---|---|---|---|---|
| claude-opus-4.5 | 47.5% | 52.4% | 43.4% | 17 / 50 |
| claude-sonnet-4.5 | 44.9% | 48.0% | 42.2% | 17 / 50 |
| gpt-5.2 | 42.7% | 46.0% | 39.9% | 17 / 50 |
El F1 se mueve 4.8 puntos dependiendo de quién juzgue. El rango no se mueve en absoluto: decimoséptimo bajo los tres.
Toda la ejecución costó $5.88 — $0.118 por pull request — y produjo 153 hallazgos, 3.06 por PR (defecto 114, seguridad 27, contrato 6, estructural 6).
Por qué esta comparación es justa. Martian envía sus propias evaluaciones de 49 herramientas en el repositorio de referencia, producidas por los mismos tres jueces en los mismos 50 PRs contra los mismos dorados. No volvimos a puntuar a nadie: sus filas se toman como se publicaron y la nuestra se añade. Reproduzca toda la tabla con:
python3 autoloop/offline_table.py anthropic_claude-sonnet-4-5-20250929
Fuera de línea no es la tabla de clasificación pública. Martian ejecuta dos referencias. La tabla de clasificación pública es la en línea: 200,000 pull requests reales puntuados por lo que los desarrolladores realmente corrigieron. Esta tabla es la fuera de línea: 50 PRs seleccionados puntuados contra un conjunto dorado. Miden cosas diferentes y los números no son intercambiables. Afirmaciones de la forma "la herramienta X es #1 en Martian" generalmente se refieren a la tabla en línea, una métrica diferente o un juez diferente.
Lo que este número no contiene. El grafo estaba vacío para los 50 PRs
(graph_status nulo, deriva vacía en todos), porque el conjunto de referencia son
pull requests aislados de un solo repositorio: no hay un servicio hermano para que un
símbolo tenga consumidores. La deriva entre repositorios, la razón por la que este producto
lleva un grafo de símbolos, contribuyó exactamente con nada a la puntuación anterior. No
es medible aquí, y no lo estamos afirmando desde esta tabla. Vea
Repositorios de prueba para verlo funcionar en código real.
Auditoría de los falsos positivos
La puntuación de la referencia tiene un piso estructural: el juez compara nuestro comentario contra una lista finita de dorados escritos por humanos, por lo que un hallazgo correcto que el anotador nunca escribió se cuenta como falso por construcción. Abrimos los 79 nuestros en la fuente en el commit medido y asignamos un veredicto a cada uno.
De 79 hallazgos puntuados como falsos positivos, 33 son defectos reales que el conjunto dorado no contiene, 38 son genuinamente incorrectos y 8 no pudieron resolverse desde el código. Eso coloca la precisión verdadera de esta ejecución entre 69.7% y 75.0% en lugar del 48.0% medido — pero esa cifra corregida no puede compararse con nada en la tabla anterior, porque nadie ha auditado las otras herramientas de la misma manera y sus falsos positivos casi con certeza contienen una proporción similar de defectos reales; para comparación con otras herramientas, el 48.0% medido es el número honesto, porque es el mismo método aplicado a todos.
Veinticuatro de los 38 hallazgos genuinamente incorrectos comparten cuatro causas raíz, y ninguna
de ellas es "el modelo es débil": las cuatro se refieren a lo que se le mostró al modelo.
La más grande es un identificador declarado en el mismo archivo pero fuera del extracto
que recibió el agente: un parámetro de método 26 líneas arriba, una importación en la línea 3, un
attr_reader en la línea 18.
El informe completo da la afirmación, el código en ese commit, el veredicto, el razonamiento y un enlace permanente para cada uno de los 79, para que cualquier veredicto pueda disputarse con la misma evidencia frente a usted.
Repositorios de prueba
Cada revisión en la ejecución anterior sigue viva y pública. Estos son pull requests reales de proyectos reales, bifurcados con su historial, que llevan los comentarios en línea que Celmis escribió:
| Bifurcación | PRs |
|---|---|
| celmis-bench/keycloak | 9 |
| celmis-bench/grafana | 10 |
| celmis-bench/discourse-graphite | 10 |
| celmis-bench/cal.diy | 10 |
| celmis-bench/sentry | 6 |
| celmis-bench/sentry-greptile | 4 |
Vale la pena abrir primero:
- keycloak#17 — una desreferencia nula y una pregunta de indexación de códigos de recuperación en el proveedor de almacenamiento de prueba de Keycloak
- grafana#16 — una falla de Storage registrada contra la métrica Legacy, una de tres instancias del mismo error en ese archivo
- cal.diy#11 —
forEachcon un callback asíncrono, por lo que las eliminaciones son de fuego y olvido y eltrycircundante no captura nada - sentry#11 — siete comentarios en línea en un solo PR de consumidor de Kafka
Está leyendo salida sin editar, incluidos los hallazgos que la auditoría anterior marca como incorrectos. Nada se eliminó después de la puntuación.
Configuración
./scripts/init-env.sh escribe .env desde .env.example y
genera cada secreto. El ejemplo envía cada secreto vacío a propósito: una
versión anterior colocaba el comando generador junto a la variable, los archivos dotenv
no tienen comentarios en línea, y cada instalación que lo copió se ejecutó con una contraseña
maestra impresa en el repositorio.
La configuración llega a los contenedores solo a través del bloque environment: en
docker-compose.yml — la imagen no lleva .env. Una variable no nombrada allí
toma su valor predeterminado de código sin importar lo que diga su .env.
GET /healthz informa los relojes de revisión tal como el proceso realmente los resolvió,
que es cómo verifica lo que llegó.
Los relojes están documentados como un conjunto en .env.example, con el invariante que
los une:
REVIEW_LLM_TIMEOUT_SECONDS × (1 + RETRY_FACTOR) ≤ REVIEW_TIMEOUT_SECONDS
Eleve uno y el otro tiene que seguirlo; una prueba lo hace cumplir.
| Variable | Predeterminado | |
|---|---|---|
REVIEW_TIMEOUT_SECONDS | 900 | reloj de pared para una revisión; pasado este, las etapas finales se retiran y el comentario lo dice |
REVIEW_LLM_TIMEOUT_SECONDS | 300 | una llamada de modelo. Eleve a ~600 para un modelo de razonamiento lento |
REVIEW_LLM_TIMEOUT_RETRY_FACTOR | 2.0 | cuánto más largo es el reintento después de un tiempo de espera; 1.0 desactiva la ampliación |
REVIEW_MAX_DIFF_SIZE_BYTES | 500000 | los diffs más grandes se rechazan, no se truncan |
REVIEW_VERIFIER_ENABLED | false | el veto de falsos positivos del LLM |
REVIEW_AGENT_CONCURRENCY | 3 | llamadas de proveedor en vuelo por revisión |
CELMIS_JOB_LEASE_SECONDS | 600 | límite de silencio del trabajador antes de que un trabajo pueda reclamarse |
CELMIS_DEPLOYMENT_MODE | single_tenant | multi_tenant aísla los espacios de trabajo entre sí |
Operaciones
docker compose logs -f api # follow the API
docker compose exec api analyzer graph-stats <repo> # what parsed, what did not
./scripts/backup.sh # Postgres + volumes
./scripts/restore.sh <archive>
Admin → Monitoreo muestra la profundidad de la cola, el gasto por espacio de trabajo y la configuración
de modelo por agente. Uso y costo desglosa el gasto por superficie, para que una compilación
de documentación por lotes no se lea como chat.
Desplegar en un servidor es ./scripts/deploy-on-server.sh v0.1.0, ejecútalo en el
servidor: extrae las imágenes publicadas, levanta la pila detrás de Caddy y
sella la compilación a la que enlaza el pie de página de AGPL. Nada fuera de esa máquina necesita
una credencial para ello. Consulta
docs/ORACLE_CICD.md, o
docs/HETZNER.md para una VM simple.
Desarrollo local
# Postgres and Qdrant from compose, everything else on the host
docker compose up -d postgres qdrant
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
alembic upgrade head
uvicorn src.api.main:app --reload --port 8000
cd web && npm install && npm run dev # http://localhost:3000
pytest -q # the suite
ruff check . # lint, ratcheted at zero
cd web && npx tsc --noEmit
Referencia de CLI
analyzer se instala mediante pip install -e .; dentro de Docker usa
docker compose exec api analyzer …. Cada comando acepta --help.
analyzer init | crea la estructura del espacio de trabajo |
analyzer index <path|url> | analiza un repositorio en el grafo |
analyzer ask "<question>" | una pregunta, respuesta citada |
analyzer chat | sesión interactiva |
analyzer review <provider> <repo> <pr> | revisa una solicitud de extracción; --post publica |
analyzer generate | construye la bóveda de documentación |
analyzer refresh | reindexa lo que cambió |
analyzer graph-stats <repo> | qué se analizó, por lenguaje |
analyzer serve | la API sin Docker |
analyzer review-serve | el receptor de webhook solo |
Subcomandos agrupados: analyzer repo, analyzer group, analyzer auth,
analyzer mcp, analyzer scip.
Arquitectura
┌──────────────┐
GitHub / GitLab ──▶│ webhook │──┐
Bitbucket └──────────────┘ │
▼
Browser ──▶ web (Next.js) ──▶ api (FastAPI) ──▶ Postgres jobs, policies, audit
│ Qdrant embeddings
│ sandbox untrusted execution
▼
model provider
(direct, or via a LiteLLM gateway)
- Postgres almacena trabajos, políticas, historial de ejecución, gastos y el registro de auditoría. La
cola de trabajos duradera es una tabla — la desencolación es
SELECT … FOR UPDATE SKIP LOCKED, y un trabajador renueva su concesión mientras trabaja en lugar de adivinar una duración de antemano. - Qdrant almacena incrustaciones, una colección por instalación con aislamiento de espacio de trabajo aplicado en el filtro.
- sandbox ejecuta cualquier cosa no confiable — un conjunto de pruebas, una compilación — como su propio uid en su propia red, sin base de datos, sin claves y con una raíz de solo lectura.
- LiteLLM es opcional. Establece
LITELLM_PROXY_URLyLITELLM_MASTER_KEYjuntos y cada llamada se enruta a través de la puerta de enlace; deja cualquiera vacío y las claves del proveedor se usan directamente.
Solución de problemas
Un contenedor no se inicia. docker compose logs <service>. La API dice
al inicio qué características opcionales no están disponibles y por qué, en lugar de fallar
silenciosamente.
Las revisiones no producen nada. Verifica GET /healthz para los relojes resueltos, luego
docker compose logs api | grep agent_. Cada agente registra su tiempo transcurrido, su
modelo y su código de fallo.
Un tiempo de espera, no una interrupción. local_timeout significa que el plazo de esta instalación
transcurrió antes de que el proveedor respondiera — aumenta
REVIEW_LLM_TIMEOUT_SECONDS. Deliberadamente no se informa como una falla del
proveedor.
Preguntas y respuestas no cita nada. El repositorio probablemente no está indexado, o está indexado
sin incrustaciones. Repositorios muestra el estado de cada uno; analyzer graph-stats <repo> muestra qué se analizó.
El sandbox está siempre ocupado. SANDBOX_SLOTS es cuántos trabajos se ejecutan a la vez
y es el control que cuesta memoria. SANDBOX_SLOT_WAIT es cuánto tiempo un llamador
espera en la cola antes de que se le diga que vuelva.
Estructura del proyecto
src/
api/ FastAPI app, routers, schemas
review/ PR review — agents, orchestrator, providers, policies
indexing/ parsers, symbol graph, embeddings
qa/ retrieval and answer composition
generation/ documentation vault
llm/ provider clients, error taxonomy, cost ledger
sync/ git providers, the durable job queue, workers
sandbox/ the isolated execution server
mcp_server/ the MCP surface
security/ redaction, patterns, log filtering
web/ Next.js UI (App Router, 16 locales)
tests/ 5200+ tests
deploy/ Caddy overlay and the LiteLLM gateway config
docs/ deploy guides and the end-to-end walk-through
bench/ benchmark harness and results
Procedencia y derechos
Este repositorio tiene un único commit raíz de aproximadamente cien mil líneas — la forma que un volcado de código de origen poco claro tiene para un escáner de procedencia, y una que necesita una explicación en lugar de un encogimiento de hombros. Tiene una: PROVENANCE.md declara la posición de licencia y el origen de el código — el desarrollo ocurrió en privado antes de este commit, y nada de ello es necesario para compilar, auditar o bifurcar lo que está aquí.
Ese archivo es un registro de hechos, no la licencia. La licencia es
AGPL-3.0, con una excepción: cualquier cosa bajo ee/, y cualquier archivo
cuyo nombre contenga .ee., está cubierto por LICENSE_EE en su lugar.
LICENSING.md declara el límite en su totalidad — LICENSE en sí mismo es
el texto AGPL sin modificar, porque un archivo de licencia con un preámbulo delante
no se reconoce como esa licencia.
ee/ no contiene código de producto hoy — el límite se trazó antes de la primera
etiqueta porque agregarlo después significa volver a preguntar a cada contribuyente que ya
ha enviado trabajo bajo un AGPL sin calificar.
Todo lo enviado aquí es AGPL, incluidas las partes que parecen comerciales: la consola de auditoría, uso y gastos, verificaciones de cumplimiento, métricas de instalación. Los controles de seguridad nunca son solo para empresas — el registro de auditoría se escribe bajo AGPL y siempre lo será. Consulta CONTRIBUTING.md para saber dónde va el código nuevo.