restic-defensive-mcp

Servidor MCP stdio estruturalmente somente leitura para inspecionar repositórios restic. Repositórios e credenciais são selados na inicialização; apenas um subconjunto fixo de subcomandos restic pode ser executado (snapshots/ls/find/stats). Sem shell, sem backup/restore/forget/prune.

Documentação

restic-defensive-mcp

Servidor MCP (stdio) estruturalmente somente leitura para inspecionar repositórios restic por meio de um cliente MCP não confiável sem expor um shell, CLI de formato livre ou caminho de mutação.

Meus backups estão presentes e recentes, seus metadados são legíveis e um snapshot contém os arquivos que espero?

Por que não expor restic diretamente?

Restic é uma ferramenta de backup poderosa. Um chamador sem restrições pode:

  • excluir histórico de retenção (forget / prune)
  • sobrescrever ou extrair dados (backup / restore / dump)
  • desbloquear ou reescrever o estado do repositório
  • aceitar URLs de repositório arbitrárias e comandos de senha

Wrappers finos que expõem "execute restic com esses argumentos" ou substituições de repositório por chamada recriam esse raio de impacto dentro do MCP.

Este projeto é diferente por construção:

  1. Repositórios são declarados e selados no início do processo
  2. A superfície MCP é exclusivamente somente leitura (sem flag de recurso para gravações)
  3. Apenas um subconjunto compilado de subcomandos do restic pode ser construído
  4. Sem shell: exec.CommandContext apenas com argv fixo
  5. Hosts, tags, caminhos e tamanhos de resultados são limitados
  6. Segredos vêm de arquivos com permissão verificada, nunca de RESTIC_PASSWORD_COMMAND
  7. Erros são estruturados e redigidos
  8. Cada ferramenta informa uma classe de custo explícita (light / moderate / expensive)

Requisitos

  • Go 1.25+ (compilação)
  • restic 0.17.1+ em PATH (ou restic_binary na configuração); testado com 0.19.x
  • Um cliente MCP que suporte servidores stdio locais

Instalação (a partir do código-fonte)

git clone https://github.com/ThomasCrouzet/restic-defensive-mcp.git
cd restic-defensive-mcp
make build
./bin/restic-defensive-mcp --version

Início rápido

  1. Crie arquivos de senha e de localização do repositório (arquivos regulares, sem symlinks). Evite colocar a senha no histórico do shell:
secret_dir="${XDG_CONFIG_HOME:-$HOME/.config}/restic-defensive-mcp"
install -d -m 700 "$secret_dir"
install -m 600 /dev/null "$secret_dir/repository"
printf '%s\n' '/path/to/your/repo' > "$secret_dir/repository"
install -m 600 /dev/null "$secret_dir/password"
"${EDITOR:-vi}" "$secret_dir/password"
  1. Copie e edite config.example.yaml, incluindo os dois caminhos de arquivo acima.

  2. Execute o binário recém-compilado:

./bin/restic-defensive-mcp --config /path/to/config.yaml
  1. Aponte seu host MCP para o binário com --config (somente transporte stdio).

Exemplo de trecho de host MCP (ilustrativo):

{
  "mcpServers": {
    "restic-defensive": {
      "command": "/absolute/path/to/restic-defensive-mcp",
      "args": ["--config", "/etc/restic-defensive-mcp/config.yaml"]
    }
  }
}

Ferramentas (exatamente sete)

FerramentaCustoPropósito
restic_capabilitiesleveVersões, limites, backends, avisos
list_repositoriesleveIDs configurados e listas de permissão (sem URLs)
list_snapshotsleveLista de snapshots limitada
get_snapshotleveMetadados de um snapshot
browse_snapshotmoderadoListagem de diretório dentro do caminho permitido
find_filesmoderado–caroBusca simples por glob (não regex arbitrário)
repository_statsmoderado–caroEstatísticas de tamanho/contagem para modos permitidos

