The central package registry is so deeply embedded in how we think about dependencies that it barely registers as a design choice anymore. You have a language, it has a registry (npm, crates.io, PyPI, RubyGems), and that registry is where packages live. The workflow is so uniform across ecosystems that questioning it feels academic.
It is not academic. The registry model has known failure modes that have caused real damage, and the git-based alternative that Al Newkirk’s git-from project explores has genuine precedent across multiple mature ecosystems. The idea deserves examination on its technical merits.
What Registries Actually Do
Before treating registries as the problem, it helps to enumerate what they provide. A registry like npm or crates.io does several distinct things: it indexes packages for search and discovery, distributes artifacts through CDN infrastructure, enforces immutability on published versions, manages a naming namespace, and provides checksums for verification.
These are real services, and they are centralized services with centralized failure modes. The left-pad incident in 2016 was an early demonstration: one maintainer unpublishing a trivially small package broke builds across a substantial portion of the Node ecosystem because npm allowed it. The event-stream compromise in 2018 showed that registry-mediated package ownership could transfer to a malicious actor. The colors and faker incident in 2022 showed that maintainers themselves could introduce deliberate sabotage.
The registry functions as a trust boundary, and that boundary has been crossed more than once.
The Git-Native Alternative
Using a git repository directly as the module source sidesteps the registry entirely. The repository URL becomes the identifier, the tag or commit hash becomes the version, and distribution falls to whatever git hosting you choose. No intermediary controls your package namespace.
Go has made this structural since the introduction of Go modules in 2018. A Go import path like github.com/spf13/cobra is also a git URL. The go.sum file records cryptographic hashes of each dependency at the exact commit fetched. The module proxy and checksum database are optional infrastructure run by the Go team for performance and reproducibility, but you can bypass them entirely with GONOSUMDB and a local go.sum. The module system works without Google’s servers.
Zig’s package manager, introduced in stable form with Zig 0.11, uses a similar model. build.zig.zon specifies dependencies as URLs with content hashes:
.dependencies = .{
.known_folders = .{
.url = "https://github.com/ziglibs/known-folders/archive/fa75e1bc672952efa0cf06160bbd942b47f6d59a.tar.gz",
.hash = "1220bb12c9bfe291eed1afe6a2070c7c1d1ee7f6",
},
},
No registry. No package name. The URL is the identity, the hash is the integrity check, and nothing else is required. Zig shipped without a central registry at all; the ecosystem built tooling around the absence rather than working around the presence.
Deno went further. From its initial design, Deno allowed importing directly from URLs:
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
deno.land/x is a convenience CDN layered on top of GitHub tags, but you can import from any URL including raw git hosting. The lock file (deno.lock) records hashes to pin exact versions. Deno’s import maps allow aliasing URLs to shorter names while keeping the URL as the canonical identity.
Rust’s Cargo occupies middle ground. It is registry-centric by default, but Cargo.toml supports git dependencies directly:
[dependencies]
serde = { git = "https://github.com/serde-rs/serde", rev = "1.0.197" }
The Cargo team has historically discouraged this in favor of crates.io references, partly because ecosystem tooling (docs.rs, cargo audit) flows through the registry. Git dependencies are present but second-class.
What git-from Adds to This Picture
Al Newkirk’s git-from extends this approach with a focus on making git repositories a first-class module system rather than a secondary escape hatch. The framing distinction matters. Most of the tools above treat git-based dependencies as a fallback for packages not yet published to a registry, or as a temporary workaround during development. A tool designed around git as the primary module source changes the user model from the ground up.
The implications cascade. If module identity is a git URL, then forking is no longer a separate concept from versioning. You can depend on a fork without republishing anything to any registry. You can pin to a specific branch of upstream code, not just a tagged release. You can vendor dependencies by mirroring. The collaboration model that git already provides for source code extends naturally to dependency management, and the workflow your team already knows for managing code changes applies directly to managing dependency changes.
This is also what makes language-agnostic tooling in this space interesting. Go modules, Zig packages, and Deno imports each implement the git-URL-as-identity model, but they implement it within their own runtimes and build systems. A tool that makes the model available outside a specific language ecosystem has genuine utility for polyglot projects, configuration management pipelines, and tooling that does not fit any single language’s package manager.
The Trade-offs That Remain
None of this comes without cost, and the problems the registry model solves do not disappear by removing the registry.
Discoverability degrades without a central index. Finding a library requires knowing where to look, which shifts discovery to secondary tools like GitHub search, curated lists, or ecosystem documentation. For exploratory development, this is a real friction point. Registries did not become the dominant model because developers were naive about decentralization; they solved a genuine problem.
Versioning semantics shift in ways that require care. Semantic versioning over a registry is a convention with enforcement weight: npm treats published versions as immutable, and crates.io does the same. Git tags are arbitrary strings and can be moved or deleted. Zig’s approach of hashing archive contents rather than trusting the tag handles this correctly, but it requires the tooling to be explicit about what it trusts at each layer. A git-based system that pins branch names rather than content hashes provides no reproducibility guarantees at all.
Security scanning becomes harder without a central index. Tools like npm audit, cargo audit, and pip-audit query advisory databases that map package names and versions to known vulnerabilities. A git-URL-based system needs to map those same advisories to repository URLs and commit ranges, which is technically feasible but requires building parallel infrastructure. That infrastructure has not yet caught up to registry-based tooling in most ecosystems.
Namespace management becomes the user’s problem. Registries prevent two packages from having the same name within a namespace. With git URLs as identifiers, name conflicts are impossible by construction, but so is the discoverability that a shared namespace enables. You cannot go get cobra; you must know that it lives at github.com/spf13/cobra.
The Broader Trajectory
What makes this space worth watching is that the movement toward git-native dependency resolution is not primarily driven by new tooling. Established languages converged on it independently. Go made it structural from the start. Deno made URL imports idiomatic. Zig shipped without a central registry at all and built community tooling around the absence. These are different ecosystems arriving at variations of the same model, driven by the same frustrations with registry failure modes.
The pattern that emerges from all three is consistent: use a URL as the canonical identity, use a content hash for integrity, and treat any registry or proxy layer as optional infrastructure that improves performance and availability without being required for correctness. That combination sidesteps most of the failure modes that have historically caused damage, at the cost of discoverability infrastructure that has to be rebuilt outside the registry.
Projects like git-from are trying to make that pattern portable and explicit. Whether language-agnostic tooling can build enough ecosystem around the model to make discoverability less painful is the open question. The technical case for the approach is solid; the ecosystem case still requires work.