This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Njord (Njörðr)

Local staging, Artifact publishing and more!

Njord is an attempt to bring simple “publishing” (new term, is not “deploying with a twist”) to Maven 3 and Maven 4 with some extras like “local staging”.

Maveniverse Njörðr is a tool that makes possible “local staging” (as you may know from MRM), repository operations and publishing repositories.

https://github.com/maveniverse/njord

Maven generated plugin documentation

1 - What is it?

Publish where you want (and more)!

Njord is a Maven 3 and Maven 4 extension that offers local staging, repository operations and repository publishing. More precisely, Njord is a Resolver 1.x and 2.x extension (is yet another RepositoryConnector) that is loaded via Maven extensions mechanism and extends Resolver. Njord does not mingle with your build at all.

Njord supports “templates”, that define repository properties. Based on template a repository (“artifact store” in Njord lingo) is created. And those repositories can be merged, redeployed, dropped, validated or published (and more coming). Also, based on chosen template, Resolver is configured to deploy mandatory checksums for you. On the other hand, mandatory signatures are NOT generated, it is you who must provide them as part of your build.

In short, Njord keeps things “as before” (as without it): user never had to worry about checksums (Resolver did generate them always), while the signatures are usually provided by a plugin enabled in “release profile”. Njord goes step forward, and IF user selects any “SCA” template
(“SCA” stands for “Stronger Checksum Algorithms”, see Resolver Checksums) it will configure Resolver, and it will implicitly generate required stronger checksums without any user intervention required.

For now, templates supported out of the box are:

NameModeMandatory ChecksumsMandatoryRedeploy allowed?
releaseRELEASESHA-1, MD5GPG (Sigstore optional)no
release-scaRELEASESHA-512, SHA-256, SHA-1, MD5GPG (Sigstore optional)no
release-redeployRELEASESHA-1, MD5GPG (Sigstore optional)yes
release-redeploy-scaRELEASESHA-512, SHA-256, SHA-1, MD5GPG (Sigstore optional)yes
snapshotSNAPSHOTSHA-1, MD5GPG (Sigstore optional)no
snapshot-scaSNAPSHOTSHA-512, SHA-256, SHA-1, MD5GPG (Sigstore optional)no

The njord: URI

Njord can be considered also as new transport for Maven. It defines the njord: URI prefix and supports several forms:

  • njord: URI is a shorthand for njord:release-sca (default template), see below.
  • njord:<TEMPLATE> is equivalent to njord:template:<TEMPLATE>, see below.
  • njord:template:<TEMPLATE> URI when deployed to, will create new store using given template.
  • njord:store:<STORE> URI when deployed to, will try to deploy to given store that already must exist (will not be created).

Similarly, to use Njord URIs in cases like maven-deploy-plugin parameter for alternate deployment repository, the format accepts id::uri formatted string, so Njord URIs looks like id::njord: or id::njord:template:release-sca with one important detail: the id repository ID is there only to fulfil syntactical requirements, is unused otherwise. Store name will be determined at the moment of creation.

Example URIs:

  • njord: - means “use default template and create a new store” that is release-sca.
  • njord:snapshot - means “use template by name snapshot and create a new store”.
  • njord:store:release-00001 - means “select existing store release-00001 and use that”.

Basically by setting up your POM with distribution release repository using URL njord: and snapshot repository using URL njord:snapshot you are ready to use Njord. But, Njord is not intrusive, you can still use it by doing nothing in your project and just deploying with -DaltDeploymentRepository=id::njord: as well.

Hints:

2 - Using it

Using Njord!

Short explanation how to use Njord. It is totally non-invasive, but still you can integrate it as well with your project. The example below is not touching (modifying) the project it is about to publish.

There is only one required thing: the extension must be loaded. Still, the extension is non invasive, and remains fully dormant, does not interfere with your build at all, merely defines the njord: transport.

Setting it up

Njord can be added as POM build extension:

    <extensions>
      <extension>
        <groupId>eu.maveniverse.maven.njord</groupId>
        <artifactId>extension</artifactId>
        <version>${maveniverse.release.njordVersion}</version>
      </extension>
    </extensions>

