Interface gráfica nativa de macOS para o HTTrack Website Copier.
O HTTrack copia sites inteiros para o disco, mas no macOS só existe como linha
de comando — o WebHTTrack do mundo Unix serve um wizard HTML num mini-servidor
e depende do navegador. O MacHTTrack é um app Mac de verdade: SwiftUI, uma
janela, sidebar de sites, progresso ao vivo.
Completa a família do projeto: WinHTTrack no Windows, WebHTTrack no Unix, MacHTTrack no macOS.
Status: versão 1.0 — baixe o
.app. Adicione uma URL, escolha a pasta, acompanhe o progresso ao vivo, pare e retome. Ohttrack-agentespelha pela linha de comando e reporta progresso em JSON Lines; toda a lógica mora no app SwiftUI. Detalhes de arquitetura no design doc.
- Espelhar sites — URL, pasta de destino, profundidade e limite de velocidade
- Configurar por site, em abas no estilo WinHTTrack — regras de incluir/excluir
(
-*.zip,+*.pdf), robots.txt, user-agent e referer - Rede e autenticação — proxy (com usuário e senha) e login HTTP do site,
guardados no Keychain, nunca no
sites.json - Acompanhar e controlar — progresso ao vivo, conexões ativas e log; parar e retomar de onde parou
- Gerir a lista — nome amigável para cada site e excluir, opcionalmente apagando a pasta do espelho
Um download por vez, por ora. No radar: vários espelhos simultâneos e mais gestão das cópias (tamanho em disco, revelar no Finder, abrir no navegador).
Fora do escopo: busca dentro do conteúdo baixado, distribuição pública (assinatura Developer ID, notarização, App Store), Windows e Linux.
Um único .app, sem instalador e sem dependência externa. Dentro dele, dois
executáveis:
MacHTTrack.app/Contents/MacOS/
├── MacHTTrack ← app SwiftUI
└── httrack-agent ← motor: libhttrack.a + OpenSSL, tudo estático
O app lança um httrack-agent por download. As opções do mirror vão por
argv, na mesma sintaxe da CLI do HTTrack; só os segredos vão por stdin,
como linhas CHAVE=valor, para não ficarem visíveis no ps. O progresso volta
por stdout em JSON Lines, alimentado pelos callbacks loop e filesave da
libhttrack.
O agente é exercitável inteiramente pelo terminal — é assim por design.
O stdin precisa ser fechado: os segredos são lidos até EOF, antes
da primeira linha de saída, então sem redirecionamento o processo fica parado
esperando, sem emitir nada (o agente avisa no stderr quando percebe que o
stdin é um terminal).
# Sem segredo nenhum — repare no </dev/null.
agent/httrack-agent "http://example.com/" -O /tmp/espelho -r2 --quiet </dev/null
# Com credencial HTTP: pelo stdin, nunca no argv (o `ps` mostra argv).
printf 'HTTP_USER=alice\nHTTP_PASSWORD=hunter2\n' \
| agent/httrack-agent "http://example.com/privado/" -O /tmp/espelho -r2 --quiet
# Atrás de um proxy que exige autenticação.
printf 'PROXY_USER=bob\nPROXY_PASSWORD=s3cr3t\n' \
| agent/httrack-agent "http://example.com/" -P "http://proxy.local:3128" \
-O /tmp/espelho -r2 --quietChaves aceitas no stdin: HTTP_USER, HTTP_PASSWORD, PROXY_USER,
PROXY_PASSWORD — desconhecidas são ignoradas de propósito, para o app poder
acrescentar novas sem quebrar um agente antigo. A credencial HTTP é registrada
para o host da URL semente; ela precisa, portanto, trazer esquema
(http:/https:, com o // opcional).
O agente recusa a execução (código 2, um error de nível fatal no
protocolo, hts_main2() nunca roda) quando não consegue decidir com segurança
de quem são os bytes da autoridade de uma URL semente — tipicamente uma
credencial com /, ?, # ou @ não codificado. A mensagem diz qual
codificação usar (%2F, %3F, %23, %40). Vale inclusive para uma URL
credenciada com @ no caminho ou na query
(http://user:pass@host/p?x=a@b → escreva x=a%40b). O raciocínio inteiro,
ramo por ramo, está em report_strip_argv_credentials()
(agent/agent_report.c).
Cada linha da saída é um JSON: ready primeiro, done
por último, progress/file/error no meio — com a exceção das falhas de
arranque, documentadas em "Protocolo do agente" no
design doc.
Por que processos separados? A API do HTTrack é process-global em vários
pontos — httrack-library.h:191-192 avisa que hts_get_stats() devolve um
static "not thread-safe and overwritten by the next call". Dois mirrors no mesmo
processo corrompem estado. Um processo por mirror resolve isso por construção,
isola falhas do C e mantém a UI responsiva.
- macOS 26 ou superior, Apple Silicon
- Xcode 26+
- Homebrew com
openssl@3(só para construir; o.appresultante não depende do Homebrew) - autoconf, automake, libtool (para construir a libhttrack)
git submodule update --init --recursive
./scripts/build-libhttrack.sh # libhttrack.a estática
make -C agent # httrack-agent
make -C agent check # suíte do agente (5 conjuntos)
swift test # suíte do app
./scripts/assemble-app.sh # monta build/MacHTTrack.app
open build/MacHTTrack.appO make roda scripts/check-linkage.sh automaticamente — se o agente
ganhar qualquer dependência fora de /usr/lib, o build falha.
O make -C agent check roda a suíte inteira do agente: os dois testes
unitários (test-json, test-secrets) e as três suítes de shell
(tests/integration.sh, tests/test_stop.sh, tests/test_tls.sh). As de
shell sobem servidores HTTP/HTTPS locais com python3 e levam alguns minutos.
A dependência C é construída estaticamente e embutida no binário. O ponto
crítico, que o scripts/build-libhttrack.sh e o agent/Makefile já resolvem:
os .a do OpenSSL devem ser passados por caminho absoluto. O flag
--disable-shared governa a libhttrack, não o OpenSSL — com -lssl -lcrypto o
linker acha .dylib e .a no mesmo diretório e prefere a dinâmica, prendendo o
binário a /opt/homebrew/....
Verificação manual do mesmo que o check-linkage.sh faz — só devem aparecer
bibliotecas de /usr/lib:
otool -L agent/httrack-agentEm macOS 26.6 / Apple Silicon / Xcode 26.6, sobre o HTTrack 3.49.14:
| Verificação | Resultado |
|---|---|
configure + make |
exit 0; libhttrack.a com 4,0 MB |
| Link estático | Só /usr/lib/libz, /usr/lib/libiconv, libSystem. 6,0 MB |
| Execução | HTTrack version 3.49-14 |
| HTTPS real | Espelhou https://example.com/ sem erros nem avisos |
make -C agent check |
Cinco suítes verdes: test-json, test-secrets, integração, parada/retomada e TLS |
swift test |
127 testes verdes, sem avisos — inclui um fim a fim que espelha, para e retoma contra o servidor de fixtures |
scripts/assemble-app.sh |
MacHTTrack.app monta e assina; espelha, para e retoma pela interface |
O HTTrack não valida certificados TLS — é decisão do upstream
(htslib.c:5731: "no cert verify, by design"), e nenhuma função de
verificação aparece em src/*.c.
Na prática: https:// garante que o tráfego é criptografado, mas não que o
servidor do outro lado seja quem diz ser. Um intermediário com certificado
autoassinado é aceito em silêncio. Por isso o MacHTTrack não exibe cadeado nem
usa a palavra "seguro" em lugar nenhum da interface — seria uma promessa que o
motor não cumpre.
Esse comportamento está travado por agent/tests/test_tls.sh: se uma versão
futura do motor passar a rejeitar certificados autoassinados, o teste falha —
não porque rejeitar seja errado, mas porque a exigência de interface acima
deixaria de valer e precisa ser revista, não descoberta por acaso.
O HTTrack é GPL-3.0-or-later. Este projeto o vincula estaticamente, então herda a GPL-3.0-or-later.
