Two complementary projects under the git-pkgs umbrella that together allow any project, forge, registry, or researcher to understand their place in the dependency graph without relying on a single centralised index.
- Upstream — A feed reader for your dependencies. Subscribes to standardised feeds from packages you depend on, stores entries locally, and makes dependency activity queryable and actionable.
- Downstream — An ATProto client library for publishing and indexing dependency relationships. Enables decentralised reverse-dependency discovery by letting projects declare their dependencies as signed, structured records that anyone can index.
Both projects share a local-first, sqlite-backed architecture. They meet in the middle at the git-pkgs proxy, which serves as both an upstream feed aggregator and a downstream record publisher.
The split maps onto the asymmetry of the problem: upstream is fundamentally local knowledge (you already know what you depend on), downstream is fundamentally networked knowledge (you can't know who depends on you without them telling someone).
A project's dependencies are constantly changing — new releases, security advisories, deprecations, maintainer changes, funding status shifts. Today this information is scattered across registry feeds, forge release pages, advisory databases, and mailing lists. There is no unified, local, machine-readable way to monitor what's happening across your dependency tree.
Centralised platforms like GitHub Dependabot or Snyk provide some of this, but they are proprietary, opinionated about remediation, and unavailable to self-hosted or alternative forge users.
Upstream is an RSS/Atom feed reader purpose-built for dependency monitoring. It subscribes to feeds for every package in your dependency tree, enriches entries with standardised package metadata, and stores everything in a local sqlite database.
It is deliberately low-tech: no custom protocol, just well-known feed formats with structured extensions. It composes with what already exists — most forges and registries already publish feeds — rather than requiring upstream sources to adopt anything new.
Upstream aggregates feeds from multiple source types:
- Registry feeds — New releases, yanked versions, ownership changes (npm, PyPI, crates.io, RubyGems, Packagist, Hex, etc.)
- Forge feeds — Release notes, tags, changelogs from GitHub/GitLab/Forgejo/Codeberg. GitHub exposes per-repo
releases.atomonly (no org-level aggregate), so upstream enumerates and polls per-repo feeds with ETag/If-None-Match for cheap conditional GETs. - Advisory feeds — Security advisories from OSV, GHSA, NVD, and ecosystem-specific sources
- Ecosystem feeds — Maintainer activity, funding status, project health signals from ecosyste.ms and similar sources
Each feed entry is stored with standardised extensions that make it machine-actionable — a categorised entry type (release, advisory, deprecation, maintainer_change, yank, funding_change), purl identifier, ecosystem, affected version ranges, severity scores, and references.
Upstream derives its subscription list automatically from git-pkgs manifest and lockfile data. Running upstream init in a project with a package.json, Cargo.lock, go.sum, or any of the 30+ ecosystems git-pkgs supports will generate the initial feed list. No manual configuration required.
All feed data is stored in a local sqlite database using conventions shared with the broader git-pkgs toolchain. Once fetched, feed data is queryable offline.
A team or organisation running a git-pkgs proxy gets a shared, local view of what's happening across all their collective dependencies — advisories, releases, deprecations — with no external service dependency. The proxy fetches each feed once on behalf of all clients behind it.
Forgejo can integrate upstream to surface dependency activity directly within the forge UI. Since Forgejo already parses manifests via git-pkgs, it knows what each project depends on — upstream adds a live feed of what's happening with those dependencies. Advisories, new releases, and deprecations become visible alongside the project's code, issues, and pull requests. This gives self-hosted Forgejo instances dependency monitoring without relying on GitHub or any external platform.
- Remediation — Upstream surfaces activity; it does not open pull requests, pin versions, or prescribe fixes. Other tools can consume its database to do that.
- Centralised service — There is no hosted upstream. It runs wherever git-pkgs runs.
- Feed publishing — Upstream consumes feeds; emitting them is downstream's job (or the proxy's).
A maintainer cannot know who depends on their package without someone crawling every manifest in existence and building a central index. That index is expensive to run, a single point of failure, and structurally biased toward whatever the crawler can see. ecosyste.ms already captures dependency-change events, but surfacing patterns across them is too expensive on a single centralised database.
Downstream is a general-purpose Go ATProto client library — intentionally unopinionated about record content — that lets projects, forges, and registries publish dependency relationships as signed, structured records into their own repositories. Anyone can run a scoped indexer over the firehose to answer "who depends on X" without one party having to crawl and store everything. Compute spreads across whoever cares about a given slice.
Lexicons are defined under the dev.gitpkgs namespace, covering dependency events, snapshots, package metadata, and advisory alerts. They map closely to data git-pkgs already produces.
- Forges — A Forgejo instance publishes dependency records for every hosted repo as manifests change, becoming a first-class node in the network rather than something a third party has to crawl.
- Registries — Package registries publish release and ownership records.
- ecosyste.ms — Publishes the dependency-change events it already captures, letting third-party indexers do the expensive pattern analysis.
- Individual projects — Via git-pkgs directly, for projects not on a participating forge.
Downstream ships indexer scaffolding so anyone can subscribe to the firehose, filter to lexicons and purls they care about, and maintain a local reverse-dependency view in sqlite.
All git-pkgs tools share sqlite as their local storage layer with shared conventions — the same database that holds dependency resolution data, cached package metadata, and proxy state also holds upstream feed entries and downstream indexed records. This means every tool in the git-pkgs ecosystem can read from and contribute to the same local knowledge store.
Everything is keyed on Package URL (purl). A purl like pkg:npm/express@4.18.2 is the common identifier across manifests, feed entries, ATProto records, advisories, and proxy cache entries. That's what makes the joins work — a single identifier connects a dependency in your lockfile to its upstream feed, its advisory history, and its downstream consumers.
A single query can join across all of them: "my dependency X has a new advisory (upstream feed), it's used in these three modules (git-pkgs dependency graph), and here are the 30 downstream consumers of my package that also depend on X (downstream index)." That's a transitive risk view built entirely from local data, with no API calls.
This interop extends across deployment contexts too. A developer's local database, a Forgejo instance's database, and a proxy's database all use the same schema conventions and the same purl keys. Tooling built against one works against all of them.
git-pkgs has a subcommand plugin system, and both upstream and downstream ship as plugins — git-pkgs upstream ... and git-pkgs downstream .... This keeps a single entry point while allowing each project to be developed, versioned, and distributed independently. Third parties can build their own plugins on top of the same sqlite + purl foundation, extending git-pkgs with new capabilities without forking or modifying the core.
The git-pkgs proxy sits where the two directions meet. Inbound, it aggregates upstream feeds once on behalf of every client behind it. Outbound, it can publish downstream records for every package that flows through it. A single proxy deployment turns an organisation into both a consumer of dependency intelligence and a contributor to the decentralised graph, with no extra infrastructure.