Alternatively, with Maven 3 create project-wide, or with Maven 4+ create user-wide ~/.m2/extensions.xml like this:

<?xml version="1.0" encoding="UTF-8"?>
<extensions>
    <extension>
        <groupId>eu.maveniverse.maven.njord</groupId>
        <artifactId>extension</artifactId>
        <version>${currentVersion}</version>
    </extension>
</extensions>

It is recommended (but not mandatory) to add this stanza to your settings.xml as well if you don’t have it already (to not type whole G of plugin):

  <pluginGroups>
    <pluginGroup>eu.maveniverse.maven.plugins</pluginGroup>
  </pluginGroups>

Next, set up authentication. Different publishers require different server, for example sonatype-cp publisher needs following stanza in your settings.xml:

    <server>
      <id>sonatype-cp</id>
      <username>USER_TOKEN_PT1</username>
      <password>USER_TOKEN_PT2</password>
    </server>

Supported publishers and corresponding server.ids are:

Publisher (publisher ID)server.idWhat is needed
Sonatype Central Portal (sonatype-cp)sonatype-cpObtain tokens for publishing by following this documentation.
Sonatype OSS on https://oss.sonatype.org/ (sonatype-oss)sonatype-ossObtain tokens for publishing by following this documentation and using OSS instance.
Sonatype S01 on https://s01.oss.sonatype.org/ (sonatype-s01)sonatype-s01As above but using S01 instance.
Apache RAO on https://repository.apache.org/ (apache-rao)apache.releases.httpsAs above but using RAO instance.

Make sure your settings.xml contains token associated with proper server.id corresponding to you publishing service you want to use.

That’s all! No project change needed at all.

Using it

Next, let’s see an example of Apache Maven project (I used maven-gpg-plugin):

  1. For example’s sake, I took last release of plugin (hence am simulating release deploy): git checkout maven-gpg-plugin-3.2.7
  2. Deploy it (locally stage): mvn -P apache-release deploy -DaltDeploymentRepository=id::njord: (The id is really unused, is there just to fulfil deploy plugin syntax requirement. The URL njord: will use “default” store template that is RELEASE. You can target other templates by using, and is equivalent of this njord:release. You can stage locally snapshots as well with URL njord:snapshot. Finally, you can target existing store with njord:store:storename-xxx).
  3. Check staged store names: mvn njord:list
  4. Optionally, check locally staged content: mvn njord:list-content -Dstore=release-xxx (use store name from above)
  5. Optionally, validate locally staged content: mvn njord:validate -Ddetails -Dstore=release-xxx (use store name from above)
  6. Publish it to ASF: mvn njord:publish -Dstore=release-xxx -Dtarget=apache-rao (use store name from above)
  7. From now on, the repository is staged on RAO, so you can close it, vote, and on vote pass, release it. All the usual fluff as before.
  8. Drop locally staged store: mvn njord:drop -Dstor=release-xxx (use store name from above)

Check out Maven generated plugin documentation for more mojos.

3 - Configuring it

Using Njord!

Njord uses existing Maven infrastructure to get the configuration, still a bit more explanation is needed for some bits.

For start, user interacts with Njord via njord: URI using vanilla Maven plugins like maven-deploy-plugin is, and also using njord-maven-plugin Mojos. All the mojos does not require projects to be run (and are also aggregator Mojos). Still, IF Mojos are invoked with Maven Project present (ie in a checkout where POM is present, and Maven loads it) the Project will be used as “extra contextual source” for some operations.

Njord basedir

By default, Njord uses ~/.njord directory (in user home) directory as basedir. All the stores (locally staged artifacts) are here. This directory may also contain a plain Java properties file njord.properties to define some workstation-wide Njord configuration (usually not needed). The file full path is ~/.njord/njord.properties.

Njord properties

