Este guia é para quem opera um Hermes Agent auto-hospedado e administra a conta Linux e o repositório GitHub privado. O resultado é específico: um arquivo completo de dados de usuário do Hermes, criptografado antes de sair do servidor, confirmado no repositório remoto privado previsto e recuperado e importado em outra máquina descartável. É um procedimento documentado para a CLI do Hermes no commit 86fa9e6, conferido em 19 de outubro de 2026. A BIG CHANGE verificou sintaxe e fluxo do script com testes locais inertes; não executou backup real do Hermes, criptografia age, envio ao GitHub, disparo programado ou restauração. Considere o backup comprovado para sua instalação somente após realizar todas as verificações abaixo.

A grande mudança

  • O que mudou: O comando de backup completo do Hermes reúne dados de trabalho em um ZIP, e seu modo cron sem agente executa um script revisado. Juntos permitem criar uma cópia criptografada programada sem fazer um modelo de IA realizar o backup.
  • Por que importa: O operador verifica três fatos separados: o Hermes concluiu o arquivo, o conteúdo criptografado chegou ao repositório privado previsto e uma instalação nova consegue importá-lo. Cada verificação detecta uma falha diferente. O arquivo ainda exclui a aplicação e parte dos dados locais da máquina.
  • O que observar: A decisão prática é se o arquivo e o histórico Git crescente continuam pequenos o suficiente para esta rota limitada. Um teste de recuperação que falha ou qualquer limite de tamanho exige outro plano de armazenamento antes de depender da programação.

1. Prepare servidor, chave e repositório

Use Linux com Bash, as ferramentas GNU stat -c e du -sb, além de CLI do Hermes instalada, Git, GitHub CLI (gh), age, unzip, sha256sum, SSH e um gateway Hermes ativo para cron. Confirme que os comandos instalados hermes backup, hermes import e hermes cron create --help correspondam à referência CLI fixada, às perguntas frequentes e ao manual de cron. Identifique o usuário do sistema e o HERMES_HOME responsáveis pelo gateway. Faça toda a configuração e o primeiro backup manual como esse usuário. O exemplo pressupõe um clone Git dedicado e limpo e uma ramificação main.

O backup completo do Hermes inclui configuração, credenciais, memórias, habilidades, sessões e perfis sob a raiz de dados. Exclui código Hermes, dependências e ambientes baixados, backups anteriores, pontos de verificação, perfis de credenciais do navegador, arquivos auxiliares SQLite e alguns arquivos locais. Não coleta arquivos não relacionados em outras partes do servidor. Não use --quick para obter esta cópia completa de dados. Revise itens importantes fora desse limite e proteja-os separadamente. O ZIP contém segredos mesmo que o destino seja privado.

Gere uma identidade age, a chave privada necessária para descriptografar o arquivo, em local protegido fora de HERMES_HOME, do diretório temporário de preparação e do clone Git. Guarde uma cópia de recuperação dessa identidade em armazenamento independente. O destinatário exibido age1… é público e vai na configuração; a identidade privada não. Siga a referência de uso do age.

Terminal
umask 077
install -d -m 700 /PRIVATE-KEY-STORE
age-keygen -o /PRIVATE-KEY-STORE/hermes-backup-identity.txt
age-keygen -y /PRIVATE-KEY-STORE/hermes-backup-identity.txt

Crie um repositório privado exclusivo para este backup, confira quem tem acesso e configure leitura não interativa com gh e escrita Git SSH para o mesmo usuário do sistema que executa Hermes cron. Mantenha tokens e chaves SSH privadas fora dos dados Hermes, da preparação e do Git. Um login interativo em outra conta não comprova acesso programado. Substitua os marcadores em maiúsculas antes de executar os exemplos. Se a criação do repositório já gerou README ou uma ramificação main, inspecione e pule os comandos de inicialização conflitantes.

Terminal
gh repo create ACCOUNT/PRIVATE-REPOSITORY --private --clone
gh repo view ACCOUNT/PRIVATE-REPOSITORY --json isPrivate --jq .isPrivate
cd PRIVATE-REPOSITORY
git remote set-url origin git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git
git remote get-url --all origin
git remote get-url --push --all origin
git branch -M main
printf '%s\n' 'Encrypted Hermes backup storage; ciphertext and checksum only.' > README.md
git add README.md
git commit -m 'Initialize private backup repository'
git push -u origin HEAD:main
git ls-remote --heads origin main

