Sin base de datos, sin nodo líder: un servidor Git que es un solo binario delante de tu bucket
Haz el inventario de lo que hoy sostiene tu servidor Git self-hosted. Casi con seguridad hay un Postgres o un MySQL con la metadata. Hay un volumen de disco que no puede perderse, porque ahí viven los objetos de verdad. Hay backups de las dos cosas, y hay un procedimiento de restore que nadie probó este año. Y si alguna vez quisiste correr dos instancias, apareció la pregunta de quién manda: un nodo líder, un lock distribuido, un Redis, algo.
walgit borra esa lista entera. El README lo dice sin adornos:
“no database, no leader and no local state that matters”
(sin base de datos, sin líder y sin estado local que importe)
Y el principio que lo hace posible cabe en cinco palabras: “The bucket is the repository.” El disco y la memoria de la máquina donde corre son caché. La fuente de verdad es el object store.
Es una implementación en Rust de la arquitectura que Cursor describió en Git at any scale, publicada por Tobi Lütke con licencia MIT. Trae smart HTTP v0/v2 para fetch y push, clones vía bundle-uri servidos como archivos estáticos, Git LFS, una UI web para browsing, una API JSON con SDK, políticas de push por repositorio, webhooks y auth OIDC. Y, según el README, sirve repositorios más grandes que la máquina donde corre.
Todo el consenso es un compare-and-swap
Acá está el mecanismo, y vale la pena entenderlo antes de tocar un comando, porque explica por qué la lista de infraestructura de arriba desaparece.
El README lo resume así: la arquitectura consiste en usar “a write-ahead log in object storage as the source of truth, and making every on-disk repository a cache.” (un write-ahead log en el object store como fuente de verdad, y convertir cada repositorio en disco en un caché.)
Cuando llega un push, no se escribe en una base de datos ni se le pregunta a un coordinador. Se agrega una entrada al WAL en el bucket. El punto de commit —el instante en que ese push pasa a existir para todo el mundo— es una operación de compare-and-swap sobre un manifest diminuto. Es el único punto de coordinación de todo el sistema. Las lecturas usan conditional GET, así que cualquier instancia lee el estado actual sin preguntarle a nadie. Y como cualquier instancia puede reconstruir un repositorio desde el WAL, puedes apuntar cinco máquinas al mismo bucket y no necesitan sincronizarse entre ellas: se sincronizan contra el object store, que es lo único que existe.
El post de Cursor describe la misma mecánica para el sistema que ellos llaman 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.” Y sobre el 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.”
En el hilo de Hacker News apareció una objeción que conviene aclarar, porque es el malentendido más fácil de tener acá: “I’m confused by the word consensus in the explanation, to me it seems like a last-write-wins strategy, no consensus involved?” (Me confunde la palabra consenso; me parece una estrategia de last-write-wins.)
Es exactamente lo contrario. En last-write-wins el segundo escritor pisa al primero y el primero nunca se entera. En un compare-and-swap el segundo escritor falla: la escritura lleva la condición “solo si el manifest sigue siendo la versión que leí”, y si otro push entró en el medio, la condición no se cumple, la operación se rechaza y hay que releer y reintentar. No se pierde nada. Es el mismo primitivo con el que se construye un lock, sin el lock.
Por qué esto es de 2024 y no de siempre
Este es el dato que le da contexto a todo lo demás, y que explica por qué de repente aparecen varios proyectos con la misma forma.
Ese compare-and-swap sobre object storage no existió durante los primeros dieciocho años de S3. AWS anunció los conditional writes —el If-Match que hace posible el put-if-match— el 26 de noviembre de 2024. Antes de esa fecha, cualquier sistema que necesitara un punto de commit atómico sobre S3 tenía que traerse un coordinador externo: DynamoDB, ZooKeeper, etcd, un Postgres.
Ryan Dahl lo señaló públicamente 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 solo de S3 para almacenamiento y coordinación. Todo esto se volvió posible gracias al soporte de compare-and-swap de S3 desde 2024. Es un patrón práctico de confiabilidad y costos.)
El hilo de HN converge en lo mismo desde otro lado y nombra a los vecinos: SlateDB, PicoMQ, celld, y Terraform 1.10, que desde esa versión hace state locking directamente sobre S3 y ya no necesita la tabla de DynamoDB que fue obligatoria durante años. walgit es la versión Git de un patrón que está apareciendo en toda la industria al mismo tiempo. Si vienes siguiendo el tema, la conclusión práctica es más amplia que este repositorio: una feature de S3 sacó la base de datos de coordinación de toda una clase de sistemas, y conviene revisar cuáles de los tuyos todavía la están pagando.
Lo que hereda de Cursor, y por qué te suena
Si leíste lo que escribimos sobre Origin, hay una distinción de nombres que conviene tener clara. Continuity es el sistema de almacenamiento de Cursor; Origin es el producto de code hosting construido encima. Lo que walgit implementa es Continuity, no Origin: el motor, no el forge.
Y ahí está lo interesante del calendario. En esa nota, el cierre era que un forge es lo más caro de cambiar en tu stack, que Origin no ofrecía self-hosting y que no había ruta de exportación documentada. Seis días después de que Cursor publicara cómo funciona su motor de almacenamiento, esa arquitectura corre con licencia MIT contra un bucket tuyo. No reemplaza a Origin como producto —ya vamos a ver todo lo que walgit deliberadamente no hace— pero la parte difícil, la que hace que Git escale sin una base de datos, ahora está en un repositorio que puedes leer.
Un apunte para no titularlo mal: Lütke escribió en X que lo implementó “over the weekend as an exercise” después de leer el post de Cursor, con el contexto de que estaba frustrado con el sistema Git interno de Shopify. Es un proyecto personal, no un producto de Shopify ni algo que esté declarado en producción en ningún lado.
Levantarlo contra un bucket
Lo que sigue sale del README y del justfile del proyecto. No ejecuté ninguno de estos comandos: no hay bucket S3 ni toolchain de Rust en el entorno donde escribo, así que los bloques son fieles a la documentación, no a una corrida propia. Trátalos como tales.
El despliegue mínimo son tres cosas: un bucket compatible con S3 (o GCS), un archivo de configuración y un binario.
1. El archivo de configuración. Este es el ejemplo del README, y es todo lo que necesita un servidor funcional:
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
Fíjate en dos claves. auto_create_on_push = true significa que el primer push a una ruta que no existe crea el repositorio; cómodo para empezar, algo que probablemente quieras en false una vez que haya más de una persona empujando. Y tokens toma el secreto de una variable de entorno vía token_env, no del archivo — el archivo de configuración se puede versionar sin llevarse credenciales adentro.
2. Arrancar el servidor. Un comando, con el token generado al vuelo:
WALGIT_TOKEN_ME=$(openssl rand -hex 24) walgit serve --config walgit.toml
3. Empujar. Es git estándar contra smart HTTP; no hay cliente especial:
git -c http.extraHeader="Authorization: Bearer $WALGIT_TOKEN_ME" push https://git.example.com/acme/app.git main
Eso es el sistema completo. Para escalar horizontalmente, apuntas otra máquina al mismo bucket con la misma configuración y listo: no hay que unirla a un cluster ni decirle quién es el líder, porque no hay cluster ni líder.
Compilarlo
Hay tres caminos, y el proyecto trae los tres empaquetados:
just web-build && cargo build --release -p walgit-cli
nix build .#walgit
podman build -t walgit -f Containerfile .
Para probarlo en local sin un bucket real, el repositorio incluye un store de desarrollo y una configuración standalone:
just dev-store
./target/release/walgit-server --config walgit.standalone.toml
Ese es el camino que yo empezaría: just dev-store primero, un push de prueba contra walgit.standalone.toml, y recién después apuntar a un bucket de verdad.
Auth
Tres modos, en orden de compromiso creciente: none, token y oidc. El del ejemplo es token, con anonymous_read como flag separado —puedes tener lectura abierta y escritura autenticada— y permisos de escritura por principal. oidc es el que querrás si ya tienes un proveedor de identidad y no quieres administrar tokens a mano.
Encima de eso hay política de push por repositorio y webhooks para eventos de refs, que es lo que conecta esto con tu CI existente: walgit no trae CI, avisa cuando algo cambió.
Mantenimiento: la parte que no aparece en los tutoriales
Un WAL que solo crece necesita que alguien lo ordene, y acá es donde el proyecto muestra que pensó más allá del demo. Los comandos de mantenimiento son: checkpoint, bundles, compaction, base rebuilds, connectivity audits y repairs.
Vale la pena entender para qué sirve cada familia. Los checkpoints y la compaction mantienen el WAL acotado, para que materializar un repositorio desde cero no signifique releer meses de historia. Los bundles son lo que alimenta el bundle-uri: paquetes estáticos que un clone puede bajar directo del bucket, de modo que la parte pesada de un clone la sirve el object store y no tu proceso. Los base rebuilds rehacen la base sobre la que se apilan las entradas incrementales. Y las connectivity audits y los repairs son el equivalente a git fsck para esta arquitectura: verificar que todos los objetos referenciados estén realmente ahí.
Ese último par es el que yo pondría en un cron antes que ningún otro. Cuando la fuente de verdad es un bucket, la clase de error que te importa deja de ser “se murió el disco” y pasa a ser “hay una referencia a un objeto que no está”. Un audit programado es la diferencia entre enterarte tú y enterarte por un desarrollador que no puede clonar.
Del lado de las pruebas, el repositorio trae just test, just e2e, just test-s3 para correr contra un store real, y una suite de simulación:
cargo test -p walgit-server --test sim
Que un proyecto de este tamaño incluya simulación de fallos dice bastante sobre qué tan en serio se tomó la parte distribuida.
Lo que walgit no va a hacer
El repositorio tiene un archivo GOAL.md que es más útil que el README para decidir si esto te sirve, porque enumera los no-objetivos de forma explícita. La meta declarada es “A share-nothing git host, fast for monorepos, with an object store as the only source of truth.” (Un host de git share-nothing, rápido para monorepos, con un object store como única fuente de verdad.)
Y las exclusiones son deliberadas: nada de code review, merge queues ni CI. Tampoco está optimizado para millones de repositorios chicos, ni pretende forkear git ni inventar formatos de objetos —usa git upstream, gix, Rust con tokio y axum, y object stores estándar.
Léelo al derecho: walgit no es un reemplazo de GitHub ni de GitLab. Es la capa de almacenamiento y transporte de Git, hecha para monorepos grandes, y todo lo que un forge te da además de eso sigue siendo tu problema. Si lo que te duele es que tu servidor Git se cae con el monorepo, esto apunta justo ahí. Si lo que te duele es que tu equipo necesita pull requests, esto no es la herramienta.
El mismo archivo declara objetivos de rendimiento concretos: repositorios de varios GB sirviéndose rápido desde máquinas de pocos GiB de RAM, lookups de refs en frío por debajo de un segundo, y clones de CI en segundos. Importante: son metas de diseño escritas por el autor, no resultados medidos publicados. No hay benchmark en el repositorio que las respalde, así que trátalas como la vara que el proyecto se puso, y mídelas tú contra tu propio repositorio antes de creerlas.
Qué clase de proyecto es
Acá conviene ser preciso, porque las señales apuntan en dos direcciones a la vez.
A favor: el artefacto está completo de una manera que no es habitual. Containerfile y compose.yaml para contenedores, flake.nix para Nix, justfile con recetas de test, e2e y CI, walgit.example.toml y walgit.standalone.toml, auth OIDC, LFS, SDK, y la suite de simulación de fallos ya mencionada. Nada de eso es lo que se encuentra en un experimento publicado a las apuradas.
En contra: un solo commit —la historia viene aplastada— y ningún release ni tag. No hay versión que fijar en un Containerfile de producción; hoy solo puedes apuntar a una rama. Y el repositorio no se declara experimental en ninguna parte: ni el README ni GOAL.md traen sección de estado o advertencia. La única señal de “esto es un ejercicio” está en X, no en el proyecto.
El hilo de Hacker News (id=49420598) es chico —69 puntos, 11 comentarios— y lo subió alguien ajeno al proyecto; el autor no participa en la discusión. Así que no hay, por ahora, un canal público donde el mantenedor esté respondiendo preguntas de operación.
Mi lectura práctica: esto vale una tarde contra un bucket de prueba esta semana, y no vale ponerlo delante del monorepo del que depende tu equipo hasta que existan tags que puedas fijar. La arquitectura está probada —es la que Cursor corre— y la implementación se lee cuidada, pero un servidor Git de producción sin versionado es un problema operativo distinto del de la calidad del código.
Lo que sí haría ya, aunque nunca despliegues walgit: leer el mecanismo. Si tienes sistemas que hoy pagan una base de datos solo para tener un punto de commit atómico sobre almacenamiento de objetos, ese compare-and-swap de S3 probablemente también te sirva a ti.
¿Qué te cuesta hoy tu servidor Git en infraestructura que no es Git — base de datos, discos que no se pueden perder, un nodo que es el que manda? Cuéntame qué parte de esa lista te gustaría borrar primero.