Esta página argumenta por que os requisitos de segurança do projeto são atendidos: o que se protege, de quem, onde ficam as fronteiras de confiança, quais princípios de desenho são aplicados, quais fraquezas comuns são contidas e por qual código. Cada contramedida nomeia o arquivo que a implementa. O que o desenho não afirma está listado no fim, ao lado dos comandos que conferem o resto. Relatos de segurança vão pelo SECURITY.pt-BR.md.
Os arquivos de módulo são citados no caminho que têm na distribuição, <área>/<módulo>-<versão>/,
como o marketplace.json os lista.
01
A afirmação
Usado como documentado, o House Party Protocol não roda nada que o operador não pediu, não envia nada para fora da máquina a menos que uma pessoa rode um comando que o envie, não guarda credencial, e entrega os bytes que o workflow de release construiu.
02
Modelo de ameaças
O que se protege
- o repositório e a árvore de trabalho do operador, sobre os quais o agente e os módulos agem;
- as credenciais no ambiente do operador (chaves de modelo, tokens);
- a integridade dos registros de evidência, das attestations e dos vereditos, que decidem o que conta como pronto;
- os artefatos de release: o wheel, o sdist e os diretórios de módulo.
De onde vêm as ameaças
- um modelo ou agente que propõe um comando destrutivo, ou afirma que o trabalho está pronto quando não está;
- uma página web aberta no navegador do operador que tenta alcançar um servidor local;
- um atacante de rede entre o adaptador de exemplo e o endpoint dele, ou um endpoint que redireciona;
- um download adulterado, ou um módulo alterado depois de emitido;
- uma action ou um pacote de terceiro comprometido no CI;
- uma contribuição que carrega conteúdo malicioso.
Fora do escopo
- um atacante que já roda código sob a própria conta do operador;
- o próprio host de agente (Claude Code, Codex CLI) e o provedor de modelo por trás dele;
- o que uma pessoa decide rodar à mão.
03
Fronteiras de confiança
A máquina do usuário
O HPP é uma ferramenta de linha de comando que roda como o operador, sob demanda. O pacote hpp
não inicia daemon, servidor nem scheduler e não tem telemetria remota
(MANIFESTO.pt-BR.md, "O que o projeto se recusa a fazer"). Ele importa só
a biblioteca padrão do Python (pyproject.toml: dependencies = []; imposto por
tests/test_stdlib_only.py). Todo veredito é re-derivado de arquivos em disco.
O host de agente
O host roda o modelo e as ferramentas dele; o HPP fornece hooks e comandos em volta, e trata o que o modelo diz como uma afirmação a conferir, não como fato:
hpp init --applyescreve um arquivo,.hpp/profile.json, e só lê osettings.jsondo host (hpp/wizard.py). Ligar hooks num host continua sendo ação humana.- O classificador de política (
hpp/policy.py) devolveALLOW,MANUALouBLOCK; ele nunca executa o comando, e uma política do usuário só consegue endurecer um veredito embutido, nunca afrouxá-lo. - Um veredito da lane ou da família de modelo de quem construiu é recusado, e o done gate roda de novo os comandos do critério em vez de confiar numa mensagem de conclusão (MANIFESTO.pt-BR.md).
- Os hooks de módulo são WARN-only por padrão (SECURITY.pt-BR.md); um hook nunca derruba o host.
Os módulos
Um módulo é código que o operador instala. A integridade dele é conferida antes de ele rodar:
- todo diretório de módulo traz
CHECKSUMS.txt(sha256 por arquivo), einstallers/kit-forge-1.5.4/kit_doctor.py verify <module>compara cada arquivo com ele; kit_doctor.py installplaneja primeiro: uma execução de plano roda todos os self-tests do módulo e não escreve nada no projeto de destino, e a instalação só é aplicada numa segunda invocação, explícita (--apply), que copia os arquivos*.example.*do módulo para o destino antes da própria etapa de self-test (run_installemkit_doctor.py);- antes de um módulo ser emitido, um linter de IP/PII recusa credenciais, dados pessoais e caminhos de máquina (CONTRIBUTING.pt-BR.md).
O endpoint do exemplo decide.py
O examples/typed-decisions/decide.py é o único componente cujo propósito é mandar dados para fora
da máquina: ele envia o texto que uma pessoa lhe dá ao endpoint que essa pessoa declarou. Ele só
roda quando uma pessoa o roda, e a política o classifica como MANUAL. Dentro desse propósito:
- a chave é lida do ambiente na hora da chamada e nunca é escrita em lugar nenhum; uma mensagem de
erro que a conteria a tem trocada por
<redacted>; - uma chave só é enviada por
https, ou para um endereço de loopback; - um redirecionamento é recusado (
_NoRedirect), então o cabeçalhoAuthorizationchega a um host só; - um estado que parece carregar segredo é recusado antes de qualquer coisa sair da máquina
(
digestemhpp/decision.py); - uma tentativa, um timeout em cada espera de socket, e uma resposta limitada a 1 MiB.
O dashboard em loopback
O Lane Dashboard do lane-kit (multi-session/lane-kit-1.8.2/scripts/lane_dashboard.py) é uma
página que o operador liga e desliga. É a exceção delimitada que o MANIFESTO permite a um módulo:
- ele escuta só em loopback:
--hostaceita apenas127.0.0.1,::1oulocalhost(LOOPBACK), e uma escrita é recusada a menos que o endereço do próprio servidor seja um deles; - leituras e ações são recusadas a menos que o cabeçalho
Hostnomeie um dos três nomes de loopback com a porta do servidor (_allowed_hosts), o que derrota DNS rebinding; - cada execução emite o seu próprio token (
secrets.token_urlsafe(24)emmake_server); uma escrita precisa carregá-lo no cabeçalhoX-HPP-Token, comparado comsecrets.compare_digest, com um corpoapplication/jsonde no máximo 65.536 bytes; - a página é servida com uma Content-Security-Policy de
default-src 'none',connect-src 'self',form-action 'none',base-uri 'none'eframe-ancestors 'none', maisX-Frame-Options: DENYeReferrer-Policy: no-referrer; - um
GETsó lê; toda ação é o argv de um comando de terminal listado emROUTE_COMMANDS, e o comando de terminal faz a mesma coisa.
A cadeia de suprimentos do CI
Os workflows sob .github/workflows/ constroem e publicam o que os usuários instalam:
- toda action de terceiro é fixada num SHA de commit completo, com a versão como comentário (30 das
30 linhas
uses:dos quatro workflows em 2026-09-29, contadas comgrep -hE 'uses: [^@]+@[0-9a-f]{40} # v' .github/workflows/*.ymlcontragrep -h 'uses:' .github/workflows/*.yml); o Dependabot propõe as atualizações (.github/dependabot.yml); - todo
pip installusa--require-hashescontra os arquivos de.github/requirements/; - o token padrão é somente leitura (
permissions: contents: read), e cada job que precisa de mais pede por conta própria; todo checkout definepersist-credentials: false; - a release atesta a proveniência de build do wheel e do sdist num job só dela
(
actions/attest-build-provenance), anexa oSHA256SUMSe o bundle de proveniência à release, e publica no PyPI por trusted publishing — nenhum workflow lê um segredo de token guardado.
04
Princípios de desenho seguro aplicados
| princípio | onde é aplicado | código |
|---|---|---|
| economia de mecanismo | nenhuma dependência de runtime, nenhum daemon, nenhum banco; todo mapa é uma projeção de arquivos | pyproject.toml, tests/test_stdlib_only.py |
| padrões seguros contra falha | o dashboard escuta em loopback por padrão; uma chave é recusada em http puro; um redirecionamento é recusado; hpp init não escreve nada sem --apply | lane_dashboard.py, decide.py, hpp/wizard.py |
| mediação completa | toda escrita do dashboard passa pelas checagens de loopback, Host, token, content-type e tamanho antes de rodar | DashboardHandler.do_POST em lane_dashboard.py |
| menor privilégio | os tokens dos workflows são somente leitura por padrão; o job de release pede contents: write e discussions: write, o job de attestation id-token: write e attestations: write | .github/workflows/release.yml |
| separação de privilégio | quem constrói não pode ser o próprio checker; a proveniência é assinada num job separado do build | hpp/attest.py, release.yml |
| menor mecanismo comum | cada execução do dashboard emite o seu próprio token, nunca compartilhado com outra execução | make_server em lane_dashboard.py |
| desenho aberto | as contramedidas estão no código publicado; o único segredo que o harness gera é o token por execução do dashboard | esta página |
| aceitabilidade psicológica | toda ação do dashboard também é um comando de terminal que faz exatamente a mesma coisa | ROUTE_COMMANDS, tests/test_lane_dashboard.py |
05
Fraquezas comuns e o que as contém
| fraqueza | contramedida | código |
|---|---|---|
| injeção de comando do sistema operacional | comandos rodam como lista de argv com shell=False, nunca por um shell | run_bounded em hpp/_process.py; hpp/evals.py |
| um segredo numa linha de comando ou num registro | uma linha de comando que parece carregar segredo é recusada antes de rodar; contexto e estado com cara de segredo são recusados | _carries_secret em hpp/evidence.py; _SECRET_PATTERN em hpp/context.py; digest em hpp/decision.py |
| DNS rebinding | allowlist de Host em toda leitura e ação | _host_ok em lane_dashboard.py |
| falsificação de requisição entre sites | token por execução num cabeçalho próprio, só corpo JSON; o handler não implementa OPTIONS, então um preflight de CORS nunca é respondido | do_POST em lane_dashboard.py |
| clickjacking | frame-ancestors 'none' e X-Frame-Options: DENY | serve_page em lane_dashboard.py |
| exposição de um serviço local à rede | escuta em loopback; --host limitado a nomes de loopback | LOOPBACK em lane_dashboard.py |
| ataque de tempo sobre o token | comparação em tempo constante | secrets.compare_digest em lane_dashboard.py |
| credencial repassada num redirecionamento | redirecionamentos recusados | _NoRedirect em decide.py |
| transmissão de chave em texto claro | chave só por https ou para loopback | ask em decide.py |
| validação imprópria de certificado | HTTPS passa pelo urllib com o contexto padrão do Python, que verifica o certificado e o nome do host; nenhum código o desliga | decide.py; as sondas do health-kit e do operator-kit |
| credenciais guardadas | chaves vêm do ambiente na hora da chamada; nenhum módulo contém credencial; o PyPI aceita a identidade do workflow em vez de um token | decide.py, SECURITY.pt-BR.md, release.yml |
| consumo descontrolado de recursos | timeouts que param a árvore de processos inteira; corpos de resposta e de requisição limitados | hpp/_process.py, decide.py, lane_dashboard.py |
| um artefato ou módulo adulterado | CHECKSUMS.txt por módulo, SHA256SUMS e proveniência de build assinada por release | kit_doctor.py, release.yml |
| um registro de evidência editado | o registro faz hash do que guarda e hpp evidence verify o re-deriva; com um arquivo de allowed-signers, verify recusa um registro sem assinatura, assinado por outra pessoa ou alterado depois de assinado | hpp/evidence.py, hpp/signing.py |
| uma dependência de CI comprometida | actions fixadas por SHA, requisitos fixados por hash, token padrão somente leitura | .github/workflows/, .github/requirements/ |
"Nenhum código o desliga" foi medido quando esta página foi escrita: nenhum arquivo .py sob
hpp/, examples/, scripts/ ou os módulos contém CERT_NONE, _create_unverified_context,
check_hostname ou verify=False.
06
O que este caso não afirma
- Hooks avisam; não param. Um hook de módulo é WARN-only por padrão, então um aviso não para um agente. Um veredito só impede algo quando um hook, um job de CI ou uma pessoa o chama.
- O classificador de política é pequeno. É um conjunto explícito de regras, e não pega toda forma destrutiva de um comando.
- Loopback não isola da própria conta do operador. Qualquer processo que rode como o mesmo usuário alcança o dashboard e lê a página dele, token incluído.
- O dashboard permite script inline. A CSP dele carrega
script-src 'unsafe-inline', então o escape é a defesa contra marcação vinda de texto do repositório: a página escapa esse texto com a funçãoescantes de inseri-lo. Este caso não afirma que todo ponto de inserção foi auditado. - Um assento de House Session tem impressão digital, não sandbox. A impressão digital não vê uma escrita num caminho que o repositório ignora, nem o que um assento faz fora do worktree dele (MANIFESTO.pt-BR.md).
- Revisão de code owner é uma configuração do repositório. O
.github/CODEOWNERSnomeia o mantenedor para todo caminho; ele só obriga enquanto a proteção de branch exigir revisão de code owner. - O adaptador de exemplo envia o que recebe. O
decide.pyenvia o texto de estado ao endpoint que a pessoa declarou — esse é o propósito dele.
07
Como conferir este caso
A partir da raiz do repositório:
python -m pytest tests/test_lane_dashboard.py tests/test_decision.py tests/test_evidence.py tests/test_process.py tests/test_stdlib_only.py -q
python installers/kit-forge-1.5.4/kit_doctor.py verify multi-session/lane-kit-1.8.2
gh attestation verify <file> --repo rusharlabs/house-party-protocol
O primeiro comando roda os testes que guardam as contramedidas acima (entre eles
test_a_page_on_another_origin_cannot_act, test_the_page_cannot_be_framed,
test_adapter_refuses_a_redirect_and_the_key_never_reaches_the_second_host e
test_a_secret_like_command_line_is_refused_before_it_runs); o segundo prova os bytes de um módulo;
o terceiro prova de onde veio um arquivo de release. Quem mantém este caso e como ele muda está no
GOVERNANCE.pt-BR.md.