# Arquitetura de segurança

> Como o Latchkey isola cada job de CI: VMs dedicadas de uso único, nenhuma rede de entrada, credenciais efêmeras, criptografia em repouso e em trânsito, e as restrições sob as quais o agente de autorreparo é executado.

Source: https://latchkey.dev/pt/documentation/security-architecture

## Summary

- Cada job recebe sua **própria máquina virtual EC2**, que executa exatamente um job e é então destruída, junto com seu disco criptografado.
- Os runners ficam em **subnets privadas, sem IP público e com zero regras de entrada** - nada consegue se conectar a eles.
- As credenciais de runner são **just-in-time e de uso único**, apagadas do Parameter Store no momento em que o runner as lê.
- O agente de autorreparo roda **como um usuário não root** por trás de uma allowlist de sudo com uma única entrada, e suas ferramentas de IA são executadas em um **sandbox de negação por padrão**.

Esta página foi escrita para uma revisão de segurança. Ela descreve como a plataforma de runners é realmente construída - a fronteira de isolamento, o que sai da máquina, como as credenciais são tratadas e o que o agente de autorreparo tem e não tem permissão de fazer. Para o que o GitHub App pode ler e escrever nos seus repositórios, veja [Segurança e permissões do GitHub](/documentation/security-and-permissions).

## Isolamento: um job, uma máquina

O Latchkey não compartilha uma máquina entre jobs. Cada job roda em uma máquina virtual EC2 dedicada (classe m6a, Ubuntu 24.04 LTS) lançada para aquele job. Seu job não é um contêiner compartilhando um kernel com outro inquilino, e não é um processo em um host compartilhado de longa duração.

O uso único é imposto na camada de registro do GitHub, não apenas por convenção. Runners de cold start se registram com uma **configuração just-in-time**, que o GitHub aceita para exatamente um job. Runners de warm pool se registram com a flag `--ephemeral`, que faz o mesmo. Nos dois casos, a instância é configurada para terminar no desligamento, então não pode ser reutilizada mesmo que algo dê errado.

| Fronteira | Como ela é imposta |
| --- | --- |
| Um job por máquina | Configuração just-in-time do GitHub (cold start) ou registro com `--ephemeral` (warm pool) |
| A máquina não pode ser reutilizada | O comportamento de desligamento iniciado pela instância é definido como terminar, tanto no launch template quanto no caminho de lançamento direto |
| O disco não sobrevive | O volume EBS raiz é criptografado e marcado como delete-on-termination |
| Jobs descontrolados são recolhidos | Um processo de limpeza termina instâncias que passam do limite de 4 horas por job (teto rígido de 8 horas) |
| Runners são vinculados a um único workspace | As instâncias são marcadas com o ID da organização e registradas em um único grupo de runners da organização |

Grupos de runners são criados com `visibility: selected` e uma lista explícita de repositórios, então um runner registrado para o seu workspace só recebe jobs dos repositórios que você habilitou.

> ****
> Sua fronteira de isolamento é a máquina virtual: uma instância EC2 dedicada por job, com seu próprio kernel, seu próprio disco criptografado e nenhum caminho para qualquer outro inquilino. Se o seu regime de conformidade tem requisitos específicos de infraestrutura, fale conosco antes do onboarding e vamos resolvê-los junto com você.

## O que roda como root, e por que isso está contido

Dentro da sua própria VM, seu job tem controle total: o usuário `runner` tem sudo sem senha e acesso ao Docker. Isso é normal em CI - builds instalam pacotes e executam contêineres - e é seguro justamente porque o raio de alcance é uma única máquina de vida curta, sem rede de entrada, que é destruída ao final do job. Preferimos afirmar isso claramente a insinuar um sandbox que não existe.

## Rede

## Credenciais e segredos

1. **As credenciais de runner são emitidas por job** Uma configuração de runner just-in-time é gerada para um único job e gravada no AWS Systems Manager Parameter Store como um SecureString criptografado, com escopo daquela instalação e daquele job.
2. **O runner a lê uma vez e depois a apaga** Na inicialização, a instância busca o parâmetro e o apaga imediatamente, porque ele contém material de chave. O mesmo padrão vale para os tokens de registro de warm pool, que são mantidos em cache apenas durante sua vida útil de 55 minutos.
3. **O acesso ao GitHub é de curta duração** O GitHub App assina um JWT com sua chave privada e o troca por um installation access token de curta duração a cada operação. Não existe nenhum token do GitHub de longa duração armazenado em um runner.
4. **Os segredos da plataforma ficam fora do alcance dos runners** A chave privada do GitHub App fica no Parameter Store como um SecureString. O segredo que um runner pode ler é deliberadamente mínimo e separado do segredo de configuração da plataforma, de modo que um papel de instância comprometido não consegue ler credenciais da plataforma.

Os deploys se autenticam na AWS por assunção de papel via GitHub OIDC. Não há chaves de acesso AWS de longa duração na CI. As chaves de API da integração MCP são armazenadas apenas como um hash SHA-256 - a chave em texto puro é exibida uma vez na criação e nunca é persistida.

