Sem banco de dados, sem nó líder: um servidor Git que é um único binário na frente do seu bucket
Faça o inventário do que hoje sustenta seu servidor Git self-hosted. Quase com certeza há um Postgres ou um MySQL com os metadados. Há um volume de disco que não pode ser perdido, porque é aí que vivem os objetos verdadeiros. Há backups das duas coisas, e há um procedimento de restore que ninguém testou este ano. E se alguma vez você quis rodar duas instâncias, apareceu a pergunta de quem comanda: um nó líder, um lock distribuído, um Redis, algo.
walgit apaga essa lista inteira. O README diz sem adornos:
“no database, no leader and no local state that matters”
(sem banco de dados, sem líder e sem estado local que importe)
E o princípio que o torna possível cabe em cinco palavras: “The bucket is the repository.” O disco e a memória da máquina onde roda são cache. A fonte de verdade é o object store.
É uma implementação em Rust da arquitetura que Cursor descreveu em Git at any scale, publicada por Tobi Lütke com licença MIT. Traz smart HTTP v0/v2 para fetch e push, clones via bundle-uri servidos como arquivos estáticos, Git LFS, uma UI web para browsing, uma API JSON com SDK, políticas de push por repositório, webhooks e auth OIDC. E, segundo o README, serve repositórios maiores que a máquina onde roda.
Todo o consenso é um compare-and-swap
Aqui está o mecanismo, e vale a pena entendê-lo antes de tocar um comando, porque explica por que a lista de infraestrutura de cima desaparece.
O README resume assim: a arquitetura consiste em usar “a write-ahead log in object storage as the source of truth, and making every on-disk repository a cache.” (um write-ahead log no object store como fonte de verdade, e converter cada repositório em disco em um cache.)
Quando chega um push, não se escreve em um banco de dados nem se pergunta a um coordenador. Agrega-se uma entrada ao WAL no bucket. O ponto de commit — o instante em que esse push passa a existir para todo o mundo — é uma operação de compare-and-swap sobre um manifest minúsculo. É o único ponto de coordenação de todo o sistema. As leituras usam conditional GET, então qualquer instância lê o estado atual sem perguntar a ninguém. E como qualquer instância pode reconstruir um repositório a partir do WAL, você pode apontar cinco máquinas para o mesmo bucket e elas não precisam se sincronizar entre si: sincronizam-se contra o object store, que é a única coisa que existe.
O post de Cursor descreve a mesma mecânica para o sistema que eles chamam de Continuity: “All updates to the write-ahead log are synchronized with an atomic compare-and-swap (CAS) operation on S3, so it’s always safe for any instance of a repository to receive a push.” E sobre o disco: “We treat repositories like a warm cache on disk, but the source of truth is always the write-ahead log in S3… If a repository is missing from the local disk when accessed on a host, we just materialize it from the WAL.”
No thread de Hacker News apareceu uma objeção que convém esclarecer, porque é o mal-entendido mais fácil de ter aqui: “I’m confused by the word consensus in the explanation, to me it seems like a last-write-wins strategy, no consensus involved?” (Fico confuso com a palavra consenso; me parece uma estratégia de last-write-wins.)
É exatamente o contrário. Em last-write-wins o segundo escritor sobrescreve o primeiro e o primeiro nunca fica sabendo. Em um compare-and-swap o segundo escritor falha: a escrita leva a condição „só se o manifest continuar sendo a versão que li‟, e se outro push entrou no meio, a condição não é atendida, a operação é rejeitada e é preciso relê-la e tentar novamente. Nada se perde. É o mesmo primitivo com o qual se constrói um lock, sem o lock.
Por que isto é de 2024 e não de sempre
Este é o dado que dá contexto a tudo o mais, e que explica por que de repente aparecem vários projetos com a mesma forma.
Esse compare-and-swap sobre object storage não existiu durante os primeiros dezoito anos de S3. AWS anunciou os conditional writes — o If-Match que torna possível o put-if-match — no 26 de novembro de 2024. Antes dessa data, qualquer sistema que precisasse de um ponto de commit atômico sobre S3 tinha que trazer um coordenador externo: DynamoDB, ZooKeeper, etcd, um Postgres.
Ryan Dahl o sinalizou publicamente sobre walgit: “like celld, walgit depends only on s3 for storage and coordination. This has all become possible because of S3’s support for Compare And Swap since 2024. It’s just a practical pattern for reliability and costs.” (walgit depende apenas de S3 para armazenamento e coordenação. Tudo isto se tornou possível graças ao suporte de compare-and-swap do S3 desde 2024. É apenas um padrão prático de confiabilidade e custos.)
O thread de HN converge no mesmo de outro ângulo e nomeia os vizinhos: SlateDB, PicoMQ, celld, e Terraform 1.10, que desde essa versão faz state locking diretamente em S3 e já não precisa da tabela de DynamoDB que foi obrigatória durante anos. walgit é a versão Git de um padrão que está aparecendo em toda a indústria ao mesmo tempo. Se você vem acompanhando o assunto, a conclusão prática é mais ampla que este repositório: um feature do S3 tirou o banco de dados de coordenação de toda uma classe de sistemas, e convém revisar quais dos seus ainda o estão pagando.
O que herda de Cursor, e por que te soa familiar
Se você leu o que escrevemos sobre Origin, há uma distinção de nomes que convém ter clara. Continuity é o sistema de armazenamento de Cursor; Origin é o produto de code hosting construído em cima dele. O que walgit implementa é Continuity, não Origin: o motor, não o forge.
E aí está o que é interessante no calendário. Naquela nota, o fechamento era que um forge é a coisa mais cara de mudar em seu stack, que Origin não oferecia self-hosting e que não havia rota de exportação documentada. Seis dias depois de Cursor publicar como funciona seu motor de armazenamento, essa arquitetura roda com licença MIT contra um bucket seu. Não substitui Origin como produto — já vamos ver tudo o que walgit deliberadamente não faz — mas a parte difícil, a que faz Git escalar sem um banco de dados, agora está em um repositório que você pode ler.
Uma observação para não titulá-lo errado: Lütke escreveu no X que o implementou “over the weekend as an exercise” depois de ler o post de Cursor, com o contexto de que estava frustrado com o sistema Git interno de Shopify. É um projeto pessoal, não um produto de Shopify nem algo que esteja declarado em produção em lugar algum.
Levantá-lo contra um bucket
O que segue sai do README e do justfile do projeto. Não executei nenhum desses comandos: não há bucket S3 nem toolchain de Rust no ambiente onde escrevo, então os blocos são fiéis à documentação, não a uma execução própria. Trate-os como tal.
O despliegue mínimo são três coisas: um bucket compatível com S3 (ou GCS), um arquivo de configuração e um binário.
1. O arquivo de configuração. Este é o exemplo do README, e é tudo o que um servidor funcional precisa:
cat > walgit.toml <<'EOF'
[server]
listen = "0.0.0.0:8080"
public_url = "https://git.example.com"
auto_create_on_push = true
[server.auth]
mode = "token"
anonymous_read = false
tokens = [{ principal = "me", token_env = "WALGIT_TOKEN_ME", write = true }]
[store]
backend = "s3"
bucket = "my-walgit"
[store.s3]
endpoint = "https://s3.us-east-1.amazonaws.com"
region = "us-east-1"
EOF
Fique atento a duas chaves. auto_create_on_push = true significa que o primeiro push para uma rota que não existe cria o repositório; conveniente para começar, algo que você provavelmente quer em false uma vez que haja mais de uma pessoa fazendo push. E tokens tira o segredo de uma variável de ambiente via token_env, não do arquivo — o arquivo de configuração pode ser versionado sem levar credenciais dentro.
2. Iniciar o servidor. Um comando, com o token gerado no ato:
WALGIT_TOKEN_ME=$(openssl rand -hex 24) walgit serve --config walgit.toml
3. Fazer push. É git padrão contra HTTP inteligente; não há cliente especial:
git -c http.extraHeader="Authorization: Bearer $WALGIT_TOKEN_ME" push https://git.example.com/acme/app.git main
Esse é o sistema completo. Para escalar horizontalmente, você aponta outra máquina para o mesmo bucket com a mesma configuração e pronto: não precisa uni-la a um cluster nem dizer quem é o líder, porque não há cluster nem líder.
Compilar
Há três caminhos, e o projeto traz os três empacotados:
just web-build && cargo build --release -p walgit-cli
nix build .#walgit
podman build -t walgit -f Containerfile .
Para testar localmente sem um bucket real, o repositório inclui um store de desenvolvimento e uma configuração standalone:
just dev-store
./target/release/walgit-server --config walgit.standalone.toml
Esse é o caminho que eu começaria: just dev-store primeiro, um push de teste contra walgit.standalone.toml, e só depois apontar para um bucket de verdade.
Auth
Três modos, em ordem de comprometimento crescente: none, token e oidc. O do exemplo é token, com anonymous_read como flag separado — você pode ter leitura aberta e escrita autenticada — e permissões de escrita por principal. oidc é o que você vai querer se já tiver um provedor de identidade e não quiser administrar tokens manualmente.
Acima disso há política de push por repositório e webhooks para eventos de refs, que é o que conecta isso com seu CI existente: walgit não traz CI, avisa quando algo mudou.
Manutenção: a parte que não aparece nos tutoriais
Um WAL que só cresce precisa que alguém o organize, e aqui é onde o projeto mostra que pensou além da demo. Os comandos de manutenção são: checkpoint, bundles, compaction, base rebuilds, connectivity audits e repairs.
Vale a pena entender para que serve cada família. Os checkpoints e a compaction mantêm o WAL limitado, para que materializar um repositório do zero não signifique reler meses de histórico. Os bundles são o que alimenta o bundle-uri: pacotes estáticos que um clone pode baixar direto do bucket, de modo que a parte pesada de um clone é servida pelo object store e não pelo seu processo. Os base rebuilds refazem a base sobre a qual as entradas incrementais se acumulam. E os connectivity audits e os repairs são o equivalente a git fsck para essa arquitetura: verificar que todos os objetos referenciados estão realmente lá.
Esse último par é o que eu colocaria em um cron antes de qualquer outro. Quando a fonte de verdade é um bucket, o tipo de erro que importa deixa de ser “o disco morreu” e passa a ser “há uma referência para um objeto que não existe”. Um audit programado é a diferença entre você descobrir e um desenvolvedor descobrir quando não consegue clonar.
No lado dos testes, o repositório traz just test, just e2e, just test-s3 para rodar contra um store real, e uma suite de simulação:
cargo test -p walgit-server --test sim
Que um projeto desse tamanho inclua simulação de falhas diz bastante sobre quão a sério a parte distribuída foi levada.
O que walgit não vai fazer
O repositório tem um arquivo GOAL.md que é mais útil que o README para decidir se isso te serve, porque enumera os não-objetivos de forma explícita. A meta declarada é “A share-nothing git host, fast for monorepos, with an object store as the only source of truth.” (Um host git sem compartilhamento, rápido para monorepos, com um object store como única fonte de verdade.)
E as exclusões são deliberadas: nada de code review, merge queues ou CI. Também não está otimizado para milhões de repositórios pequenos, nem pretende bifurcar git ou inventar formatos de objetos — usa git upstream, gix, Rust com tokio e axum, e object stores padrão.
Leia direito: walgit não é um substituto para GitHub ou GitLab. É a camada de armazenamento e transporte do Git, feita para monorepos grandes, e tudo que um forge te dá além disso continua sendo seu problema. Se o que dói é que seu servidor Git cai com o monorepo, isso aponta direto para lá. Se o que dói é que seu time precisa de pull requests, essa não é a ferramenta.
O mesmo arquivo declara objetivos de desempenho concretos: repositórios de vários GB sendo servidos rápido de máquinas com poucos GiB de RAM, lookups de refs a frio em menos de um segundo, e clones de CI em segundos. Importante: são metas de design escritas pelo autor, não resultados medidos publicados. Não há benchmark no repositório que as respaldem, então trate-as como a régua que o projeto se colocou, e meça-as você contra seu próprio repositório antes de acreditar.
Que tipo de projeto é
Aqui convém ser preciso, porque os sinais apontam em duas direções ao mesmo tempo.
A favor: o artefato está completo de uma forma que não é habitual. Containerfile e compose.yaml para containers, flake.nix para Nix, justfile com receitas de test, e2e e CI, walgit.example.toml e walgit.standalone.toml, auth OIDC, LFS, SDK, e a suite de simulação de falhas já mencionada. Nada disso é o que se encontra em um experimento publicado às pressas.
Contra: um único commit — o histórico vem compactado — e nenhuma release ou tag. Não há versão que você possa fixar em um Containerfile de produção; hoje você só pode apontar para uma branch. E o repositório não se declara experimental em lugar nenhum: nem o README nem GOAL.md trazem seção de status ou aviso. O único sinal de “isso é um exercício” está no X, não no projeto.
A thread do Hacker News (id=49420598) é pequena — 69 pontos, 11 comentários — e foi enviada por alguém externo ao projeto; o autor não participa da discussão. Então não há, por enquanto, um canal público onde o mantenedor está respondendo perguntas de operação.
Minha leitura prática: isso vale uma tarde contra um bucket de teste esta semana, e não vale colocar na frente do monorepo do qual seu time depende até que existam tags que você possa fixar. A arquitetura está provada — é a que Cursor roda — e a implementação se lê com cuidado, mas um servidor Git de produção sem versionamento é um problema operacional diferente da qualidade do código.
O que eu faria já, mesmo que nunca coloque walgit em produção: ler o mecanismo. Se você tem sistemas que hoje pagam um banco de dados só para ter um ponto de commit atômico sobre armazenamento de objetos, esse compare-and-swap de S3 provavelmente também te serve.
O que te custa hoje seu servidor Git em infraestrutura que não é Git — banco de dados, discos que não podem ser perdidos, um nó que é quem manda? Diga-me qual parte dessa lista você gostaria de apagar primeiro.