Documentación
Todo lo que hace falta para instalar CodeGuard, enrolar un repositorio y entender por qué te bloqueó un commit. Está escrita para que la leas cuando algo salió mal, así que cada sección termina en un comando concreto.
Instalación
CodeGuard es un producto de Windows. Se instala para el usuario actual y no pide permisos de administrador en ningún momento.
Con el paquete instalador
Es la vía normal. CodeGuard-Setup.exe es un asistente
gráfico; lo genera dist\build-dist.ps1 cuando la máquina
tiene Inno Setup.
CodeGuard-Setup.exe # para reparto masivo, sin interacción: CodeGuard-Setup.exe /VERYSILENT
El setup queda registrado en «Aplicaciones instaladas» de Windows con su desinstalador, como cualquier otro programa.
Con el script
PS> powershell -ExecutionPolicy Bypass -File dist\install.ps1
Dos banderas útiles:
| Bandera | Qué hace |
|---|---|
-ApiKey "…" | Guarda la clave del modelo en el Administrador de credenciales de Windows durante la instalación. |
-SkipTrivy | No descarga trivy, que pesa unos 60 MB. Útil en un primer reparto; se puede añadir después con codeguard repair. |
Qué instala y dónde
| Ruta | Contenido |
|---|---|
%LOCALAPPDATA%\CodeGuard\bin | Los binarios: codeguard.exe y el daemon, más el rulepack fijado. |
%LOCALAPPDATA%\CodeGuard\engines | Los motores descargables. |
%LOCALAPPDATA%\CodeGuard\rulepacks | Las reglas de la casa, por versión. |
%LOCALAPPDATA%\codeguard | La base de datos local de análisis (codeguard.db). |
Además añade las dos primeras rutas al PATH del usuario y
deja el daemon arrancando con la sesión.
Los motores y de dónde salen
Son dieciséis, pero CodeGuard no instala dieciséis. Cada uno llega por el camino que le corresponde, y eso decide qué se puede verificar por hash y qué hace falta que ya tengas:
| Cuántos | Cuáles | |
|---|---|---|
| Los instala CodeGuard | 9 |
semgrep · squawk · ruff · mypy · trivy · govulncheck · staticcheck · google-java-format · PMD (más gitleaks, que es la compuerta de secretos y va aparte de los dieciséis) |
| Va dentro de CodeGuard | 1 |
gofmt — no ejecuta el binario, usa go/format, que es gofmt como librería. No hay nada que instalar ni hace falta tener Go. |
| Usan tu cadena de herramientas | 6 |
go vet (Go) · tsc y eslint (Node y el proyecto) · dotnet format, build y list package (el SDK de .NET) |
Si CodeGuard trajera su propio TypeScript, un repositorio que compila
con la 4.9 se analizaría aquí con la 5.9 y daría errores distintos a
los del CI — que es exactamente lo contrario de lo que promete el
producto. Usar el tsc del proyecto es lo que hace que el
veredicto local y el del CI sean el mismo. Con eslint, igual: corre el
que el repositorio configuró, con sus reglas.
Con mypy la decisión fue la contraria —ese binario sí es nuestro— y el criterio es el mismo: mypy sólo aplica si el repositorio ya lo configuró, así que no hay una versión del proyecto a la que respetar. Cuando la herramienta define el veredicto, manda la del proyecto; cuando no, la traemos nosotros.
La contrapartida es que esos seis pueden no estar. Y entonces
el análisis lo dice: la capa aparece como
ausente o degradada y el veredicto enumera lo
que no se revisó, en vez de dar por bueno lo que nadie miró.
checksums.txt que publican los
propios proyectos en sus releases, no de la primera descarga que le
tocó a una máquina. Un binario alterado en tránsito o en un espejo no
llega a instalarse. Y codeguard engines vuelve a
comprobarlo en cada máquina, cuando quieras.
| Motor | De dónde sale | Verificado |
|---|---|---|
| gitleaks · trivy | Release de GitHub del proyecto | Sí — SHA-256 fijado |
| google-java-format · PMD | Release de GitHub, sólo si hay un JDK | Sí — SHA-256 fijado |
| semgrep · squawk · ruff · mypy | pip, contra PyPI | Con las firmas de PyPI |
| staticcheck · govulncheck | go install — se compilan, y eso tarda | Con la suma de comprobación de los módulos de Go |
| gofmt | Dentro del propio binario | — |
| go vet | Tu instalación de Go | — |
| tsc · eslint · biome | El node_modules del repositorio | — |
| dotnet format · build · list package | Tu SDK de .NET | — |
Comprobar que quedó bien
PS> codeguard version PS> codeguard engines # identidad de cada motor PS> codeguard status --todos # enrolamiento de todos los repos
Si codeguard version responde dev, estás
usando un binario compilado a mano en vez del instalado.
Enrolar un repositorio
Lo hace una sola persona, una sola vez por repositorio.
El resto del equipo queda enrolado con un git pull.
PS> cd C:\dev\mi-repo PS> codeguard init
Qué hace init, paso a paso
- Detecta los lenguajes sobre los archivos que git ya rastrea — no sobre el disco, así que
node_modulesy compañía no cuentan. - Reconoce las migraciones y escribe sus rutas en
paths.migrations. - Propone exclusiones según el stack:
node_modules,.nextydistsi hay Node;obj/,*.g.csy*.designer.cssi hay .NET. - Escribe
.codeguard/config.yamlcon las compuertas, los pesos de riesgo y el rulepack fijado. - Instala los hooks en
.githooks/y apuntacore.hooksPathahí. - Genera la baseline en
.codeguard/baseline.txt: escanea el repo entero y suprime lo que ya existía, para que sólo lo nuevo bloquee. - Registra el proyecto y avisa al daemon, así que aparece en el panel sin esperar al primer commit.
Después: versionar
PS> git add .codeguard .githooks PS> git commit -m "enrolar el repo en CodeGuard"
Quien haga git pull recibe la configuración. Le falta sólo
activar los hooks en su copia, porque core.hooksPath es
configuración local de git y no viaja en el repositorio:
PS> codeguard install
Dos avisos que conviene leer
Verificar el enrolamiento
PS> codeguard status # este repo PS> codeguard status --todos # todos los de la máquina
Revisa config, hooks, baseline, rulepack y paridad, y cuando algo falta imprime el comando exacto que lo arregla.
Desenrolar
codeguard forget sólo quita el proyecto de la lista del
agente: deja de verse en el panel y en el explorador, pero
no toca el repositorio y los hooks siguen donde estén.
Para desenrolarlo del todo:
PS> codeguard forget PS> git config --unset core.hooksPath PS> rm -r .githooks .codeguard
Comandos
Todos salen del mismo binario, codeguard.exe. Los hooks lo
invocan por dentro; el resto los escribes tú.
Los motores
Una compuerta de secretos y dieciséis motores deterministas. De un cambio concreto sólo corren los que aplican: si no tocaste Go, gofmt no se ejecuta — y eso se llama no aplica, que no es lo mismo que no corrió.
Las reglas de la casa
semgrep no trae reglas propias: aplica el rulepack de
la casa, fijado por versión en la configuración del repositorio. En
2026.08.2 son 130 reglas, repartidas en
ocho archivos y en los tres pilares —seguridad, calidad y datos—, con
el catálogo completo y sus severidades en
rulepacks/2026.08.2/CATALOG.md.
Esa cifra es contable, no una promesa de folleto:
PS> cd rulepacks\2026.08.2\semgrep PS> (Select-String -Path *.yaml -Pattern '^\s*-\s+id:').Count 130
Las compuertas
Una compuerta decide qué pasa cuando una familia de hallazgos aparece.
Viven en el bloque gates de la configuración, versionadas
con el repositorio.
| Compuerta | Por defecto | Qué cubre |
|---|
Tres reglas sobre las compuertas
- Lo que bloquea aquí, bloquea en el CI. Ese es el criterio entero: una compuerta que bloquee algo que el CI acepta convierte a CodeGuard en un obstáculo.
- El modelo nunca bloquea.
llm: never_blockno es un valor por defecto que se pueda subir: es un principio del producto. - Los secretos no se negocian. No entran en la baseline, no bajan a aviso por el feedback del equipo, y si la compuerta no puede correr, el commit se detiene.
Auto-calibración
Cada hallazgo del panel tiene un botón de útil y otro de falso positivo. Con suficientes votos y demasiados falsos positivos en ese repositorio, una regla baja sola de bloqueante a aviso. La calibración es por repositorio porque un falso positivo lo es en un contexto, no en abstracto. gitleaks queda fuera de este mecanismo, siempre.
PS> codeguard stats # precisión por regla en este repo PS> codeguard stats --all # agregando todos los repos
Configuración
Un solo archivo, .codeguard/config.yaml, versionado en el
repositorio. Es la fuente única de verdad para el agente y para el CI:
por eso la paridad se sostiene sin que nadie la vigile.
El archivo que genera init
version: 1 rulepack: "2026.08.2" languages: [typescript, sql] paths: exclude: ["**/*.log", "**/node_modules/**", "bin/**"] migrations: ["db/migrations/*.sql"] # Motor de esas migraciones: el pilar datos (squawk) sólo analiza # PostgreSQL. Este valor lo decides tú, no la detección automática. # Valores: postgres | sqlite | mysql | sqlserver migrations_dialect: postgres sensitive: [] # rutas de auth/pagos/PII: suben el riesgo generated: [] # Complejidad ciclomática por función a partir de la cual se avisa. # Nunca bloquea: partir una función es decisión de quien la escribe. max_complexity: 15 gates: secrets: block format: block compile: block lint_error: block semgrep_error: block migration_unsafe: block cve_critical: warn_local_block_ci llm: never_block risk: threshold: 35 weights: touches_migration: 30 touches_sensitive: 25 ai_generated: 20 touches_security_config: 20 adds_dependency: 15 touches_query: 15 many_files: 10 tests_only: -20 docs_only: -40 ui: max_visible_findings: 7 auto_open_panel: on_block llm: provider: "azure-foundry" endpoint: "https://TU-RECURSO.services.ai.azure.com/openai/v1" api_key_env: "FOUNDRY_API_KEY" model: "FW-Kimi-K3" model_fast: "gpt-5.6-sol" timeout_ms: 20000 max_diff_tokens: 12000 monthly_budget_usd: 0 price_in_per_mtok: 0 price_out_per_mtok: 0 max_diff_lines: 2000
paths
| Clave | Qué hace |
|---|---|
exclude | Rutas que las capas deterministas no miran. La compuerta de secretos las ignora: un secreto en una ruta excluida bloquea igual. |
migrations | Qué archivos son migraciones. Fuera de esta lista, squawk no mira nada. |
migrations_dialect | postgres (o vacío) activa squawk. Cualquier otro valor lo apaga, y eso es lo correcto: squawk parsea PostgreSQL y contra otro motor sus hallazgos bloquean con un arreglo que rompe el esquema. |
sensitive | Rutas de autenticación, pagos o datos personales. Suben el riesgo del cambio. |
generated | Código generado. Se excluye igual que exclude. |
risk
El riesgo no decide si tu commit pasa — eso lo deciden las compuertas.
Decide si el cambio merece la capa de consejo del modelo, que corre
después y en sombra. Por encima de threshold se pide
consejo; por debajo no, y se registra.
ai_generated merece una nota: CodeGuard reconoce por las
variables de entorno cuando el commit lo lanza un agente de codificación
y le suma 20 al riesgo del cambio. No lo bloquea ni lo trata distinto en
las compuertas: sólo lo mira con más atención.
Los demás valores
| Clave | Por defecto | Qué hace |
|---|---|---|
rulepack | 2026.08.2 | La versión de las reglas de la casa. Es lo que fija la paridad: el CI usa exactamente la misma. |
max_complexity | 15 | Complejidad ciclomática por función a partir de la cual se avisa. Nunca bloquea. |
max_diff_lines | 2000 | Por encima, el análisis degrada a sólo-secretos y lo dice. Un cambio de esa talla no es revisable de todas formas. |
ui.max_visible_findings | 7 | Cuántos hallazgos muestra el panel antes de resumir el resto. |
ui.auto_open_panel | on_block | never, on_block u on_findings. |
Dónde se buscan las reglas
Mandan siempre las reglas instaladas. El rulepack del repositorio se usa como respaldo, y cuando se usa, el análisis lo dice.
El daemon, el orbe y el panel
El daemon arranca con la sesión y es quien corre los motores. El hook —que es efímero— le pasa el diff por una tubería con nombre protegida por SID y DACL de sólo-usuario, y espera el veredicto.
Con una excepción: la compuerta de secretos no se analiza aquí. Corre antes, dentro del proceso del hook, porque es fail-closed y no puede depender de que el agente esté vivo. Cuando frena algo sí le avisa —y por eso el orbe se pone en rojo y el panel se abre con el archivo y la línea—, pero ese aviso nunca lleva el valor de la credencial. Ver si es un secreto.
El orbe
Vive en la esquina de la pantalla. Cambia de clima según el estado y los cambios se funden en unos 0,8 segundos: nunca saltan.
| Estado | Clima | Susurro | Cuándo |
|---|
- Clic — abre o cierra el panel, que florece desde el orbe y se pliega de vuelta.
- Clic derecho — el menú.
- El susurro — al cambiar de estado aparece un texto que se desvanece solo. Mientras el análisis corre no caduca: va diciendo cuántas capas llevan, porque una capa lenta puede tardar más que cualquier temporizador razonable.
- Prohibido por diseño — modales, sonidos, notificaciones emergentes, robarte el foco.
El panel
Sale del orbe y trae, en este orden: el veredicto, la paridad con el CI, los bloqueantes en acordeón con su insignia de pilar, las sugerencias y las capas que no pudieron mirar. Cada hallazgo lleva el código señalado, por qué importa, cómo arreglarlo y los dos botones de feedback.
El vocabulario de las capas
La cabecera del panel enumera todas las capas, no sólo las que fallaron. Existe porque antes «corrió y no encontró nada» y «no corrió» llegaban a la pantalla idénticos — el mismo silencio que este producto existe para no producir.
| Estado | Qué significa |
|---|
El explorador del código
codeguard graph --deep abre un mapa a nivel de función:
quién llama a quién, qué consulta sale de dónde y dónde cayeron los
hallazgos del último análisis. Corre local, sin navegador.
Me bloqueó, ¿ahora qué?
Lo primero: el commit no existe. No hay nada que deshacer. Tu trabajo sigue donde estaba, preparado en el índice.
Leer el bloqueo
El hook escribe en la terminal, una línea por hallazgo:
CodeGuard secretos ✓ CodeGuard formato/lint/tipos/reglas/migraciones ✗ CodeGuard [cookie-sin-httponly] src/api.ts:3 Cookie de sesión sin httpOnly CodeGuard BLOQUEADO: 1 problema(s) que el CI también rechazaría
Entre corchetes va la regla, después el archivo y la línea, y luego qué detectó. Para los bloqueos de las capas deterministas, el panel añade el porqué y el cómo arreglarlo, con el código señalado.
Si es un secreto
Un secreto no se puede silenciar: no entra en la baseline y no baja a aviso por el feedback del equipo.
La compuerta corre dentro del proceso del hook y detiene el commit ahí
mismo. Después avisa al agente: el orbe se pone en rojo y el
panel se abre —auto_open_panel: on_block vale
también para esto— con el archivo y la línea de cada secreto y el aviso
de rotar la credencial primero.
El aviso es un extra, no una dependencia: si el agente está apagado, el commit se bloquea igual. El veredicto sale siempre por la salida de error del commit, con archivo y línea, y el intento queda registrado en el historial local — la pestaña Historial del panel lo lee de ahí.
Si es formato
Se arregla solo. El formateador de cada lenguaje lo corrige:
PS> gofmt -w . # Go PS> ruff format . # Python PS> npx eslint --fix . # TS/JS PS> dotnet format # C#
Si el hallazgo ya estaba antes
No debería bloquear: para eso está la baseline. Si un repositorio se enroló hace tiempo y aparece deuda vieja, regenérala:
PS> codeguard baseline
Si crees que es un falso positivo
Pulsa falso positivo en el panel. No es un botón decorativo: es la entrada de la auto-calibración, y con suficientes votos la regla baja sola a aviso en ese repositorio. Un falso positivo confirmado en una regla determinista se corrige o se apaga; no se tolera.
Si necesitas commitear ya
PS> git commit --no-verify -m "…"
La única cosa que --no-verify no debería saltarse es un
secreto. Si el bloqueo era ese, rota la credencial.
Pasarle los hallazgos a un agente de código
codeguard report escribe
.codeguard/HALLAZGOS.md con instrucciones precisas, pensado
para entregárselo a Claude Code, Codex, Cursor o similares. Es
re-ejecutable: al volver a correrlo marca como RESUELTOS los que ya no
aparecen.
COMPLETADO exige que no queden bloqueantes y que
todas las capas hayan corrido. Si no, dice PARCIAL y con
qué faltó. Importa porque ese archivo es el criterio de terminado que se
le entrega a un agente.
Los agentes de codificación funcionan sin ninguna integración: hacen
git commit, el hook dispara igual, leen el bloqueo por la
salida de error, corrigen y reintentan.
En el CI
Es el mismo binario con el mismo rulepack. Ahí está la paridad entera: no hay dos implementaciones que mantener sincronizadas.
- name: CodeGuard run: | codeguard ci --base ${{ github.event.pull_request.base.sha }} \ --head ${{ github.sha }} \ --format sarif --out codeguard.sarif - uses: github/codeql-action/upload-sarif@<sha> with: sarif_file: codeguard.sarif
Sale con código 1 si hay bloqueantes. Con --shadow registra
todo pero nunca falla el job, que es como se estrena en un repositorio
grande sin parar a nadie el primer día.
Qué cambia respecto a tu máquina
| En local | En el CI | |
|---|---|---|
| CVE crítico | Avisa | Bloquea |
| Base de vulnerabilidades | No se actualiza (no cabe en el hook) | Se actualiza |
| Secretos | El índice (--staged) | El historial del rango |
| Caché de resultados | Sí | No — el runner es efímero y nunca acertaría |
El modelo (opcional)
La capa de consejo es opcional y se elige. Corre después de responderle al hook, así que su latencia nunca toca tu commit, y jamás bloquea.
PS> codeguard config # abrir la ventana de configuración PS> codeguard config --ver # ver la configuración actual PS> codeguard config --probar # una llamada real al modelo
Habla el dialecto de OpenAI y el de Anthropic, con preajustes para Azure AI Foundry, OpenAI, Anthropic, OpenRouter, Groq, DeepSeek, Ollama y LM Studio. Los dos últimos corren en tu propia máquina: el código no sale de ahí.
La clave
api_key_env). La clave vive en el
Administrador de credenciales de Windows, se lee en el momento de
usarla, y no viaja a ningún motor: el entorno de los motores se
construye con una lista de permitidos, no de prohibidos.
PS> codeguard config --guardar-clave FOUNDRY_API_KEY
Lee la clave por la entrada estándar y la guarda en la bóveda del usuario.
Qué sale a la red y qué no
- El diff se redacta antes de cualquier llamada: nada que parezca una credencial sale.
- Sólo se pide consejo si el riesgo del cambio supera el umbral. Por debajo, no hay llamada.
- Lo que vuelve pasa por una verificación anti-alucinación: ¿el archivo está en el diff?, ¿la línea existe?, ¿la confianza es suficiente? Lo que no pasa se descarta y se cuenta.
- El modelo no ejecuta código y no tiene camino hacia ninguna acción.
Con el modelo apagado o sin conexión, CodeGuard funciona igual: el orbe se pone en sin modelo, reglas al día y las compuertas deterministas siguen aplicándose enteras.
Contención de los motores
Los motores son binarios de terceros que corren sobre tu código en cada commit. Corren acotados, y conviene decir con precisión hasta dónde:
- Entorno por lista de permitidos, no de prohibidos — una lista de prohibidos deja pasar el siguiente secreto que alguien añada. La clave del modelo no llega a ningún motor.
- Token restringido — sin privilegios salvo el de recorrer directorios.
- Job object — mueren con el plazo junto a todos sus hijos, con tope de memoria y de procesos, y sin acceso al portapapeles, al escritorio ni a ventanas ajenas.
- Identidad verificada — cada motor descargable se compara contra el SHA-256 que publicaron sus autores.
PS> codeguard engines # identidad de cada motor PS> codeguard engines --auditar # CVEs en lo que repartimos
Firma de código
Problemas frecuentes
«La compuerta de secretos no pudo correr (fail-closed)»
gitleaks no está o no arranca. Es el único error que detiene un commit, y a propósito.
PS> codeguard repair
«Este repo apunta al rulepack X y no está instalado»
Sin rulepack no hay reglas de la casa, y por tanto no hay paridad con el CI. Actualiza la instalación, o vendorea el rulepack en el repositorio como respaldo.
«No cupo en el plazo de esta corrida»
No es un fallo. staticcheck compila el módulo y eslint arranca node: la primera corrida en frío es la cara. Con el caché caliente vuelven a entrar — en una instalación limpia se midió que aparecían fuera de plazo y en la corrida siguiente tardaban medio segundo.
«El agente no está corriendo»
El daemon está parado. Los commits siguen revisándose —el hook corre por su cuenta— pero en frío y sin caché, así que tardan más, y no verás ni orbe ni panel.
PS> codeguard daemon
Un motor aparece como ausente
Es configuración, no avería: ese motor no está instalado en esta máquina. El commit no se bloquea por ello; lo que pasa es que esa capa no miró, y el panel lo dice.
PS> codeguard repair PS> codeguard engines
squawk bloquea con un arreglo imposible
Casi seguro que tus migraciones no son de PostgreSQL y
migrations_dialect se quedó en postgres.
Declara el motor real en .codeguard/config.yaml: eso apaga
la capa, que es lo correcto — squawk sólo entiende PostgreSQL.
«Análisis omitido»
Hay cuatro motivos, y los cuatro se dicen con su nombre:
| Motivo | Qué significa |
|---|---|
| repo no enrolado | Falta .codeguard/config.yaml. Corre codeguard init. |
| sin diff que analizar | No hay nada preparado. |
| merge o revert | Funcionamiento normal: no hay nada que revisar que no se haya revisado ya. |
| todos los archivos tocados están excluidos | Decisión del equipo, no avería. El tono del mensaje y el color del orbe lo reflejan. |