## Criptografia

| Dado | Em repouso | Em trânsito |
| --- | --- | --- |
| Disco raiz do runner | EBS criptografado (gp3), excluído na terminação | n/a |
| Cache de build (S3) | SSE-S3 (AES-256), expiração em 14 dias | TLS |
| Banco de dados da aplicação | Armazenamento criptografado, sem acesso público | TLS |
| Camada de cache / fila | Criptografia em repouso e em trânsito habilitada | TLS |
| Configurações de runner e chaves do app | SecureString do Parameter Store | TLS |
| Tráfego de API | n/a | TLS 1.2 no mínimo no domínio da API |

## Isolamento do cache de build

Os objetos de cache são armazenados no S3 sob um prefixo de chave derivado do ID da sua organização, e o ID do inquilino é injetado pelo control plane a partir da requisição do job - um job não pode se declarar outro inquilino definindo uma variável de ambiente. Os objetos expiram após 14 dias.

> ****
> Toda leitura e escrita de cache passa pelo proxy de cache, que resolve o prefixo do inquilino a partir do workspace que o control plane atribuiu àquele runner - e não a partir de qualquer coisa fornecida pelo job. Os objetos de cache têm namespace por workspace e expiram após 14 dias.

## O que sai do runner

Dois tipos de dados saem da máquina, e vale ser preciso sobre o segundo.

- **Metadados de execução e de job** - nomes, tempos, status, labels e o YAML de workflow - que alimentam os recursos de análise e de otimização.
- **Saída do passo que falhou.** Quando um passo falha, o pipeline de autorreparo captura a saída padrão e a saída de erro daquele passo (limitadas a 1 MiB cada) e as armazena para que a falha possa ser diagnosticada e, quando pertinente, transformada em uma correção proposta. A retenção é de 90 dias.

> ****
> Apenas a saída do passo que falhou é capturada, e somente quando um passo falha. O GitHub Actions mascara os seus segredos registrados na saída de log antes que o Latchkey a veja, e os segredos são redigidos novamente quando os logs são servidos em um pacote de escalonamento. Se você quiser uma captura com escopo diferente para o seu workspace, avise e configuraremos isso.

## Como o agente de autorreparo é restringido

O autorreparo é a parte em que um revisor deve pressionar mais forte, porque é o único componente que altera um job em execução. Veja exatamente o que ele pode fazer.

- Ele roda como o usuário **`runner`, não como root**, na forma de um serviço systemd.
- Ele escuta em um **socket de domínio Unix** com permissões restritas ao grupo. Não tem listener TCP nem superfície de rede.
- Ele é invocado por um shim que intercepta **apenas** os scripts de passo do próprio agente do runner; todos os demais comandos passam direto, sem alteração.
- Ele **falha aberto**: se o agente atinge o tempo limite, recusa ou dá erro, o comando roda exatamente como rodaria sem o Latchkey. Um reparador quebrado não pode quebrar o seu build.

As operações privilegiadas que ele pode executar são definidas por allowlist, não por filtragem:

| Controle | O que significa |
| --- | --- |
| Allowlist de sudo | Exatamente um padrão ancorado é permitido - um `apt-get install` não interativo de um único pacote. Qualquer outra coisa começando com `sudo` é rejeitada antes de o processo ser criado. |
| Allowlist de pacotes | Os pacotes instaláveis vêm de uma lista definida em tempo de compilação, aplicada de forma independente em dois estágios, porque um dos estágios monta comandos a partir de saída de build não confiável. |
| Piso de segurança do ambiente | Definir `LD_PRELOAD`, `LD_LIBRARY_PATH`, `PATH`, `BASH_ENV` ou `ENV` é negado, assim como metacaracteres de shell e substituição de comandos. |
| Negação de caminhos sensíveis | Leituras em `/etc`, `/root`, `/proc`, `/sys`, `/var/log`, `~/.ssh`, `~/.aws` e `~/.config/gh` são negadas. |
| Sandbox das ferramentas de IA | As ferramentas do estágio 3 rodam dentro de um sandbox baseado em bubblewrap que começa negando tudo por padrão - sem rede, sem caminhos de escrita - e é ampliado por ferramenta até o conjunto mínimo necessário. |
| Nenhuma chave de IA residente | O passo de raciocínio se autentica no AWS Bedrock pelo papel da instância, então não há nenhuma chave de API de modelo de longa duração em uma máquina que executa o seu código. |
| Interruptor de desligamento | O autorreparo pode ser desativado por workspace. |

> ****
> As ações simples de reparo - definir uma variável de ambiente, instalar um pacote da allowlist, liberar disco, repetir - passam pelo executor, onde as allowlists acima são a fronteira de imposição. O estágio de raciocínio por IA roda adicionalmente dentro do sandbox de negação por padrão.

## O que o GitHub App pode acessar

Cada permissão abaixo existe para viabilizar um recurso específico. Elas estão agrupadas pelo propósito, em vez de uma lista plana, para que você veja o motivo ao lado da concessão.

