linebreak-gate
Portão de CI com falha fechada para código escrito por IA: bloqueia CVEs conhecidas e serve critérios de aceitação aprovados por humanos para Claude Code/Cursor/Codex via MCP, somente leitura para o agente.
Documentação
linebreak-gate — a compuerta de segurança LineBreak na fronteira git/CI
Veja em ação
Um pull request real, bloqueado de verdade: a compuerta é um check obrigatório, então o botão de merge fica cinza até a CVE ser corrigida ou um humano nomeado registrar uma exceção.

Veja ao vivo — um PR público que você pode abrir agora →
Uma gravação real, sem simulação: a varredura bloqueia uma CVE crítica com falha fechada, o pin é corrigido, a compuerta abre.

O ciclo de especificação: um humano nomeado aprova os critérios, check bloqueia até o critério manual ter uma aprovação registrada, então tudo passa.

Bloqueia merges que carregam vulnerabilidades conhecidas. Uma ferramenta, dois detectores — a varredura de dependências é gratuita; a revisão de IA é o upgrade Pro:
- Varredura de CVE de dependências — gratuita, sem chave — osv-scanner
em todos os ecossistemas (npm, PyPI, Go, Cargo, Maven, …), com um fallback
npm auditpara projetos npm (cobertura apenas npm e sem dados de versão instalada — a GitHub Action falha fechada se o osv-scanner não puder ser instalado em vez de degradar para ele). - IA SAST — Pro — uma revisão de segurança por LLM do código-fonte de primeira parte (injeção,
autenticação quebrada, exposição de segredos, SSRF, desserialização insegura, uso indevido de criptografia)
com verificação adversarial, habilitada por
LINEBREAK_LICENSE_KEY(hospedado, usa créditos) ouANTHROPIC_API_KEY(sua própria chave, tem precedência). Sem uma chave, a varredura de dependências ainda roda e esta etapa é pulada com um aviso.
A compuerta bloqueia e pode propor; nunca limpa automaticamente por ordem de um agente. Um humano aprova a correção ou registra uma exceção — com um motivo e um aprovador — em um arquivo de auditoria commitado no git.
Este é o mesmo núcleo de varredura que alimenta o resto da compuerta de segurança dentro do produto LineBreak (o backend desktop importa este pacote), mas é totalmente independente: uma equipe que nunca usou nada mais da LineBreak pode adicionar a compuerta ao seu repositório e obter aplicação real.
Contribuição e licença. Este repositório é o código-fonte publicado de
linebreak-gate(Apache-2.0): cada release chega aqui e no PyPI a partir do nosso CI, e cada mudança passou pela nossa própria compuerta primeiro — varredura de CVE e critérios aprovados por humanos, a mesma disciplina que vendemos. Relatórios de bugs e pedidos de recursos: abra uma issue ou discussão aqui; lemos tudo. PRs diretos para este repositório não podem ser mesclados (releases fluem pelo nosso pipeline de revisão), então comece com uma issue e seguimos a partir daí.
Início rápido — GitHub Actions
# .github/workflows/security-gate.yml
name: Security gate
on:
pull_request:
permissions:
contents: read
pull-requests: write # for the summary comment
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
# fail-on: high # blocking floor; default: critical
# Optional today; required once license enforcement is enabled.
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
# Enables the AI code review; leave unset for dependency scan only.
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
A action roda linebreak-gate scan, sempre roda report, publica um comentário
no PR (atualizado no lugar a cada push, nunca spam), envia o relatório JSON
- artefatos de auditoria como artefato de workflow, e falha o check conforme o código de saída da varredura.
Torne isso uma fronteira real: exija o check
Um job de CI que pode ser ignorado é um painel, não uma compuerta. No seu repositório:
Settings → Branches → Branch protection rules → sua branch padrão →
"Require status checks to pass before merging" → adicione o job gate (o
nome do job que roda esta action). A partir daí, um PR com uma CVE
crítica não pode ser mesclado pela interface do GitHub.
Início rápido — qualquer outro CI (exemplo GitLab)
O CLI é um pacote Python simples com códigos de saída estritos — 0 passa, 1
achados bloqueantes, 2 erro de ferramenta/configuração (falha fechada: uma falha do scanner
falha o pipeline, nunca é uma passagem limpa). Qualquer CI que respeite códigos de saída
obtém a mesma aplicação:
# .gitlab-ci.yml
security-gate:
image: python:3.11
script:
- pip install linebreak-gate
- curl -fsSL -o /usr/local/bin/osv-scanner
"$(curl -fsSL https://api.github.com/repos/google/osv-scanner/releases/latest
| python -c "import json,sys;print(next(a['browser_download_url'] for a in json.load(sys.stdin)['assets'] if a['name'].endswith('linux_amd64')))")"
- chmod +x /usr/local/bin/osv-scanner
- linebreak-gate scan
- linebreak-gate report
Marque o job como obrigatório (sem allow_failure) e proteja a branch.
Bitbucket Pipelines e Azure DevOps
A mesma compuerta em Bitbucket Pipelines e Azure DevOps: linebreak-gate ci
roda varredura + verificação, publica o comentário do PR e o status do build pela
API do provedor, e sai com 0/1/2. Esta seção está em espanhol para as equipes
que estão pilotando; o runbook passo a passo está em docs/RUNBOOK_BITBUCKET_AZURE.md
no monorepo.
La compuerta es la misma en cualquier CI. El comando linebreak-gate ci hace
en un solo paso lo que la Action de GitHub hace en varios: corre el escaneo de
dependencias y la revisión de código con IA (si hay llave), evalúa los
criterios de aceptación aprobados con el alcance correcto (por historia en el
pull request, todo el paquete en la liberación), deja la evidencia en
.linebreak/ci-out/ (report.txt, criteria.txt, report.json,
comment.md y los registros de auditoría) para publicarla como artefacto,
publica un comentario en el pull request (actualizado en cada corrida,
nunca repetido) y un estado de build, y termina con el código 0 (pasa), 1
(bloquea) o 2 (error de herramienta: la compuerta queda cerrada). El
comentario tiene el mismo contenido que el de GitHub.
Sin credenciales de API, el veredicto se imprime igual, el comentario y el estado se omiten con un aviso, y el código de salida sigue bloqueando el pipeline. La compuerta nunca se abre por no poder comentar.
linebreak-gate init detecta el proveedor por el remoto de git y escribe el
archivo que corresponde; --provider bitbucket|azure|all lo elige a mano.
Los dos archivos que genera son exactamente los de abajo.
Bitbucket Pipelines
# bitbucket-pipelines.yml
# Python image with git and curl; the gate pins osv-scanner itself.
# Alternative: the gate's CI image built from packages/gate/Dockerfile.ci
# (osv-scanner preinstalled), pushed to a registry your workspace can pull.
image: python:3.11
definitions:
steps:
- step: &linebreak-gate
name: LineBreak gate
script:
# osv-scanner drives the dependency scan; without it the gate fails closed.
- curl -fsSL -o /usr/local/bin/osv-scanner https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64
- chmod +x /usr/local/bin/osv-scanner
- pip install --quiet "linebreak-gate>=1.13.4,<2"
# Scan + acceptance criteria, PR comment and build status, exit 0/1/2.
# Repository variables (Repository settings > Pipelines > Repository variables):
# LINEBREAK_LICENSE_KEY optional today; required once enforcement is enabled
# ANTHROPIC_API_KEY enables the AI code review (secured)
# BITBUCKET_ACCESS_TOKEN repository access token, scopes pullrequest:write
# and repository:write, for the PR comment and status
- linebreak-gate ci
artifacts:
- .linebreak/ci-out/**
pipelines:
pull-requests:
"**":
- step: *linebreak-gate
branches:
main:
- step: *linebreak-gate
Variables del repositorio (Repository settings > Pipelines > Repository variables, marcadas como secured):
LINEBREAK_LICENSE_KEY: opcional hoy; requerida cuando se active la exigencia de licencia.ANTHROPIC_API_KEY: habilita la revisión de código con IA; sin ella corre solo el escaneo de dependencias, con aviso.BITBUCKET_ACCESS_TOKEN: token de acceso del repositorio (Repository settings > Access tokens) con permisospullrequest:writeyrepository:write. Es lo que permite el comentario y el estado de build. Alternativa:BITBUCKET_USERNAME+BITBUCKET_APP_PASSWORD.
Protección de rama equivalente a "required check" (Repository settings >
Branch restrictions, rama main): el merge check "Check the last commit for
at least 1 successful build and no failed builds". Los merge checks son parte
de Bitbucket Cloud Premium; con el plan Standard el build rojo se ve en el
pull request y en el estado LineBreak gate, pero no impide el merge por sí
solo (se apoya en revisores obligatorios).
Azure DevOps (Azure Repos + Azure Pipelines)
# azure-pipelines.yml
trigger:
branches:
include:
- main
pr:
branches:
include:
- "*"
pool:
vmImage: ubuntu-latest
# Container job alternative (image built from packages/gate/Dockerfile.ci):
# container: <registry>/linebreak-gate-ci:1
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: "3.11"
displayName: Python 3.11
- script: |
set -euo pipefail
mkdir -p "$HOME/bin"
# osv-scanner drives the dependency scan; without it the gate fails closed.
curl -fsSL -o "$HOME/bin/osv-scanner" https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64
chmod +x "$HOME/bin/osv-scanner"
echo "##vso[task.setvariable variable=LINEBREAK_OSV_SCANNER_BIN]$HOME/bin/osv-scanner"
pip install --quiet "linebreak-gate>=1.13.4,<2"
displayName: Install linebreak-gate
# Scan + acceptance criteria, PR comment thread and PR status, exit 0/1/2.
# Secret variables are NOT exported automatically: map them here. An
# undefined $(NAME) stays literal; the gate treats such values as unset.
- script: linebreak-gate ci
displayName: LineBreak gate
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
LINEBREAK_LICENSE_KEY: $(LINEBREAK_LICENSE_KEY)
ANTHROPIC_API_KEY: $(ANTHROPIC_API_KEY)
- task: PublishBuildArtifacts@1
condition: always()
inputs:
pathToPublish: .linebreak/ci-out
artifactName: linebreak-gate-report
displayName: Publish the gate report
Cuatro pasos manuales, en este orden:
- Crear el pipeline desde
azure-pipelines.yml(Pipelines > New pipeline > Azure Repos Git > Existing YAML) y agregar las variables secretasLINEBREAK_LICENSE_KEYyANTHROPIC_API_KEY(Edit > Variables). Una variable que no existe queda como el texto literal$(NOMBRE); la compuerta la trata como no definida. - Project settings > Repos > Repositories > el repositorio > Security: dar a
la identidad
<proyecto> Build Service (<organización>)el permiso Contribute to pull requests. Sin eso, el comentario y el estado fallan con 403 (y el pipeline sigue bloqueando por código de salida). - Repos > Branches >
main> Branch policies > Build validation: agregar este pipeline como Required, disparo Automatic. Esa política es la que deja el botón Complete apagado mientras el build esté rojo. - Opcional: en la misma página, Status checks: exigir el estado
linebreak/gateque la compuerta publica en cada pull request.
En Azure Repos el disparador pr: del YAML no aplica: la política de Build
validation es la que corre el pipeline en cada pull request. pr: queda para
repositorios alojados en GitHub o Bitbucket y construidos desde Azure
Pipelines (en ese caso el comentario debe publicarse en ese proveedor; la API
de PR de Azure DevOps no aplica y la compuerta lo dice).
Imagen Docker: dos caminos
-
Imagen base de Python +
pip install(las plantillas de arriba). Funciona hoy sin publicar nada; descargaosv-scannerdesde GitHub en cada corrida (si el runner no tiene salida a internet, usar el camino 2). -
Imagen de CI de la compuerta, construida desde
Dockerfile.cien este directorio y publicada en un registro que el workspace o la organización pueda leer:docker build -f Dockerfile.ci -t <registro>/linebreak-gate-ci:1 . docker push <registro>/linebreak-gate-ci:1En Bitbucket:
image: <registro>/linebreak-gate-ci:1y se quitan las líneas decurlypip installdel script. En Azure: un container job (container: <registro>/linebreak-gate-ci:1bajopool) y se quita el paso de instalación. La imagen traeosv-scanner,git,bashycurl, corre como root y no defineENTRYPOINT: los tres son requisitos de Bitbucket Pipelines y de los container jobs de Azure.El
Dockerfilesin sufijo es la imagen del servidor MCP (entrypointlinebreak-gate mcp, usuario sin privilegios, sinosv-scanner) y no sirve para CI.
El comando
linebreak-gate ci [--path .] [--fail-on critical|high|medium|low]
[--story all|auto|<id>] [--manual auto|warn|block] [--stage auto|release|pr]
[--out-dir .linebreak/ci-out] [--no-comment] [--no-status]
--story auto toma la historia del nombre de rama feat/<id> o story/<id>
(con sufijo permitido) cuando es una historia aprobada, y si no evalúa solo
las historias iniciadas. --manual auto es warn en un pull request y
block en cualquier otra corrida; --stage auto es pr en un pull request y
release en el resto. Los proveedores detectados son GitHub Actions, GitLab
CI, Bitbucket Pipelines y Azure Pipelines; en GitHub el comentario lo sigue
publicando la Action.
O ciclo de especificação — autor, aprove, sirva via MCP, aplique
A compuerta também aplica critérios de aceitação aprovados, e todo o ciclo é agnóstico de ferramenta — sem conta LineBreak, sem app desktop, sem servidor:
linebreak-gate spec new # scaffold a draft — fill it with any tool (your
# editor, Claude Code, ChatGPT), or distill it
# from the PRD you already have in Notion/Jira
linebreak-gate spec approve .linebreak/spec-draft.yml \
--approver "Ana Lopez <ana@example.com>" # a human on the record; commits
linebreak-gate mcp install --editor claude-code # or: cursor · codex · copilot
mcp install registra o servidor (.mcp.json, .cursor/mcp.json,
~/.codex/config.toml ou .vscode/mcp.json para GitHub Copilot no VS Code)
e escreve um bloco gerenciado curto, entre marcadores LineBreak, no arquivo que o
agente sempre lê: CLAUDE.md, .cursor/rules/linebreak.mdc, AGENTS.md ou
.github/copilot-instructions.md. O bloco diz que os critérios são o contrato
assinado, são lidos com as ferramentas MCP (não o CLI, não .linebreak/), que
comandos de escrita do CLI precisam que uma pessoa peça, e que a especificação nunca é editada. Ele
segue o idioma da especificação (espanhol ou inglês); rodar install novamente
substitui apenas esse bloco.
linebreak-gate mcp serve o pacote aprovado (.linebreak/spec/) via
MCP (stdio) para Claude Code, Cursor, Codex ou qualquer cliente MCP. Seis ferramentas:
list_stories, get_story (critérios como contexto do agente ANTES de o código ser
escrito), next_story, set_story_status, check_story (o mesmo
motor de avaliação que o CI roda, com escopo para uma história), e spec_status (aprovação +
estado de assinatura offline). Git é o transporte — sem rede, sem conta,
funciona em um clone simples — e nada na ponte pode escrever, editar ou
invalidar um critério aprovado: critérios mudam apenas editando o rascunho
e re-aprovando, com um humano no registro.
spec approve imprime o que está prestes a aprovar (e cada declaração que
mudou, antes e depois) e recusa um rascunho que não seja o do
remoto; --local aprova a cópia no disco conscientemente.
Então linebreak-gate check aplica os mesmos critérios no CI: verificações de máquina
rodam de verdade, critérios manual bloqueiam até uma aprovação registrada. Um critério tests
que executou zero testes falha, e uma verificação pode declarar o que
espera observar (expect: {output, exit}); a seção "Integrity" de
docs/CRITERIA_ENFORCEMENT.md no repositório tem os detalhes. Primeira execução guiada
com o porquê de cada passo:
linebreakapp.com/en/start.
CLI
linebreak-gate init [--path .] [--fail-on critical|high|medium|low] [--force] [--non-interactive] [--provider auto|github|bitbucket|azure|all]
linebreak-gate ci [--path .] [--fail-on ...] [--story all|auto|<id>] [--manual auto|warn|block] [--stage auto|release|pr] [--out-dir DIR] [--no-comment] [--no-status]
linebreak-gate scan [--path .] [--fail-on critical|high|medium|low] [--format summary|json]
linebreak-gate report [--path .] [--format summary|json]
linebreak-gate override --finding <id> --reason "…" --approver <name/email> [--expires YYYY-MM-DD|--days N] [--path .]
linebreak-gate override --criterion <id> --reason "…" --approver <name/email> [--expires YYYY-MM-DD|--days N] [--path .]
linebreak-gate check [--path .] [--format summary|json] [--story <id> ...|--started-only] [--manual block|warn] [--stage release|pr]
linebreak-gate signoff --criterion <id> --approver <name/email> --note "…" [--path .]
linebreak-gate tickets sync [--path .] # retry pending ticket-tracker operations
linebreak-gate spec new [--path .] [--out <file>] [--force]
linebreak-gate spec approve <draft> --approver <name/email> [--role architect] [--path .]
[--against <remote>/<branch>] [--local]
linebreak-gate spec list|next [--path .]
linebreak-gate spec show|check <story-id> [--path .]
linebreak-gate mcp [--path .] # serve the approved spec over stdio
linebreak-gate mcp install [--editor claude-code|cursor|codex|copilot] [--print]
linebreak-gate badge [--format markdown|html|url]
linebreak-gate publish --to <governance-url> [--project <id>] [--path .] [--stage pr|release] [--run-id <id>] [--dry-run]
-
initconfigura um repositório em um único comando: escreve o arquivo de pipeline para o provedor de CI do repositório (workflow do GitHub Actions,bitbucket-pipelines.ymlouazure-pipelines.yml, detectado a partir do remote do git ou escolhido com--provider; nunca sobrescreve um existente sem--force), opcionalmente escreve.linebreak/gate.yml, oferece armazenar os segredos via GitHub CLI e exigir a verificaçãogate— e imprime os links exatos de configuração para qualquer coisa que não puder fazer por você. -
cié a execução completa para provedores de CI sem uma Action nativa (Bitbucket Pipelines, Azure DevOps): scan, verificação com escopo, diretório de evidências, comentário em PR e status de build através da API do provedor, sair com o pior código. Veja a seção Bitbucket / Azure acima. -
scanexecuta ambos os detectores, escreve artefatos de auditoria nativos do git sob.linebreak/audit/, e sai com 0/1/2. -
reportrenderiza o scan registrado: contagens por severidade e cada achado com ID de CVE, CVSS, link de advisory e status de override.--format jsonpara máquinas. -
overrideregistra um reconhecimento aprovado por humano de um achado exato — o pacote + versão instalada + tupla de CVE. Um CVE diferente, uma versão atualizada ou um novo achado ainda bloqueia.--reasone--approversão obrigatórios; o registro vai para a trilha de aprovação do artefato. Faça commit do.linebreak/audit/*.jsonatualizado para que o CI o veja.--expires YYYY-MM-DD(ou--days N) limita a aceitação no tempo: após essa data, o achado bloqueia novamente comoexpired_riskaté que a aceitação seja renovada ou o achado seja corrigido (veja Riscos aceitos e tickets). -
checkavalia os critérios de aceitação aprovados (.linebreak/spec/, entregue porspec approve) contra a árvore de trabalho:build/tests/commandexecutam de verdade,manualexige um sign-off registrado. Saída 0 se tudo satisfeito (ou sem bundle — um no-op limpo), 1 bloqueando (falha ou precisa de sign-off), 2 erro de ferramenta/config/bundle (falha fechada). Escreve.linebreak/audit/criteria.json. Flags de escopo (veja Escopo da verificação):--story <id>(repetível) avalia apenas essas histórias,--started-onlyavalia apenas histórias com estado local iniciado,--manual warnrelata sign-offs ausentes sem bloquear,--stage prpula critérios marcadoscheck.when: release(listados como somente-release, não avaliados; o padrão--stage releaseos avalia). O resumo e o JSON declaram o escopo. -
signoffregistra um sign-off atribuído por humano para um critériomanualsob.linebreak/spec/signoffs/(aditivo;--approvere--noteobrigatórios). Ele vincula-se ao critério como aprovado — editar o critério e re-aprovar a especificação torna sign-offs anteriores obsoletos. Faça commit do registro. -
override --criterionregistra um override aprovado por humano para um critério de máquina falho em.linebreak/audit/criteria.json— mesma filosofia dos overrides de CVE: possível, sempre atribuído, obsoleto assim que o critério é editado. Outros critérios bloqueantes ainda bloqueiam. -
spec new/spec approve— o caminho de autoria agnóstico de ferramenta (veja a seção spec-loop acima): crie um rascunho, preencha-o com qualquer ferramenta, entregue-o como o bundle aprovado com uma aprovação humana atribuída, com commit. Aprovações locais não assinadas são marcadas comoidentity_source: client; assinaturas criptográficas vêm do serviço de governança (chave de licença). -
spec listimprime o bundle de critérios de aceitação aprovados: cada história, seus critérios com tipos de verificação e a atribuição do aprovador. Somente leitura. Saída 0 em um bundle válido ou quando nenhum existe; saída 2 em um bundle malformado (falha fechada na estrutura).spec next/show/checksão os gêmeos de CLI das ferramentas da ponte MCP.
Publicar no painel
linebreak-gate publish --to https://governance.example --project <id> envia
a execução registrada (critérios, achados, sign-offs, overrides, atestação) para um
serviço de governança LineBreak para que executivos e auditores a vejam no painel.
O bearer vem de LINEBREAK_GOV_TOKEN (um token emitido por esse serviço),
senão LINEBREAK_GOVERNANCE_TOKEN; --to tem como padrão
LINEBREAK_GOVERNANCE_BASE_URL e --project para LINEBREAK_GOV_PROJECT. Execute
após scan e check, como a última etapa do job.
Publicar nunca bloqueia uma mudança: um token ausente, um serviço inacessível
ou um corpo rejeitado imprime um aviso e sai com 0. O veredito já foi dado
por scan/check; publish apenas o relata. Sob GitHub Actions, o run_id
é derivado do ID da execução e da tentativa, então re-executar a etapa é idempotente
no lado do servidor. --dry-run imprime o corpo sem enviar.
Credenciais de governança: uma única consulta
Todo comando que fala com o serviço de governança (check para os sign-offs do painel
e a configuração do tracker, signoff / override para a
identidade verificada, publish, report --from-governance) encontra a URL
do serviço, o token e o projeto da mesma forma, o primeiro que se aplica:
- variáveis de ambiente (
LINEBREAK_GOVERNANCE_BASE_URL,LINEBREAK_GOVERNANCE_TOKEN,LINEBREAK_GOV_TOKEN,LINEBREAK_GOV_PROJECT); uma variável definida sempre vence; ~/.config/linebreak/governance.env($XDG_CONFIG_HOME/linebreak/quando definido);~/.config/linebreak/governance-local.env(uma instância local);~/.linebreak/env, mantido para compatibilidade com o aplicativo desktop.
Arquivos são linhas KEY=value (export e aspas permitidas). Apenas o primeiro arquivo
que carrega um token é usado, por inteiro: uma URL de um arquivo nunca é pareada com um
token de outro. LINEBREAK_GOV_CREDENTIALS=off desativa os arquivos. Em CI
não existem tais arquivos, então os pipelines se comportam como antes; em uma estação de trabalho, um
token em um deles faz signoff e override registrarem a identidade de governança
verificada (e recusarem se esse token falhar), exatamente como a variável
exportada faz.
Ler a execução publicada: report --from-governance
O scan roda em CI; report sozinho apenas lê o scan registrado nesta
máquina. linebreak-gate report --from-governance --project <id> lê a
última execução que o CI publicou (GET /v1/reports/projects/{id} para a lista,
GET /v1/projects/{id}/gate-runs/{run_id} para a execução): achados por severidade
com suas aceitações e marcas KEV, as contagens de critérios e o veredito como
essa execução o publicou. --stage pr|release pega a última execução daquele estágio,
--run-id uma específica, --format json a execução inteira. Somente leitura; saída 2
quando o serviço não pode ser lido, 0 caso contrário (também quando nada foi publicado
ainda).
Verificar a evidência sem rede: verify
A instância de governança assina com sua chave Ed25519 (o mesmo kid que assina a
aprovação da especificação) o expediente de cada corrida, cada exceção
e cada assinatura feita a partir do painel. "Baixar evidência" no painel (ou
GET /v1/projects/{id}/gate-runs/{run_id}/evidence) baixa um arquivo
linebreak-evidence/v1 com cada registro, seu texto assinado, sua assinatura, seu kid e
as chaves públicas da instância.
linebreak-gate verify evidencia-pagos-2026-09-12-55555555.json
linebreak-gate verify evidencia.json --key=<kid>=<llave pública base64>
Chaves de confiança: approvals.public_keys de .linebreak/gate.yml (em
--path, por padrão .) e as --key. A chave que o próprio arquivo traz
só conta com --key-from-file, e o comando avisa que é preciso confirmá-la
por outro canal (GET /v1/keys ou gate.yml). Não usa a rede.
| Saída | Significa |
|---|---|
| 0 | tudo assinado pela instância e verifica |
| 1 | uma assinatura não verifica ou seu kid não é de confiança |
| 3 | o assinado verifica, mas há registros sem assinatura da instância (anteriores a esta versão) |
| 2 | o arquivo não pode ser lido ou seu formato não é conhecido |
Badge
Mostre aos visitantes que o repositório é protegido. linebreak-gate badge imprime um trecho de README
pronto para colar (sem chamadas de rede — o badge estático do shields.io está totalmente codificado
em sua URL); --format html|url para a tag ou variantes de URL simples:
[](https://www.linebreakapp.com/en/gate)
Escopo da verificação: por história em PRs, completo no release
Uma equipe que aprova o sprint inteiro antecipadamente (o fluxo que este gate promove: especificação aprovada antes do código) veria, de outra forma, cada PR bloqueado por critérios de histórias que ninguém começou. A correção é escopo, não um gate mais fraco:
- Scan sempre. Os scans de dependências e código rodam em cada PR e no release, sem mudanças.
- Verifique a história em PRs.
linebreak-gate check --story <id>avalia apenas os critérios daquela história (--storyrepete);--story autopega a história de um branchfeat/<id>oustory/<id>, senão apenas histórias iniciadas.--started-onlyavalia apenas histórias cujo estado local édoing,reviewoudone(o estado quespec nexte a ponte MCP escrevem); histórias sem estado são listadas como não iniciadas e não contam. Quando nenhuma história está iniciada (sem arquivo de estado, um ilegível ou um tracker externo sem estados locais), o escopo não seleciona nada e a verificação é saída 2, nunca uma aprovação vazia.--manual warnrelata critériosmanualsem sign-off como pendentes em vez de bloqueantes, para que um sign-off que pertence ao release não segure um PR. - Verifique tudo no release. O job de release roda o bundle completo com
--manual block(o padrão): cada critério de cada história, cada critériomanualcom sign-off. Cada execução escreve.linebreak/audit/criteria.jsoncarimbado com seuscopeepending_signoffs, para que uma execução com escopo ou relaxada seja evidência dessa execução e nunca possa ser lida como um veredito completo (e o CI nunca envia um obsoleto).
O resumo imprime uma linha scope: (modo, histórias avaliadas, critérios contados,
política manual, estágio), uma linha release-only (not evaluated at stage pr): quando
critérios foram pulados e uma linha pending sign-off: por sign-off ausente;
o JSON carrega o mesmo sob scope (incluindo stage e
release_only) e pending_signoffs. Um id --story desconhecido
é saída 2 (um escopo que não nomeia nada é um erro, nunca uma aprovação).
Na GitHub Action, o mesmo padrão são três entradas (stage é descrito abaixo). story é all (cada
história, o padrão), auto (inferir o id de um branch feat/<id> ou story/<id>,
um slug final permitido como em feat/<id>-add-login, quando é uma
história aprovada; senão apenas histórias iniciadas) ou um id explícito. manual
é warn ou block; deixado vazio, é warn em pull_request e block em
qualquer outro evento. O comentário do PR mostra o escopo resolvido, as histórias não
iniciadas e os sign-offs pendentes.
Mudança de comportamento para usuários existentes de @v1 (1.11.0): o padrão manual em
eventos pull_request agora é warn, então um critério manual sem
sign-off não bloqueia mais um PR, a menos que o workflow defina manual: block. Adicione
o job de release abaixo (ou defina manual: block no job de PR) para manter
os sign-offs aplicados.
# .github/workflows/security-gate.yml, PR gate + release gate
name: Security gate
on:
pull_request:
push:
tags: ["v*"] # the release job runs on release tags
permissions:
contents: read
pull-requests: write
jobs:
gate:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
story: auto # this PR's story, or started stories only
manual: warn # sign-offs are listed, not blocking, on PRs
stage: pr # check.when: release criteria are listed, not evaluated
release-gate:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Baktun-Studio/linebreak-gate@v1
with:
license-key: ${{ secrets.LINEBREAK_LICENSE_KEY }}
story: all # every story, every criterion
manual: block # every manual criterion needs its sign-off
stage: release # the default; release-only criteria run and block
Exija a verificação gate no branch padrão e o job release-gate
antes de publicar. CI genérico: as mesmas duas invocações da CLI, com os
códigos de saída respeitados.
Critérios somente-release: check.when: release
Um critério verificado por um script contra um ambiente de staging compartilhado falha
para cada PR no momento em que o staging regride, incluindo o PR que o corrige.
Marque-o como when: release na especificação:
- id: checkout-smoke
statement: The checkout smoke script passes against staging.
check:
type: command
payload: ./scripts/smoke-staging.sh
when: release # absent means always
Com stage: pr no trabalho de PR (check --stage pr), tais critérios não são
avaliados: suas verificações nunca são executadas, o relatório os lista como [release-only]
com sua própria contagem, e eles não são aprovados nem reprovados. O trabalho de release
(stage: release, o padrão) os avalia como sempre, e uma falha ainda
bloqueia o release. O campo faz parte do conteúdo do critério, então
adicioná-lo a um critério aprovado reativa as aprovações e
substituições desse critério como qualquer outra edição.
Release em duas fases: check.when: attestation e --phase
Um critério como "o release carrega uma atestação assinada" é assinado EM uma
execução de release verde, então a execução que o produz não pode exigi-lo. Marque esse
critério manual como when: attestation:
- id: release-attestation
statement: The release carries a signed attestation of its green run.
check:
type: manual
when: attestation
Então faça o release em duas execuções: check --phase prepare (entrada da Action phase: prepare) avalia tudo exceto esse critério, que aparece como
[awaiting-attestation]; o aprovador assina contra essa execução; check
(--phase verify, o padrão) o aplica como qualquer critério manual, e
essa execução é o release. Outros critérios manual bloqueiam em ambas as fases.
Ambientes compartilhados: check.resource e check.environment
- id: f1-e2e
statement: Flow F1 end to end against staging.
check:
type: command
payload: node scripts/qa/flows.mjs --base "$STAGING_URL" --flow f1
when: release
resource: staging-demo-merchant # runs alone; a clash is reported as such
environment:
name: staging
version_url: ${STAGING_URL}/version # answers the deployed commit
Verificações que nomeiam o mesmo resource nunca se sobrepõem em uma máquina (um bloqueio
de SO), e uma falha é reexecutada uma vez sozinha: passar sozinha é [collision] (não é um
defeito, não bloqueia), falhar novamente é uma falha "reproduzida quando reexecutada
sozinha". Entre máquinas, serialize os trabalhos no CI ou dê a cada execução de ponta a ponta
seu próprio locatário descartável. environment registra a versão implantada que a
verificação mediu e avisa (nunca bloqueia) quando não é o commit avaliado:
atrás, à frente, divergente, inalcançável ou alterado durante a execução da verificação.
Configuração — .linebreak/gate.yml
O rigor do portão é governança, então ele vive no repositório — mudar o limiar é em si um PR: visível, revisável, atribuível no histórico do git.
# .linebreak/gate.yml
fail_on: critical # critical (default) | high | medium | low
exclude_paths: # optional: root-relative globs excluded from scanning
- fixtures
- "sandbox/*"
code_scan: auto # auto (run when model credentials are set) | on (required) | off
security: # exploitation policy, see "Prioridad por explotación" below
block_kev: true # default: anything in CISA's KEV catalog blocks
epss_threshold: 0.5 # default: off
criteria:
enforce: true # default: true whenever a spec bundle exists; false disables
# criteria checking only (the security scan is unaffected)
integrity: warn # warn (default) | block | off: shared tests between stories,
# statements naming identifiers gone from the code, commands that ran 0 tests
risk_acceptance: # optional: accepted risks expire (see the section below)
max_days: 90 # longest acceptance allowed
required: true # an override without --expires/--days is refused
tickets: # optional: mirror every acceptance into your tracker
provider: jira # jira | github
project: SEC # Jira project key, or "owner/repo" for GitHub Issues
labels: [linebreak]
Precedência: flag --fail-on explícita / entrada da Action → .linebreak/gate.yml →
padrão embutido (critical). fail_on pode estar no nível superior ou sob
security: (não ambos). Uma configuração inválida é um erro de ferramenta (saída 2):
um arquivo de governança quebrado nunca cai silenciosamente em um padrão.
Prioridade por exploração
Um CVSS alto diz quanto dano uma vulnerabilidade causaria; não diz se alguém a está usando. Desde 1.13.2, o portão enriquece cada achado de dependências com duas fontes públicas e ordena e bloqueia por exploração real:
- KEV (catálogo de vulnerabilidades exploradas conhecidas da CISA): "estão explorando hoje".
- EPSS (FIRST): probabilidade de exploração nos próximos 30 dias, entre 0 e 1: "provável em 30 dias".
Política
# .linebreak/gate.yml
security:
fail_on: high # piso de severidad (o en la raíz del archivo, no en ambos)
block_kev: true # cualquier hallazgo en KEV bloquea, sea cual sea su severidad (por defecto)
epss_threshold: 0.5 # bloquea desde esta probabilidad a 30 días (apagado por defecto)
intel_max_age_hours: 24 # antigüedad máxima de la caché (por defecto 24 h)
intel: true # false apaga el enriquecimiento por completo (red y caché)
Um achado bloqueia por qualquer um de três gatilhos: severidade em ou acima do
piso, presença no KEV (com block_kev) ou EPSS em ou acima do limiar. O
motivo fica registrado em cada achado (block_reason) e agregado na
execução (block_reasons) com exatamente dois valores, que o serviço de
governança lê separadamente: kev (no catálogo) e vulnerability (piso de
severidade ou limiar EPSS). Quando ambos se aplicam, vence kev. Uma substituição
registrada com linebreak-gate override --finding <id> reconhece o achado
exato como sempre, independentemente do motivo.
Ordem e pontuação
O report, o veredito e o JSON listam os achados nesta ordem:
primeiro os que estão no KEV, depois por EPSS do maior para o menor, depois por
severidade e CVSS. Um achado sem pontuação EPSS (sem CVE, ou um CVE que a FIRST
ainda não pontuou) vai depois dos pontuados, ordenado por severidade. Cada um
mostra seu rótulo: explotada activamente (KEV), EPSS 0.93 ou sem dados de exploração.
[BLOCKING: kev] CVE-2021-44228 critical cvss 10.0 log4j-core@2.14.1
explotada activamente (KEV), EPSS 1.00, CISA due 2021-12-24 risk 100
[BLOCKING: vulnerability] CVE-2024-3094 critical cvss 10.0 xz@5.6.0
EPSS 0.86 risk 100
[below floor] CVE-2019-0001 low cvss 2.0 x@1
EPSS 0.03 risk 20
O risk_score sobe com a exploração e nunca desce: um achado no KEV vale
100 independentemente da severidade; o EPSS eleva a pontuação até sua probabilidade
em porcentagem (um medium com EPSS 0.93 vale 93); a severidade é o piso
(critical 100, high 80, medium 50, low 20). Sem dados de exploração, os
números são os de sempre.
Cache e modo sem rede
As duas fontes são armazenadas em .linebreak/cache/ (a pasta é ignorada sozinha
no git; o registro de auditoria é .linebreak/audit/security.json, não o
cache). Com cache fresco, não há nenhuma chamada de rede. Com cache vencido,
consulta-se a rede e, se falhar, usa-se o cache vencido e o relatório informa
(stale). Sem rede e sem cache, os campos epss e kev ficam vazios
(null) e o relatório diz sin datos de explotación: o enriquecimento
nunca converte uma varredura em erro nem muda o veredito que teria dado
sem dados. LINEBREAK_OFFLINE=1 pula a rede (o cache continua sendo usado), útil
em pipelines sem saída para a internet; security.intel: false desliga tudo.
No artefato, kev: true é "está no catálogo", kev: false é "foi
consultado o catálogo e não está" e kev: null é "não foi possível consultar" (ou o
achado não tem CVE): para um auditor, são três fatos distintos. A execução
registra também exploit_intel (fonte e versão do catálogo dessa
execução) e verdict com seus block_reasons.
Fontes: https://api.first.org/data/v1/epss (por lotes de CVE) e
https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json.
Nenhuma requer credenciais.
Papéis e identidade de quem assina (.linebreak/roles.yml)
Sem este arquivo, qualquer um pode executar signoff ou override e o nome
que fica no registro é o que a pessoa escreveu (identity_source: client). Com ele, o portão sabe quem pode assinar o quê:
# .linebreak/roles.yml
roles:
ciso:
members: [ana@example.com]
can:
sign_criteria: ["*"] # patrones sobre el id del criterio, la historia o la épica
approve_overrides: ["*"]
accept_security_risk: [critical, high, medium, low]
qa:
members: [luis@example.com, "github:luis-qa"]
can:
sign_criteria: ["e12-*"]
approve_overrides: []
accept_security_risk: [low, medium]
policy:
require_roles: true # una firma sin rol autorizado se rechaza
require_verified_identity: false # true: una identidad solo declarada no cuenta
signoffeoverrideregistram o papel com o qual se assina:--role, ou é inferido quando a pessoa tem exatamente um papel que o permite. Se não tiver nenhum erequire_rolesestiver ativo, o comando falha e diz qual papel seria necessário. Aceitar um achado de segurança é limitado por severidade.check,scanereportreavaliam cada assinatura e cada substituição contra os papéis vigentes. Um registro cujo papel não existe mais, cujo membro saiu, ou que não cobre esse critério, é rejeitado com motivorole_deniede uma linha legível (role denied: <id> (<historia>): ...); bloqueia mesmo que a verificação seja executada com--manual warn. O registro não é apagado: fica como evidência e uma assinatura posterior autorizada o substitui.- Identidade: além de
client, o portão reconhecevcs(no CI, usa o ator do provedor:GITHUB_ACTOR,GITLAB_USER_EMAIL,BITBUCKET_STEP_TRIGGERER_UUID,BUILD_REQUESTEDFOREMAIL) egovernance(comLINEBREAK_GOVERNANCE_BASE_URLeLINEBREAK_GOVERNANCE_TOKENconsultaGET /v1/mee usa essa identidade; um token configurado que falha interrompe o comando, nunca cai silenciosamente em um nome escrito). A identidade verificada manda; o que foi escrito em--approveré salvo comodeclared_approver. Na lista de membros, pode-se colocar um e-mail ou a formaproveedor:login(github:luis-qa). policy.require_verified_identity: truefaz com que uma assinatura comidentity_source: clientseja registrada como declarada, mas não conte: o comando avisa ao assinar echecka rejeita com motivoidentity_unverified.- Sem arquivo, ou com ambas as políticas em
false, nada muda: nada é rejeitado e os registros carregamrole: null. Um arquivo malformado é erro de configuração (saída 2), nunca uma omissão silenciosa.
Registros de auditoria
Cada varredura e cada substituição são registradas em .linebreak/audit/security.json
(dependências) e .linebreak/audit/code.json (AI SAST) — o mesmo formato de
documento versionado que as ferramentas LineBreak escrevem, carregando achados (ID do CVE,
CVSS, link do aviso), mecanismo de varredura, carimbo de tempo, ator e a trilha de aprovação
com o motivo + aprovador de cada substituição, o papel sob o qual foi feita e a
fonte de identidade (client, vcs, governance). Quem afrouxou o portão, e
quando, é em si auditável.
Riscos aceitos e tickets
Uma exceção registrada com override era para sempre: a pessoa que
aceitou o risco sai e o risco fica. Desde a versão 1.13.3, cada
aceitação pode (ou deve) expirar, e cada exceção também vive no gerenciador de
tickets da equipe, para que a rastreabilidade fique na ferramenta deles e não apenas
no LineBreak.
Expiração
linebreak-gate override --finding <id> --reason "…" --approver ana@example.com --expires 2026-12-31
linebreak-gate override --criterion <id> --reason "…" --approver ana@example.com --days 30
-
--expires YYYY-MM-DDou--days Ndefinem a data em que a aceitação deixa de valer. Vale até esse dia inclusive; no dia seguinte, o achado (ou o critério) volta a bloquear com motivoexpired_risk. O relatório diz qual achado é, quem o aceitou, quando expirou e as duas saídas: renovar com outrooverrideou corrigir. -
Com menos de 14 dias para expirar,
scan,reportecheckavisam sem bloquear (expiring risk/expiring exception). -
A política vive em
.linebreak/gate.yml:risk_acceptance: max_days: 90 # ninguna aceptación puede ir más allá de 90 días required: true # un override sin vencimiento se rechaza (exit 2)Sem este bloco, aceitações sem data continuam permitidas (o comportamento anterior). A mesma política se aplica a achados de segurança e a exceções de critérios.
-
Renovar é registrar um
overridenovo sobre o mesmo alvo. A aceitação anterior não é sobrescrita: fica no histórico do artefato (.linebreak/audit/security.json,code.json,criteria.json) e a mais recente é a que vale. Cada entrada guardaexpires, eticketquando há gerenciador configurado. -
Em
--format json,scan/reporttrazemblock_reasons(vulnerability,expired_risk) e, por detector,expiredeexpiring;checktrazblock_reasons,expired_overrideseexpiring_overrides.
Tickets (Jira primeiro, GitHub Issues também)
# .linebreak/gate.yml
tickets:
provider: jira # jira | github
project: SEC # clave del proyecto en Jira, o "owner/repo" para GitHub Issues
labels: [linebreak] # etiquetas que llevan todos los tickets
main_branch: main # rama cuyos scans abren tickets de atención (opcional)
issue_type: Task # solo Jira: tipo de incidencia a crear (opcional)
Credenciais por ambiente, nunca no repositório: JIRA_BASE_URL,
JIRA_EMAIL e JIRA_API_TOKEN para Jira (API REST v2, funciona em Cloud e em
Data Center); GITHUB_TOKEN para GitHub Issues.
O que o portão faz quando há um tickets: configurado:
| Momento | O que acontece no gerenciador |
|---|---|
override aceita um achado ou dispensa um critério | Cria um ticket com o achado ou critério, quem aceitou, motivo, vencimento, repositório, commit e link para o registro de evidência. A chave fica na entrada de evidência (ticket: "SEC-123"). Se já existia, comenta e reabre (renovação). |
scan ou check veem uma aceitação vencida | Comenta o ticket e o reabre se estava fechado. Um único comentário por data de vencimento, mesmo que o gate rode em cada push. |
O achado desaparece do scan, ou o critério passa sozinho em um check completo | Comenta e fecha o ticket. |
Um scan na rama principal encontra um achado que não estava no escaneamento anterior salvo | Abre um ticket de atenção (vulnerabilidade nova sobre código já liberado). |
Detalhes que vale a pena saber:
- O gerenciador nunca bloqueia o veredicto. Sem rede, credenciais erradas ou um
projeto que rejeita a incidência: o veredicto é o que dizem os
artefatos, um aviso é impresso, o erro fica na evidência
(
ticket_errorna entrada) e a operação fica pendente em.linebreak/audit/tickets.json.linebreak-gate tickets synctenta novamente o que está pendente (exit 0 quando não sobra nada; exit 1 se algo ainda estiver pendente). - A idempotência vive no gerenciador. Cada ticket leva as etiquetas
linebreak-target-<hash>elinebreak-artifact-<security|code|criteria>; antes de criar, a compuerta busca por etiqueta. Assim, um runner de CI que não tem o livro local nunca duplica tickets, e pode fechar os de achados corrigidos. - "Novo" na rama principal é medido contra o artefato anterior em
.linebreak/audit/(o que estava comprometido antes de reescrevê-lo). O primeiro scan de um repositório não abre nada: não há linha de base. Só contam achados no nível do limiarfail_onou acima, e que não tenham já um ticket. Em GitHub Actions, a rama é lida deGITHUB_REF_NAME; um pull request (GITHUB_HEAD_REF) nunca conta como rama principal. - Vale a pena comprometer
.linebreak/audit/tickets.jsonjunto com ooverride(assim comosecurity.json), para que o histórico do ticket viaje com a evidência.
Preços
Grátis, para sempre: o scan de CVEs de dependências e todo o loop de spec — autoria, aprovação humana, servidor MCP e aplicação em CI. Sem chave, sem conta.
Pro — US$ 99/mês por equipe (preços):
aprovações assinadas criptograficamente e à prova de adulteração (Ed25519, verificáveis
offline), modo de aplicação com chave obrigatória e revisão de código com IA hospedada sem
chave de API para gerenciar. Compre na página de preços — sua LINEBREAK_LICENSE_KEY
chega por e-mail em segundos (é a entrada license-key da Action).
Prefere usar sua própria chave de modelo? ANTHROPIC_API_KEY também habilita a revisão com IA;
a revisão hospedada do Pro é o caminho de configuração zero.
O gate roda aberto por padrão: funciona sem chave e imprime um aviso
quando nenhuma LINEBREAK_LICENSE_KEY está definida (suprimido para usuários BYOK). Isso é
freemium — o scan de dependências roda grátis. Equipes que quiserem exigir uma
chave Pro válida para o gate rodar podem optar pelo
LINEBREAK_ENTITLEMENTS_PROVIDER=remote, que verifica a elegibilidade antes de
qualquer scan e falha fechado em caso de chave ausente/inválida/revogada, plano errado ou
serviço inacessível — bloqueando todo o gate, incluindo o scan de dependências.