Njord applies following “precedence” rule to calculate effective (configuration) properties:

  • Maven system properties
  • Njord system-wide properties (sourced from ~/.njord/njord.properties)
  • Maven project properties (if present)
  • Maven user properties

This implies, that if you want to define for example njord.dryRun property, you can achieve it in multiple ways: it is possible even to have it in (effective) Project properties set by some profile. But be warned: in this example case, the property will be defined ONLY if you invoke Njord Mojos in this very same project using very same active profiles! Basic Maven stuff.

Of course, the recommended way to set this very property from example is Maven user property like mvn -Dnjord.dryRun ....

Project

Njord Mojos does not require project, and they can be invoked without any. But, in that case all the “heuristics” will be unavailable, and you will need to provide all the required input to Mojos explicitly. For example, assuming you have locally staged myproject-00001, you can still publish it by explicitly configuring publish mojo from a directory where no project exists:

$ mvn njord:publish -Dstore=myproject=00001 -Dpublisher=sonatype-cp

Naturally, your user wide settings.xml should be configured for this: authentication tokens should be set for server sonatype-cp.

When project is present, it implies several things:

First, “prefix” is known (top level project artifactId), in this example case, it would be myproject. When prefix is known, Njord will use simple heuristics, and will implicitly use last (newest) store prefixed with this prefix (so if you have myproject-00001 and myproject-00002 the latter would be selected).

Second, if project is present, and if distributionManagement is configured (usually is for projects being deployed), then Njord can deduce the publisher using the server.id from POM and the server configuration present in your settings.xml.

4 - Migrating projects

Using Njord!

Here will try to explain required steps to migrate your project to Njord. Assuming Maven basic knowledge, and some experience about existing publishing to Central, using phased out services like Sonatype OSS or Sonatype S01 are. Also, assuming you do have a project already, set up and probably published used via these legacy services.

Setup your namespace on Sonatype portal

Not much to say here, just follow the guide. Do not forget to enable SNAPSHOTS for namespace, if you intend to use them.

Edit your settings.xml and add your tokens to it. One of the main goals of Njord is to prevent copy-pasta happening in your Maven Settings. Users publishing one namespace may not experience this, but users publishing multiple namespaces are currently forced to copy-paste their auth tokens, as each project usually “invent” their own distribution management server IDs (exception is ASF, where ASF parent POM contains “well known” server ID).

Hence, Njord recommend to name and store your tokens only once in your Maven Settings (this example is for Sonatype Central Portal):

    <server>
      <id>sonatype-central-portal</id>
      <username>$TOKEN1</username>
      <password>$TOKEN2</password>
    </server>

Next, we will add a bit of Njord configuration: what publisher we want to use with this server, and what templates. Edit the server entry above to contain something like this:

    <server>
      <id>sonatype-central-portal</id>
      <username>$TOKEN1</username>
      <password>$TOKEN2</password>
      <configuration>
        <!-- Sonatype Central Portal publisher -->
        <njord.publisher>sonatype-cp</njord.publisher>
        <!-- Releases are staged locally (if omitted, would go directly to URL as per POM) -->
        <njord.releaseUrl>njord:template:release-sca</njord.releaseUrl>
        <!-- Snapshots are staged locally (if omitted, would go directly to URL as per POM) -->
        <njord.snapshotUrl>njord:template:snapshot-sca</njord.snapshotUrl>
      </configuration>
    </server>

And that’s it! Your settings.xml now contains auth for Sonatype Central Portal, and also tells Njord which publisher to use with this server (it is sonatype-cp), and which templates to use.

Note: if you want to use Central Portal Snapshots feature, then don’t forget to first enable these on Portal Web UI. Next, in that case you can remove the njord.snapshotUrl element, and enjoy “direct deploy” (so Njord does not meddle, or stage snapshots). Maven will go directly for Central Portal Snapshot endpoint. Any service that supports SNAPSHOT deploy and accepts maven-deploy-plugin deploy requests may be left without njord.snapshotUrl configuration as in that case that good old deploy plugin can do the job as well, no Njord needed.

Setup your project

