Resolving through repo.jenesis.build
The Jenesis Module Index is served as an HTTP service at repo.jenesis.build. You ask for a module name, optionally a version, and a file name. The service answers with a 302 redirect to the real file, on Google's Maven Central mirror by default. Nothing is re-hosted: the module index only decides which Maven artifact a module name maps to and points you at it.
The whole contract is a small, stable set of URL shapes, so anything that can follow a redirect is a
client - curl -L, a browser, or the Jenesis build tool. Every redirect is derived from one row of a
plain text file, and you can read those files directly if you would
rather resolve yourself.
The four routes
The path segment before the module name selects the route. The route decides which version space you are addressing and which kind of module it can serve:
| Route | URL shape | Version segment is… |
|---|---|---|
artifact |
/ |
The Maven coordinate version. Any file extension passes through verbatim. Serves named and automatic modules. |
module |
/ |
The module-info version the publisher declared. Named modules only. |
sources |
/ |
The module-info version; the redirect appends -sources to the Maven file name. Named modules only. |
documentation |
/ |
The module-info version; the redirect appends -javadoc to the Maven file name. Named modules only. |
Two version spaces are in play: / is keyed by the Maven version you see in a POM, the other three
by the module-info version the publisher embedded. Pick the route that matches the version you hold.
The <file> segment is required and its name must start with the module name; everything after that is the
extension, either after a . or after -<classifier>.. / passes any extension through, while the
other three accept ., optionally followed by . for the detached OpenPGP signature or by
. for the Sigstore bundle - which follows the -sources / -javadoc decoration, so the
signature served is the one over that jar. For a checksum sidecar, use /.
module-info.class - is reachable
through /module/, /sources/ and /documentation/. An
automatic module, which only sets Automatic-Module-Name, resolves through
/artifact/ alone. The modules the JDK itself ships, such as java.base, are left
out of the named routes too, because the JDK's own copy always shadows a jar of that name. On
/artifact/ such a name serves whichever artifact declared it, under the usual rules. Other
java.* names, such as java.xml.bind, resolve on every route.
Versions are optional
Leave the version segment out and the service returns the newest release, by Maven's version ordering
with pre-releases passed over: org. resolves to 2. even though 2. ranks above it.
A version is a pre-release when a qualifier of it ranks below the release in Maven's own ordering - alpha,
beta, milestone (including the a1, b2, m3 shorthands), rc and its cr alias, snapshot - or
when it is one of the qualifiers Maven's ordering does not know but publishers use for the same purpose:
ea, pre, prerelease, preview, dev, nightly, canary, next, test, adhoc. Everything else is
a release, so 1., 33. and 1. resolve as ordinary versions. It is the rule the build
tool applies to a STABLE version.
# 2.0.19, the newest release of org.slf4j
curl -L -O https://repo.jenesis.build/artifact/org.slf4j/org.slf4j.jar
# 2.1.0-alpha1, the newest version of any kind
curl -L -O -H 'Jenesis-Prerelease: true' https://repo.jenesis.build/artifact/org.slf4j/org.slf4j.jar
# A specific version, pinned
curl -L -O https://repo.jenesis.build/artifact/org.slf4j/2.0.9/org.slf4j.jar
Jenesis-Prerelease: true drops the filter. Without it, a module that has only ever published pre-releases
answers 404 with a body naming the header that would have served it, while a version asked for by name is
served either way. The response carries the header back when what it served is a pre-release, and every
redirect is sent with Vary: Jenesis-Prerelease.
A named version matches exactly - no ranges, no normalisation. One the index has not recorded is still
answered: the service assumes it exists under the module's newest coordinate, redirects there, and flags that
with Jenesis-BestEffort: true, which is what keeps a release from the last few hours resolvable before the
crawl records it. Jenesis-BestEffort: false asks for a 404 instead of a guess; any other value, or none,
keeps it. If the redirect target - the mirror, or the repository the request named - has no such file, it
answers 404.
artifact route: every file of a coordinate
On the artifact route the extension is opaque: whatever follows the module name becomes the suffix of
the Maven file name. Because the extension passes straight through, one route serves the jar, the POM,
its checksums and signatures, and Gradle module metadata:
# The jar
GET /artifact/org.slf4j/org.slf4j.jar
→ 302 …/org/slf4j/slf4j-api/2.0.19/slf4j-api-2.0.19.jar
# The POM of a specific version
GET /artifact/org.slf4j/2.0.9/org.slf4j.pom
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.pom
# A checksum, or a signature - same pattern
GET /artifact/org.slf4j/2.0.9/org.slf4j.pom.sha256
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.pom.sha256
# Gradle module metadata, if the publisher provides it
GET /artifact/org.slf4j/2.0.9/org.slf4j.module
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.module
The file is always requested as <module>., never as <artifactId>-<version>.,
and there is no maven-metadata.. The Jenesis build tool speaks exactly this shape, so for it the
/ route is a complete repository. A stock Maven or Gradle resolver asks for Maven-shaped file
names and is not a client of this route.
module, sources, and documentation routes
These three are keyed by the module-info version, serve named modules only, and accept .,
. or .. They map to the main jar, the sources jar, and the javadoc jar of the
same artifact:
GET /module/org.slf4j/2.0.9/org.slf4j.jar
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar
GET /sources/org.slf4j/2.0.9/org.slf4j.jar
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9-sources.jar
GET /documentation/org.slf4j/2.0.9/org.slf4j.jar
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9-javadoc.jar
GET /module/org.slf4j/2.0.9/org.slf4j.jar.asc
→ 302 …/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.jar.asc
GET /module/net.bytebuddy/1.18.14/net.bytebuddy.jar.sigstore.json
→ 302 …/net/bytebuddy/byte-buddy/1.18.14/byte-buddy-1.18.14.jar.sigstore.json
Few publishers upload a Sigstore bundle yet, and the service redirects without checking that one exists, so a request for a bundle the release lacks follows the redirect to a 404.
A named release whose declared module-info version differs from its Maven version is left out of these
routes, because the service promises that the two agree (see the guarantee below). Such a release remains
reachable through / under its Maven version.
Classifiers
A classifier is the part of the file name between the first hyphen and the next dot. It switches the lookup to the classifier-scoped view of the module index and becomes a standard Maven classifier on the redirect target:
GET /artifact/p6spy/p6spy-all.jar
→ 302 …/p6spy/p6spy/3.9.1/p6spy-3.9.1-all.jar
The same works on every route: <module>-<classifier>. under / resolves the classifier's jar
of a named module.
Where the redirect points
The Location is Google's Maven Central mirror, which carries the same artifacts and is not rate
limited the way Central is. Send Jenesis-Repository: <url> to be redirected to another Maven
repository instead: the URL replaces the mirror as the base the artifact path is appended to, so
Jenesis-Repository: https:/ redirects to Maven Central itself, and the URL
of a repository manager redirects to it. A missing trailing slash is added. The value must be an absolute
http or https URL without credentials, a query or a fragment; anything else is answered with 400. No
header, or a blank one, keeps the mirror.
The mirror trails Central by a couple of hours, so a release published since its last sync is not there
yet. That is one case for the header. A redirect carrying Jenesis-BestEffort: true is the likeliest,
because it was built for a version the index has not recorded, which usually means a very new one.
# Google's mirror
curl -sI https://repo.jenesis.build/artifact/org.slf4j/org.slf4j.jar | grep -i '^location'
# Maven Central itself
curl -sI -H 'Jenesis-Repository: https://repo.maven.apache.org/maven2/' \
https://repo.jenesis.build/artifact/org.slf4j/org.slf4j.jar | grep -i '^location'
The 302 response
A successful response is an empty-bodied HTTP 302 whose Location points at the Maven URL. It is
cached with Cache-Control: public, max-age=, where the TTL is an hour
on the public service. A redirect to a repository named by Jenesis-Repository is marked private
instead, so a shared cache never hands one client's repository to another.
The resolved coordinate is echoed back as response headers, so a client can record exactly what it
fetched without parsing the Location:
| Header | When | Value |
|---|---|---|
Jenesis-GroupId |
always | Maven groupId of the resolved row. |
Jenesis-ArtifactId |
always | Maven artifactId. |
Jenesis-MavenVersion |
always | Maven coordinate version. |
Jenesis-ModuleVersion |
/, /, / |
The publisher-declared module-info version. Omitted on /, where the lookup key is already the Maven version. |
Jenesis-BestEffort |
a version the index has not recorded | true - the redirect was built from the module's newest coordinate rather than from a recorded row. Sent to the service as false, it declines such a redirect instead. |
Jenesis-Prerelease |
the version served carries a pre-release qualifier | true - the version was asked for by name, or the request opted in to pre-releases. |
Vary |
always | Jenesis-Prerelease, Jenesis-Repository, Jenesis-BestEffort - the redirect depends on all three request headers, so a shared cache must key on them. |
When a request fails
| Status | Meaning |
|---|---|
400 |
The Jenesis-Repository header is not a usable repository URL. |
404 |
Nothing could be served. The body says why when the module or version is the problem. |
405 |
The request was not GET or HEAD. |
500 |
The service failed unexpectedly. |
502 |
The upstream index files are temporarily unreachable. |
A 404 has one of these causes:
- the path is not one of the four shapes, or the file name does not start with the module name;
- the file name has no extension, or a
.-only route was asked for another extension;jar - the module name is unknown to the index, or has no named release on a
/-family route;module/ - no version was asked for and every version the module has published is a pre-release, without
Jenesis-Prerelease: trueon the request to accept one; - a version was asked for that the index has not recorded, and the request sent
Jenesis-BestEffort: falserather than accept a redirect built from the newest coordinate; - the module has no resolved owner in this view.
Stability guarantee
The service makes one promise you can build on. A recorded (module, moduleVersion) always resolves to
the same Maven artifact, and that artifact's Maven version is the same number as the module version.
Pin a module version in your build and every later rebuild resolves to the identical jar. Maven itself
does not enforce unique module versions; the index does.
The / lookup is stable for the same reason: Maven coordinates are immutable on Central. The
resolution of a name shifts only when the ownership of that name is re-decided - usually by an operator's
explicit policy, occasionally because an older publication of the name is discovered later.
404 for a name you expected, the artifact may ship neither a module-info nor
an Automatic-Module-Name, or it may be an automatic module you asked for on
/module/ - try /artifact/. The reports show
what is and is not covered.
Using it from the build tool
The Jenesis build tool points at repo. out of the box, and the layout decides which route
it uses. The default modular_ layout translates each requires into a Maven coordinate through
/, so named and automatic modules alike resolve. The strict modular layout resolves purely by
module name through /, so every dependency must be a named module. That difference is the whole
reason a plain-jar library is reachable in one layout and not the other.
Three settings move a build to another deployment of the module index; the Dependencies chapter of the build tool covers them in full, and shows which settings the build passes on as request headers:
| Setting | Environment variable | Purpose |
|---|---|---|
-Djenesis. |
JENESIS_ |
The base URL of the module index (default https:/). |
-Djenesis. |
JENESIS_ |
Sent verbatim as the Authorization header (for example Bearer …) to the first index jenesis. names, never to the default service. |
-Djenesis. |
JENESIS_ |
The local module repository consulted first (default ~/). |
A build can also skip the service and read the index itself, which Reading the index directly describes.
From the command line, curl -L is all a manual lookup needs:
# Follow the redirect and save the jar
curl -L -O https://repo.jenesis.build/module/com.fasterxml.jackson.databind/com.fasterxml.jackson.databind.jar
# Inspect only - see the redirect target and the coordinate headers
curl -I https://repo.jenesis.build/module/com.fasterxml.jackson.databind/com.fasterxml.jackson.databind.jar
Pointing at a mirror
The URL shapes are the contract, so any deployment that serves the same shapes is a drop-in replacement. The reference service is a small HTTP function that reads four optional environment variables:
| Variable | Default | Purpose |
|---|---|---|
DATA_ |
the index data on raw. |
An HTTP(S) base URL the resolved-view files are fetched from. Point it at a fork or mirror to serve a different index. |
ARTIFACT_ |
Google's Maven Central mirror | The base URL the 302 redirects target when a request does not name one with Jenesis-Repository. Point it at a Maven mirror or proxy. |
HOME_ |
the project's GitHub page | Where a request for / redirects. |
REDIRECT_ |
3600 (seconds) |
The max-age on the 302, and the edge-cache TTL for the upstream reads. |
Setting ARTIFACT_ changes only the default: a client's Jenesis-Repository header still overrides it.
Any number of path segments before the route marker are ignored, so the same service works whether it
is mounted at /, /, or /, with no configuration.
Reading the index directly
You do not have to go through the service at all. Each redirect comes from one row of a resolved view,
a plain tab-separated file you can read over raw. or any mirror - enough to build
a resolver of your own. Each module has a directory whose path mirrors its dot-separated name
(com. → com/). It holds up to four files:
| File | Role |
|---|---|
versions. |
The audit log: every (groupId, artifactId, version) that has ever declared this name, append-only in publication order, never pruned. |
artifacts. |
The resolved view keyed by Maven version, read by /. |
modules. |
The resolved view keyed by module-info version, read by /. Present only when the owner publishes named releases. |
owners. |
An optional ownership policy: which publishing groupIds are allowed or rejected for this name. |
artifacts. has four columns, sorted version-descending:
2.0.10 named org.slf4j slf4j-api
2.0.9 named org.slf4j slf4j-api
1.7.36 automatic org.slf4j slf4j-api
The columns are version, type (named or automatic), groupId, artifactId. Find the row whose
first column is your version and fetch <artifactId>-<version> from Maven Central.
modules. has four columns, sorted module-version-descending, and lists named releases only:
2.0.10 org.slf4j slf4j-api 2.0.10
2.0.9 org.slf4j slf4j-api 2.0.9
The columns are moduleVersion, groupId, artifactId, mavenVersion. Match the first column, then
fetch the coordinate named by the last three. Classifier-scoped variants live alongside as
artifacts-<classifier>. and modules-<classifier>..
A tool that needs only the mapping can read
data/
instead, regenerated daily. It lists named modules as <module>=, one per line, for
each name that starts with its owner's groupId or a known alias of it, such as kotlin for
org..
The build tool reads them this way on request, so a project that would rather not depend on the service can have its build resolve module names from the data and fetch the coordinates from the Maven repository it already uses:
java -Djenesis.module.source=git build/jenesis/Make.java
jenesis. (or JENESIS_) points that at a fork or a mirror of the data instead of the
published files, and jenesis. and jenesis. decide the same two
questions they decide for the service - whether a module asked for without a version may resolve to a
pre-release, and whether a version the data does not record may be fetched from the module's newest
coordinate anyway.
(groupId, artifactId) you expect rather than trusting a name on its own.