| Propósito | Acesso | Permissões |
| --- | --- | --- |
| Executar seus jobs em runners do Latchkey | Leitura e escrita | Runners auto-hospedados da organização, administração, administração da organização |
| Ver o que seus pipelines fizeram | Leitura | Actions, metadados de artefatos, metadados |
| Reagir aos jobs conforme eles acontecem | Leitura e escrita | Hooks da organização, hooks de repositório |
| Ler workflows e propor correções como PRs | Leitura e escrita | Workflows, código, pull requests |
| Modelar seus custos do GitHub com precisão | Leitura | Plano da organização |
| Verificar quem está na sua organização | Leitura | Membros |
| Exibir achados de segurança no seu painel | Leitura | Alertas do Dependabot, alertas de secret scanning, eventos de segurança, segredos do Dependabot |
| Construir imagens de runner adequadas à sua stack | Leitura | Imagens personalizadas de runner da organização |

> ****
> Duas dessas merecem uma explicação direta. O acesso de escrita a **código** existe para que uma correção possa ser aberta como um pull request em uma branch - ele nunca é usado para fazer push na sua branch padrão, e o caminho de escrita é restringido de três formas (abaixo). O acesso de leitura a **código** permite que o passo de diagnóstico examine manifestos de build como `package.json`, `go.mod` ou o seu Dockerfile ao apurar por que um passo falhou. O Latchkey não solicita acesso aos valores dos seus segredos do Actions, e o GitHub não os expõe a apps.

## Alterações nos seus repositórios

O Latchkey nunca faz push na sua branch padrão. Toda alteração é um pull request que você revisa, e o caminho de escrita é restringido de três formas:

1. O caminho do arquivo precisa ser resolvido como editável contra um manifesto por repositório. Caminhos sem correspondência são, por padrão, **não editáveis** - a verificação falha fechada.
2. Pull requests originados de forks são rejeitados, e um sinal de fork ausente também é rejeitado, em vez de presumido seguro.
3. Uma correção proposta precisa corresponder à sua assinatura de falha nos logs capturados, ou é suprimida.

## Segurança operacional

- As imagens de runner são **reconstruídas semanalmente**, para que os patches de segurança do sistema operacional cheguem à frota em uma cadência definida.
- O papel de instância do runner é de menor privilégio, incluindo uma negação explícita que restringe o acesso ao Parameter Store em toda a conta, concedido por uma política gerenciada pela AWS, apenas aos caminhos de runner do próprio Latchkey.
- Toda ação administrativa que altera estado é gravada em um **log de auditoria** com operador, ação, alvo, parâmetros e resultado.
- O acesso ao painel e à API é autenticado por um autorizador JWT no API gateway. Os papéis de workspace são Owner, Admin e Member, com os owners protegidos contra remoção ou rebaixamento.

## Retenção de dados

O que o Latchkey armazena, e por quanto tempo.

| Dado | Retenção |
| --- | --- |
| Seu código-fonte no runner | Destruído junto com a instância ao final do job |
| Objetos do cache de build | 14 dias a partir da última escrita |
| Saída do passo que falhou, capturada para diagnóstico | 90 dias |
| Registros de tentativas de autorreparo | 90 dias |
| Registros de correções propostas | 365 dias |
| Metadados de execução e de job que alimentam seus painéis | Retidos enquanto o repositório permanecer habilitado |

Desabilitar um repositório interrompe a coleta para ele. Excluir o seu workspace remove os dados dele. Se você precisa de uma janela de retenção específica para a sua organização, peça e conversaremos a respeito.

## Reportando uma vulnerabilidade

Envie um e-mail para **security@latchkey.dev**. Por favor, não abra uma issue pública no GitHub para um relato de segurança.

### Meu código-fonte permanece no runner depois do job?

Não. A instância e seu volume raiz criptografado são destruídos quando o job termina. Nada do que você fez checkout sobrevive, além das entradas de cache de build que você habilitou explicitamente, que expiram após 14 dias.

### O job de outro cliente consegue ver o meu?

Não. Cada job roda em sua própria máquina virtual, registrada para um único workspace e destruída após esse único job. Os runners não têm acesso de rede de entrada.

### O agente de autorreparo pode executar comandos arbitrários no meu build?

Não. As ações privilegiadas são definidas por allowlist em vez de filtradas: um único padrão de sudo permitido, uma lista fixa de pacotes, negação de variáveis de ambiente que possibilitam injeção de código e um sandbox de negação por padrão para o estágio de ferramentas de IA. Se o agente falhar por qualquer motivo, o seu comando roda exatamente como rodaria sem ele.

### Meus segredos de build são visíveis para o Latchkey?

Não. Seus segredos do GitHub Actions são injetados pelo GitHub diretamente no seu job em tempo de execução, exatamente como nos runners hospedados pelo GitHub, e o Latchkey nunca solicita nem armazena seus valores. O GitHub mascara os segredos registrados na saída de log antes que qualquer parte dela chegue até nós.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