As your project was already published to Central, the POM may contain distribution management like this:

  <distributionManagement>
    <snapshotRepository>
      <id>myproject-snapshots</id>
      <name>My Project Snapshots</name>
      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    </snapshotRepository>
    <repository>
      <id>myproject-releases</id>
      <name>My Project Releases</name>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
  </distributionManagement>

You do not have to change anything!. But, it is worth to change it. For start, the URLs for releases here are “fake”, and some tools like SBOM engines cannot use them as intended, as the URL is not “where artifacts are published”, it is “service used to publish” instead. It is recommended to change this POM section into something like this:

  <distributionManagement>
    <snapshotRepository>
      <id>sonatype-central-portal</id>
      <name>My Project Snapshots</name>
      <url>https://central.sonatype.com/repository/maven-snapshots</url>
    </snapshotRepository>
    <repository>
      <id>sonatype-central-portal</id>
      <name>My Project Releases</name>
      <url>https://repo.maven.apache.org/maven2</url>
    </repository>
</distributionManagement>

Yes, you see it right: POM now says the truth: “we publish to Central” and “we use Sonatype Central Portal” service. Also, there is no need to distinguish server for “release” and “snapshot”.

Finally, IF you have some existing plugin that did the job before, just remove, and undo all the hoops and loops that the plugin or tool required (like adding some properties, profiles, whatnot).

And you are done!

Extension

Finally, you need to make sure that Njord extension is loaded as extension. Ideally as POM project/build/extensions:

    <extensions>
      <extension>
        <groupId>eu.maveniverse.maven.njord</groupId>
        <artifactId>extension</artifactId>
        <version>${maveniverse.release.njordVersion}</version>
      </extension>
    </extensions>

But you can do it as core extension, in .mvn/extensions.xml or Maven 4 user wide ~/.m2/extensions.xml:

<?xml version="1.0" encoding="UTF-8"?>
<extensions>
    <extension>
        <groupId>eu.maveniverse.maven.njord</groupId>
        <artifactId>extension</artifactId>
        <version>${maveniverse.release.njordVersion}</version>
    </extension>
</extensions>

If you are putting Njord into POM, it is recommended to tie plugin version to extension version, by adding plugin management entry to POM/build/pluginManagement/plugins as:

        <plugin>
          <groupId>eu.maveniverse.maven.plugins</groupId>
          <artifactId>njord</artifactId>
          <version>${maveniverse.release.njordVersion}</version>
        </plugin>

Summary

To get summary, invoke mvn njord:status.

It will tell you all publishing related information about your project, even is the auth present or not (ie you may have a typo in server.id in POM or your settings.xml).

With this setup as above, one should see output like this (example is from BOM builder plugin), with added right hand “annotated explanations”:

