diff --git a/README.md b/README.md index 33b9dc45..b152f9c3 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,9 @@ installed and signed in once with your Claude account. Bots run on your own Clau 1. Download `Botloft__x64-setup.exe` from the [latest release](https://github.com/httpsphl/botloft/releases/latest) and run it. It installs for your Windows user only, with no administrator prompt. The installer is not code-signed yet, so - Windows SmartScreen may ask you to confirm ("More info", then "Run anyway"). + Windows SmartScreen may ask you to confirm ("More info", then "Run anyway"). The + [code signing policy](docs/code-signing-policy.md) says how releases are made and how to check + your download. 2. Open Botloft. It sets itself up; there is nothing to configure. 3. Create your first crew and tell it what it is for. diff --git a/app/scripts/release.mjs b/app/scripts/release.mjs index 0aa6d55f..828fc82d 100644 --- a/app/scripts/release.mjs +++ b/app/scripts/release.mjs @@ -91,6 +91,8 @@ Both installers were built by GitHub Actions from this repository, which atteste \`\`\` gh attestation verify ${setup} --repo ${repo} \`\`\` + +How releases are made, and what Botloft changes on your computer: [Code signing policy](https://github.com/${repo}/blob/main/docs/code-signing-policy.md). `; } diff --git a/docs/code-signing-policy.md b/docs/code-signing-policy.md index 6e7b3666..46aebe64 100644 --- a/docs/code-signing-policy.md +++ b/docs/code-signing-policy.md @@ -15,6 +15,10 @@ The release pipeline is ready to sign `Botloft.exe`, `botloftd.exe`, the setup w and the uninstaller as soon as a certificate is in place. All of them carry the product name "Botloft" and the version of the release. +Botloft applied to the [SignPath Foundation](https://signpath.org) for free code signing on +30 September 2026. If the application is accepted, this page will say so, and releases will be +signed with a certificate issued to SignPath Foundation. + ## How releases are made - Every release is built by GitHub Actions ([`release.yml`](../.github/workflows/release.yml)) from diff --git a/docs/spec.md b/docs/spec.md index 4b03193b..85c01d8d 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -741,7 +741,7 @@ Identidade: o mascote do Botloft é uma chama com olhos. O ícone do app (`app/a - O feed aponta para o instalador NSIS do release, `Botloft__x64-update.exe`, e não para a tela de instalação (15.7). O instalador roda como `/P /UPDATE /R`: passivo, sem perguntas, e reabre o app. O hook de desinstalação não faz nada com `/UPDATE` (15.4), o daemon segue rodando, e o app reaberto o atualiza porque ele ficou `outdated` (15.2). - Os artefatos são assinados com a chave do updater do Tauri (`createUpdaterArtifacts`), e a chave pública fica no `tauri.conf.json`. `requireSignedVersion` exige que a assinatura traga a versão, para um feed adulterado não empurrar uma versão antiga de volta. O instalador não tem assinatura Authenticode, então o SmartScreen avisa na primeira execução. - Release: a versão fica só no `[workspace.package]` do `Cargo.toml` (o `tauri.conf.json` não repete a versão e usa a do crate). Um push de tag `vX.Y.Z` roda `.github/workflows/release.yml`, que confere tag e versão, roda `pnpm bundle` com `TAURI_SIGNING_PRIVATE_KEY` (segredo do repositório), depois `pnpm bundle:setup` (15.7), `release.mjs stage`, o atestado de origem e `release.mjs publish`, que abre um release **rascunho** com a tela de instalação (`-setup.exe`), o instalador do atualizador (`-update.exe`) e o `.sig` dele, e o `latest.json`. O updater só enxerga o release depois que o dono o publica. -- Atestado de origem: entre o `stage` e o `publish`, o workflow roda `actions/attest@v4` nos dois `.exe` (permissões `id-token`, `attestations` e `artifact-metadata`). É um atestado de proveniência SLSA assinado pelo Sigstore: qualquer pessoa confere com `gh attestation verify --repo httpsphl/botloft` que o instalador saiu deste workflow, deste repositório e daquele commit. As notas do rascunho começam com "Verify your download": o SHA-256 dos dois instaladores e esse comando. O VirusTotal fica de fora das notas enquanto a tela de instalação tiver alertas de aprendizado de máquina (na 0.6.0, 2 de 71: Microsoft `Wacatac.C!ml` e Trapmine), à espera da análise de falso positivo. +- Atestado de origem: entre o `stage` e o `publish`, o workflow roda `actions/attest@v4` nos dois `.exe` (permissões `id-token`, `attestations` e `artifact-metadata`). É um atestado de proveniência SLSA assinado pelo Sigstore: qualquer pessoa confere com `gh attestation verify --repo httpsphl/botloft` que o instalador saiu deste workflow, deste repositório e daquele commit. As notas do rascunho começam com "Verify your download": o SHA-256 dos dois instaladores e esse comando. Terminam com o link da política de assinatura de código, que o README também mostra no passo de download: a SignPath Foundation pede o termo "Code signing policy" na página inicial e nas de download. O VirusTotal fica de fora das notas enquanto a tela de instalação tiver alertas de aprendizado de máquina (na 0.6.0, 2 de 71: Microsoft `Wacatac.C!ml` e Trapmine), à espera da análise de falso positivo. - Assinatura de código (Authenticode): ainda não há certificado, e sem ele o release sai sem assinatura. O caminho já está pronto: `app/scripts/sign.mjs ` assina com o `signtool` do Windows SDK o certificado que estiver no repositório de certificados da máquina, escolhido pela impressão digital em `BOTLOFT_SIGN_THUMBPRINT` (uma chave em nuvem aparece ali depois que o cliente dela entra na conta), com carimbo de tempo RFC 3161 (`BOTLOFT_SIGN_TIMESTAMP`) e confere a assinatura em seguida. Sem a variável, não faz nada; com ela e sem o certificado, falha, para um release nunca sair sem assinatura por engano. Quem chama: o Tauri, pelo `bundle.windows.signCommand` do `tauri.bundle.conf.json`, para tudo que entra no instalador (o `Botloft.exe`, o sidecar `botloftd.exe`, os plugins do NSIS, o desinstalador e o próprio instalador; ele pula o que já estiver assinado); e o `setup.mjs` para a tela de instalação, que o Tauri não monta. A cópia do daemon em `\bin` é o mesmo arquivo assinado do instalador. O `release.yml` passa as duas variáveis do repositório. O `.sig` do atualizador e o atestado de origem são feitos depois, sobre os arquivos já assinados. - Todo binário leva nome do produto e versão nos metadados, que é o que um serviço de assinatura confere: o app e a tela de instalação pelo `tauri-build`, o instalador pelo NSIS e o `botloftd.exe` por um `VERSIONINFO` no `botloftd.rc` (produto "Botloft", descrição "Botloft background service", versão do crate), preenchido pelo `build.rs`. - Opções de certificado vistas em 2026-09-30, para uma pessoa física no Brasil: o Azure Artifact Signing não atende (pessoa física só nos EUA e no Canadá); a SignPath Foundation assina de graça projetos abertos aprovados, com o nome dela como editor e uma aprovação manual por release; o certificado Open Source da Certum (a partir de € 49, chave em nuvem) sai no nome do desenvolvedor; a SSL.com tem validação individual com assinatura em nuvem e action oficial, por cerca de US$ 300 por ano. Nenhuma tira o aviso do SmartScreen de imediato: a reputação cresce a cada release assinado com a mesma identidade. A política pública está em `docs/code-signing-policy.md`.