gh repo create --clone pode selecionar HTTPS conforme preferências locais; o remoto SSH explícito acima define o destino desta receita. Prossiga somente quando isPrivate retornar true, os colaboradores forem os previstos, as URLs efetivas de leitura e envio corresponderem, cada uma, à única URL SSH revisada e o acesso remoto funcionar como usuário programado. As regras de remotos Git permitem reescrita de URL, destinos extras e um pushurl separado; o script confere valores brutos e efetivos em cada execução.

Saia do clone antes de salvar os próximos dois blocos. Crie um diretório privado de configuração fora da raiz Hermes, do clone Git, da preparação e do armazenamento de chaves. Salve hermes-github-backup.sh e hermes-github-backup.conf ali; os comandos de instalação copiam os arquivos para o perfil proprietário. O clone Git dedicado deve permanecer limpo.

Terminal
install -d -m 700 /ABSOLUTE/PRIVATE-SETUP
cd /ABSOLUTE/PRIVATE-SETUP

2. Instale o script de backup revisado

Salve o bloco Bash completo abaixo como hermes-github-backup.sh nesse diretório privado. Salve o segundo bloco como hermes-github-backup.conf ao lado, após substituir todos os marcadores por caminhos absolutos ou pelo destinatário público. Esta configuração shell deve ser revisada localmente antes de ser carregada e acessível somente ao proprietário. Não deve conter senha, token ou identidade privada age. O script é a amostra proposta completa; sintaxe e fluxo inerte foram verificados, mas adapte e teste na sua instalação. O arquivo latest.zip.age produzido é o arquivo criptografado.

Terminal
#!/usr/bin/env bash
# Documentation-based Linux sample for an operator to review and adapt.
# Place with hermes-github-backup.conf under the owning HERMES_HOME/scripts/.
# Requires GNU stat -c and du -sb. Git Bash was used only for inert fixtures.
set -Eeuo pipefail
umask 077

script_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
owning_home=$(CDPATH= cd -- "$script_dir/.." && pwd -P)
if [[ -n ${HERMES_HOME:-} && $(CDPATH= cd -- "$HERMES_HOME" && pwd -P) != "$owning_home" ]]; then
  printf 'Backup stopped: cron profile and script directory differ.\n' >&2
  exit 1
fi
export HERMES_HOME="$owning_home"

config="$script_dir/hermes-github-backup.conf"
[[ -f "$config" ]] || { printf 'Backup stopped: reviewed configuration missing.\n' >&2; exit 1; }
# This file is local, owner-reviewed shell configuration. It must contain no secret.
# shellcheck source=/dev/null
source "$config"
: "${HERMES_BIN:?set an absolute Hermes executable path}"
: "${REPO_DIR:?set an absolute private clone path}"
: "${STAGING_PARENT:?set an absolute private staging path}"
: "${AGE_RECIPIENT:?set an age public recipient}"
: "${EXPECTED_REMOTE:?set the exact private GitHub SSH remote}"
: "${BRANCH:=main}"