Não há nenhum run_restic, backup, restore, forget, prune, unlock, check --read-data nem argumento de URL de repositório.

Formas completas de requisição/resposta: docs/tool-contract.md.

Configuração

Veja config.example.yaml. Princípios:

  • Chamadores MCP passam apenas repository_id
  • Prefira repository_file + password_file em vez de segredos inline
  • Localizações de repositório e credenciais de backend são carregadas e seladas na inicialização; alterar seus arquivos de origem não redireciona um servidor em execução
  • allowed_hosts / allowed_tags / allowed_paths são aplicação de regras, não cosméticos
  • Listas de permissão vazias significam sem restrição para essa dimensão; o servidor registra um aviso na inicialização (empty_allowlist) para que operadores percebam
  • Chaves YAML desconhecidas e múltiplos documentos YAML são rejeitados
  • Arquivos de segredo devem ser arquivos regulares, sem symlink; Unix exige modo 0600 ou mais restritivo, enquanto implantações Windows devem aplicar ACLs NTFS equivalentes
  • find_files exige um path explícito quando múltiplas raízes estão na lista de permissão
  • Limites de tempo de execução: zeros se tornam padrões; valores fora do mínimo/máximo absoluto são rejeitados (não ajustados silenciosamente). Os padrões são o ponto de partida recomendado; valores podem ser aumentados apenas até esses tetos

Backends suportados (v0.1)

BackendSuportado
sistema de arquivos localsim (somente caminhos absolutos)
S3sim (credenciais via arquivos de ambiente permitidos)
Backblaze B2sim
servidor RESTsim
SFTPnão (inicia ssh)
rclonenão (inicia rclone)
outrosnão

Notas de compatibilidade: docs/restic-compatibility.md.

Efeitos locais: cache e locks

A inspeção não é um no-op puro no disco nem na tabela de locks do repositório:

  • restic pode criar ou atualizar um cache local (cache_dir ou padrão)
  • restic pode adquirir um lock de repositório durante snapshots/ls/find/stats

Este servidor nunca muta intencionalmente conteúdos de backup ou conjuntos de snapshots. Ele também nunca executa verificação completa de dados (restic check --read-data). repository_stats responde apenas perguntas de tamanho/contagem.

Detalhes: SECURITY.md.

Limites de privacidade

Nomes de arquivos de snapshot, caminhos, hosts, tags, tamanhos e timestamps são sensíveis. Este servidor:

  • nunca retorna conteúdos de arquivos
  • nunca retorna URLs de repositório ou caminhos de arquivos de segredo
  • limita listagens e resultados de busca
  • sanitiza caracteres de controle em nomes
  • mantém logs de auditoria livres de caminhos completos por padrão

Demonstração (repositório temporário local)

make demo

Executa o harness de ponta a ponta contra um repositório restic descartável criado por internal/testrepo. Ele exercita todas as ferramentas MCP, comprova a negação de caminho e a ausência de ferramentas de mutação e, em seguida, remove o fixture. As operações do repositório permanecem locais; downloads normais de módulos Go ainda podem ocorrer em um checkout novo.

Desenvolvimento

make fmt
make lint
make test          # unit tests
make race          # race detector
make fuzz-smoke    # short run of every fuzz target
make integration   # real restic temp repo (requires restic)
make vet

Dependências

MóduloPor quê
github.com/modelcontextprotocol/go-sdkSDK Go oficial do MCP (servidor/cliente stdio)
gopkg.in/yaml.v3Análise de arquivos de configuração

O próprio Restic é um binário externo, não um módulo Go.

Não-objetivos

  • Orquestração ou agendamento de backups
  • Restore / forget / prune / unlock / repair
  • Proxy genérico de CLI do restic
  • Importação de configuração do Autorestic
  • API remota multi-tenant
  • restic check --read-data completo como ferramenta casual
  • Telemetria ou atualização automática

Licença

MIT, veja LICENSE.