[cstamas@angeleyes bom-builder-maven-plugin (master)]$ mvn njord:status
[INFO] Scanning for projects...
[INFO] Njord 0.6.0 session created
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] eu.maveniverse.maven.bom-builder:bom-builder                       [pom]
[INFO] eu.maveniverse.maven.plugins:bom-builder3                 [maven-plugin]
[INFO] eu.maveniverse.maven.bom-builder:it3                               [pom]
[INFO] 
[INFO] ------------< eu.maveniverse.maven.bom-builder:bom-builder >------------
[INFO] Building eu.maveniverse.maven.bom-builder:bom-builder 1.1.1-SNAPSHOT [1/3]
[INFO]   from pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- njord:0.6.0:status (default-cli) @ bom-builder ---
[INFO] Project deployment:
[INFO]   Store prefix: bom-builder  <------------------------------------------------ store prefix, build-builder-00001, etc
[INFO] * Release
[INFO]   Repository Id: sonatype-central-portal
[INFO]   Repository Auth: Present  <------------------------------------------------- auth is present
[INFO]   POM URL: https://repo.maven.apache.org/maven2/  <--------------------------- POM "truth", once published, artifacts are here
[INFO]   Effective URL: njord:template:release-sca  <-------------------------------- The Njord URL we use when deploy releases
[INFO] - release-sca  <-------------------------------------------------------------- The template we set, detailed
[INFO]     Default prefix: 'bom-builder'
[INFO]     Allow redeploy: false
[INFO]     Checksum Factories: [SHA-512, SHA-256, SHA-1, MD5]
[INFO]     Omit checksums for: [.asc, .sigstore, .sigstore.json]
[INFO] * Snapshot
[INFO]   Repository Id: sonatype-central-portal
[INFO]   Repository Auth: Present
[INFO]   POM URL: https://central.sonatype.com/repository/maven-snapshots/  <-------- Snapshots do not use Njord, mvn deploy deploys directly Portal
[INFO] 
[INFO] No candidate artifact stores found  <----------------------------------------- Nothing has been locally staged yet
[INFO] 
[INFO] Project publishing:
[INFO] - 'sonatype-cp' -> Publishes to Sonatype Central Portal  <-------------------- The publishing service we set, detailed
[INFO]   Checksums:
[INFO]     Mandatory: SHA-1, MD5
[INFO]     Supported: SHA-512, SHA-256
[INFO]   Signatures:
[INFO]     Mandatory: GPG
[INFO]     Supported: Sigstore
[INFO]   Published artifacts will be available from:
[INFO]     RELEASES:  central @ https://repo.maven.apache.org/maven2/
[INFO]     SNAPSHOTS: sonatype-central-portal @ https://central.sonatype.com/repository/maven-snapshots
[INFO]   Service endpoints:
[INFO]     RELEASES:  sonatype-central-portal @ https://central.sonatype.com/api/v1/publisher/upload
[INFO]     SNAPSHOTS: sonatype-central-portal @ https://central.sonatype.com/repository/maven-snapshots
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for eu.maveniverse.maven.bom-builder:bom-builder 1.1.1-SNAPSHOT:
[INFO] 
[INFO] eu.maveniverse.maven.bom-builder:bom-builder ....... SUCCESS [  0.037 s]
[INFO] eu.maveniverse.maven.plugins:bom-builder3 .......... SKIPPED
[INFO] eu.maveniverse.maven.bom-builder:it3 ............... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.329 s
[INFO] Finished at: 2025-05-24T22:49:03+02:00
[INFO] ------------------------------------------------------------------------
[INFO] Njord session closed
[cstamas@angeleyes bom-builder-maven-plugin (master)]$ 

Publish it!

With this setup, you just perform mvn deploy as you did before. If your checkout is snapshot, you will deploy to Central Portal Snapshots. If your checkout is a release, it will be locally staged once Maven finishes.

To publish, just use mvn njord:publish, or just do mvn deploy -Dnjord.autoPublish.

Once Maven returns, your project is being validated at https://central.sonatype.com/publishing

Check out Maven generated plugin documentation for more mojos.

What if don’t want (or cannot) change the POM?

In this case Njord can still be used, but the “inconvenience” is that you need to hand over all info to Njord.

The maven user settings change is mandatory, you need to have tokens set in your settings.xml.

The presence of Njord extension is mandatory as well, You can load it as user-wide extension (~/m2/extensions.xml) if you use Maven 4 or you can create (or edit if exists) project .mvn/extensions.xml.

Below I assume you work with a release checkout (ie you checked out a tag, also the “release profile” in this example follow ASF convention):

$ mvn -P apache-release deploy \                                                     1)
         -DaltDeploymentRepository=id::njord: \                                      2)
         -Dnjord.autoPublish \                                                       3)
         -Dnjord.publisher=sonatype-cp \                                             4)
         -Dnjord.publisher.sonatype-cp.releaseRepositoryId=sonatype-central-portal   5)

So what happens here? We invoke “deploy for release” (1), but using “alternate deployment repository” (2), that will create an artifact store from default template. Note that id::url form is standard format accepted by maven-deploy-plugin but the id is in fact unused, as store ID will be known only after it is created. At session end (3) the created store with deployed artifacts will be published, using specified publisher service (4) and auth material from specified server (5) in user settings.xml.