fail() { printf 'Backup stopped: %s.\n' "$1" >&2; exit 1; }
for path in "$HERMES_BIN" "$REPO_DIR" "$STAGING_PARENT"; do
  [[ $path == /* ]] || fail 'a configured path is not absolute'
done
[[ -x "$HERMES_BIN" ]] || fail 'Hermes executable unavailable'
[[ $AGE_RECIPIENT == age1* ]] || fail 'age public recipient invalid'
[[ $EXPECTED_REMOTE == git@github.com:*/*.git ]] || fail 'remote is not the reviewed GitHub SSH URL'
[[ $BRANCH =~ ^[a-zA-Z0-9._/-]+$ ]] || fail 'branch name invalid'
[[ -d "$STAGING_PARENT" ]] || fail 'staging directory unavailable'
stage_parent=$(CDPATH= cd -- "$STAGING_PARENT" && pwd -P)
[[ $stage_parent != "$owning_home" && $stage_parent != "$owning_home"/* ]] || fail 'staging is inside Hermes data'

# Atomic, nonblocking whole-flow lock: keep it through push and remote readback.
# A stale directory after a crash requires operator inspection; never steal it.
lockdir="$stage_parent/.hermes-github-backup-flow.lock"
mkdir -- "$lockdir" 2>/dev/null || fail 'another backup flow holds the lock or a stale lock needs review'
work=''
cleanup() {
  if [[ -n $work ]]; then
    rm -f -- "$work/hermes.zip" "$work/latest.zip.age" "$work/latest.sha256" "$work/backup.log"
    rmdir -- "$work" 2>/dev/null || true
  fi
  rmdir -- "$lockdir" 2>/dev/null || true
}
trap cleanup EXIT

[[ -d "$REPO_DIR/.git" ]] || fail 'Git repository unavailable'
repo=$(CDPATH= cd -- "$REPO_DIR" && pwd -P)
[[ $repo != "$owning_home" && $repo != "$owning_home"/* ]] || fail 'repository is inside Hermes data'
[[ $stage_parent != "$repo" && $stage_parent != "$repo"/* ]] || fail 'staging is inside Git repository'
[[ $repo != "$stage_parent"/* ]] || fail 'Git repository is inside staging'

command -v age >/dev/null || fail 'age unavailable'
command -v gh >/dev/null || fail 'GitHub CLI unavailable for private visibility check'
command -v unzip >/dev/null || fail 'zip verifier unavailable'
command -v git >/dev/null || fail 'Git unavailable'
remote_repo=${EXPECTED_REMOTE#git@github.com:}
remote_repo=${remote_repo%.git}
visibility=$(gh repo view "$remote_repo" --json isPrivate --jq .isPrivate 2>/dev/null) || fail 'GitHub repository visibility unreadable'
[[ $visibility == true ]] || fail 'GitHub repository is not confirmed private'
raw_fetch_urls=$(git -C "$repo" config --get-all remote.origin.url) || fail 'Git fetch URL missing'
[[ $raw_fetch_urls == "$EXPECTED_REMOTE" ]] || fail 'Git has multiple or unexpected raw fetch URLs'
raw_push_urls=$(git -C "$repo" config --get-all remote.origin.pushurl || true)
[[ -z $raw_push_urls ]] || fail 'separate Git pushurl requires independent review'
effective_fetch_urls=$(git -C "$repo" remote get-url --all origin) || fail 'effective fetch URL unreadable'
effective_push_urls=$(git -C "$repo" remote get-url --push --all origin) || fail 'effective push URL unreadable'
[[ $effective_fetch_urls == "$EXPECTED_REMOTE" ]] || fail 'Git fetch URL rewrite or extra destination'
[[ $effective_push_urls == "$EXPECTED_REMOTE" ]] || fail 'Git push URL rewrite or extra destination'
[[ $(git -C "$repo" branch --show-current) == "$BRANCH" ]] || fail 'local branch changed'
[[ -z $(git -C "$repo" status --porcelain --untracked-files=all) ]] || fail 'repository has unrelated changes'

# No interactive prompt is possible in an unattended Hermes cron tick.
export GIT_TERMINAL_PROMPT=0
export GIT_SSH_COMMAND='ssh -o BatchMode=yes'
remote_head=$(git -C "$repo" ls-remote --heads origin "$BRANCH" | awk '{print $1}')
[[ -n $remote_head && $remote_head == "$(git -C "$repo" rev-parse HEAD)" ]] || fail 'remote unreadable or branch has changed'

work=$(mktemp -d "$stage_parent/hermes-backup.XXXXXXXX") || fail 'private staging creation failed'

if "$HERMES_BIN" backup -o "$work/hermes.zip" >"$work/backup.log" 2>&1; then
  :
else
  backup_rc=$?
  fail "Hermes full backup returned $backup_rc; temporary zip/log will be removed, so diagnose with a separate controlled manual run"
fi
[[ -s "$work/hermes.zip" ]] || fail 'Hermes archive absent or empty'
unzip -tq "$work/hermes.zip" >"$work/backup.log" 2>&1 || fail 'zip integrity check failed'
age -r "$AGE_RECIPIENT" -o "$work/latest.zip.age" "$work/hermes.zip" >"$work/backup.log" 2>&1 || fail 'archive encryption failed'
[[ -s "$work/latest.zip.age" ]] || fail 'encrypted archive absent or empty'

# Deliberately below GitHub's 50 MiB warning. Git history retains every version.
cipher_bytes=$(stat -c %s "$work/latest.zip.age")
(( cipher_bytes <= 40000000 )) || fail 'encrypted archive exceeds this small-repository recipe'
git_bytes=$(du -sb "$repo/.git" | awk '{print $1}')
(( git_bytes + cipher_bytes + 10000000 <= 500000000 )) || fail 'repository history approaches the review limit'
hash=$(sha256sum "$work/latest.zip.age" | awk '{print $1}')
printf '%s  latest.zip.age\n' "$hash" >"$work/latest.sha256"

cp -- "$work/latest.zip.age" "$repo/latest.zip.age"
cp -- "$work/latest.sha256" "$repo/latest.sha256"
git -C "$repo" add -- latest.zip.age latest.sha256
staged=$(git -C "$repo" diff --cached --name-only)
[[ $staged == $'latest.sha256\nlatest.zip.age' ]] || fail 'staged paths are not the two reviewed files'
git -C "$repo" diff --cached --check >/dev/null || fail 'staged content check failed'
git -C "$repo" commit -m "Encrypted Hermes backup $(date -u +%Y-%m-%dT%H:%M:%SZ)" >/dev/null || fail 'local backup commit failed'
run_head=$(git -C "$repo" rev-parse HEAD)
(( $(du -sb "$repo/.git" | awk '{print $1}') <= 500000000 )) || fail 'Git history exceeds review limit before push'
[[ $(git -C "$repo" rev-parse HEAD) == "$run_head" ]] || fail 'local HEAD changed before push'
git -C "$repo" push origin "$run_head:refs/heads/$BRANCH" >/dev/null || fail 'remote push failed; inspect exact remote before retrying'
remote_head=$(git -C "$repo" ls-remote --heads origin "$BRANCH" | awk '{print $1}')
[[ $remote_head == "$run_head" ]] || fail 'this run commit not confirmed at remote'
printf 'Encrypted backup verified at private remote commit %s.\n' "$run_head"
Terminal
# No passwords, tokens, age identities or private keys belong here.
HERMES_BIN=/absolute/path/to/hermes
REPO_DIR=/absolute/path/to/dedicated-private-github-clone
STAGING_PARENT=/absolute/path/to/private-staging-outside-hermes-and-git
AGE_RECIPIENT=age1replace_with_operator_public_recipient
EXPECTED_REMOTE=git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git
BRANCH=main

Crie a preparação fora da raiz Hermes e do clone Git. Coloque os arquivos revisados no diretório scripts/ do perfil proprietário. O clone deve estar limpo e alinhado com a ramificação remota main antes de cada execução. O script trava o fluxo inteiro até envio e leitura de confirmação; nunca toma uma trava ocupada ou antiga. Aceita somente latest.zip.age e latest.sha256 como novos caminhos de backup preparados para Git. Para quando o arquivo criptografado excede 40.000.000 bytes ou o .git local mais a margem se aproxima de 500.000.000 bytes. São limites conservadores deste exemplo, não garantias de armazenamento GitHub.

Terminal
export HERMES_HOME=/absolute/path/to/owning-hermes-data-root
install -d -m 700 /ABSOLUTE/PRIVATE-STAGING
install -d -m 700 "$HERMES_HOME/scripts"
install -m 700 hermes-github-backup.sh "$HERMES_HOME/scripts/hermes-github-backup.sh"
install -m 600 hermes-github-backup.conf "$HERMES_HOME/scripts/hermes-github-backup.conf"
"$HERMES_HOME/scripts/hermes-github-backup.sh"

A execução manual deve terminar com código zero e imprimir o commit criado por aquela execução após uma nova leitura do remoto. Confirme que gh repo view ainda informa privado, inspecione os caminhos rastreados do clone e verifique o commit exato no remoto previsto. Um arquivo ou commit local não é resultado externo. Uma execução interrompida não deve ser registrada assim. Segundo o Hermes, o backup completo retorna 0 somente quando todos os arquivos selecionados foram adicionados, 1 para um arquivo incompleto, mesmo havendo ZIP, e 2 quando outro backup está em andamento. O script remove o ZIP temporário e o registro ao sair; excluir normalmente não é apagamento seguro. Para arquivos ignorados, investigue com um backup manual separado e controlado hermes backup -o /PRIVATE-DIAGNOSTIC/hermes.zip, confira o status de saída e o relatório e decida como proteger o material omitido.

3. Programe e inspecione a primeira execução prevista

Somente após o resultado manual, use o modo documentado --no-agent --script do Hermes no mesmo perfil. Ele executa um arquivo sem turno de LLM. Defina uma cadência deliberada e entrega local de status sem segredos. Confira identidade do sistema do gateway, perfil e próximo horário. Segundo o Hermes, o tempo máximo do script vem de HERMES_CRON_SCRIPT_TIMEOUT, depois de cron.script_timeout_seconds e depois do padrão de 3.600 segundos. Defina e confira um valor adequado a uma execução observada; esgotar o tempo é falha. O ambiente depurado do script não recebe automaticamente credenciais de provedores gerenciadas pelo Hermes: confira o acesso gh e SSH sem intervenção sob o usuário programado real antes de depender dele.

Terminal
hermes cron create '0 3 * * *' --no-agent --script hermes-github-backup.sh --deliver local --name 'Encrypted Hermes backup'
hermes cron list
hermes cron status
hermes cron doctor

Após o primeiro horário programado, inspecione o ID exato do trabalho, o registro da execução, a saída local em ~/.hermes/cron/output/ para o perfil padrão, qualquer incidente e o commit de ponta do remoto previsto. Confirme que o gateway permaneceu disponível e o trabalho informou o commit remoto produzido naquele disparo. Uma invocação manual não comprova que cron executou. Se faltar saída, inspecione o trabalho e o gateway, sem criar uma segunda programação. Não inclua conteúdo ZIP, segredos ou credenciais nas mensagens de status.

4. Comprove recuperação e importação em máquina nova

Use uma máquina Linux descartável separada, sem perfil ou gateway Hermes de produção. Instale primeiro uma versão compatível do Hermes: o arquivo contém dados de usuário, não uma imagem da aplicação ou ambiente. Dê à máquina de teste acesso de leitura autorizado e independente ao repositório privado previsto e à identidade age guardada separadamente. Confirme o remoto do clone e o commit escolhido. Não execute hermes import no servidor de origem ou em perfil existente. Mantenha todas as programações e ações externas recuperadas inativas até uma pessoa revisar seus destinos e decidir se as ativa.

Terminal
umask 077
unset HERMES_HOME
git clone git@github.com:ACCOUNT/PRIVATE-REPOSITORY.git /PRIVATE-RECOVERY/hermes-backup-clone
cd /PRIVATE-RECOVERY/hermes-backup-clone
sha256sum -c latest.sha256
age --decrypt -i /PRIVATE-KEY-STORE/hermes-backup-identity.txt -o /PRIVATE-RECOVERY/hermes.zip latest.zip.age
unzip -tq /PRIVATE-RECOVERY/hermes.zip
hermes import /PRIVATE-RECOVERY/hermes.zip
hermes setup
hermes cron list

Confira os perfis, habilidades, memórias, sessões e definições cron importados em relação ao que pretendia proteger. hermes setup verifica configuração de provedores, mas inspecione credenciais e caminhos específicos da máquina que precisem de ajustes. Registre os resultados reais. Soma de verificação correta e ZIP descriptografável, sozinhos, não comprovam restauração utilizável. Remova o ZIP de teste em texto claro após o exercício conforme sua política local; exclusão comum não garante apagamento seguro.

Quando as verificações param

Backup Hermes incompleto, erro ZIP ou de criptografia, URL Git efetiva inesperada, repositório não privado ou ilegível, trava de fluxo ocupada, limite de tamanho, envio falho, commit remoto divergente ou importação falha deixam esta execução não confirmada. Resolva a causa específica e confira o estado atual antes de tentar novamente. Se o resultado do envio for incerto, inspecione primeiro a ramificação remota e o commit exatos. Se a trava restar após uma falha, verifique processo vivo e estado local/remoto; nunca apague uma trava ativa. Se a identidade privada age se perder, é necessária uma cópia independente válida para descriptografar. Troque primeiro as credenciais expostas se segredos em texto claro entrarem no Git; excluir arquivo posterior não remove o histórico anterior ou outros clones.

As orientações GitHub sobre arquivos grandes alertam em 50 MiB, bloqueiam arquivos Git comuns acima de 100 MiB e dizem que Git não foi projetado como ferramenta de backup. Este exemplo para deliberadamente abaixo desses limites. Cada conteúdo criptografado alterado continua no histórico Git mesmo quando latest.zip.age é substituído. Não aumente silenciosamente os limites de 40 MB/500 MB, reescreva o histórico ou suponha que privado significa criptografado. Reavalie um armazenamento próprio para backups ou um plano LFS revisado separadamente se falharem as verificações de tamanho ou recuperação. Custos de conta, armazenamento, rede e servidor continuam valendo; o script sem agente em si não requer turno de LLM. Não se mediram duração, taxa de sucesso ou economia deste procedimento.

Para outra tarefa Hermes de programação mais restrita, consulte como criar um trabalho Hermes recorrente baseado em habilidade e verificar a primeira execução. Conferir seu relatório não substitui arquivo criptografado e exercício de recuperação.

Fontes e leituras adicionais