Quickstart

From zero to a module on screen

Two routes: download the released JAR, or build it yourself. The download needs no credentials; building does, because Gradle resolves mirror-api from GitHub Packages.

On this page

Route A — use the released JAR

Every v* tag publishes the built JAR as a release asset. Downloading an asset does not go through GitHub Packages, so no token is involved:

curl -LO https://github.com/pierrejochem/SpeculumExampleModule/releases/latest/download/SpeculumExampleModule.jar
cp SpeculumExampleModule.jar /path/to/Speculum/plugins/

Restart the mirror and the module appears at top_center. The current release is v1.1.0, which needs mirror-api 1.3.0 — that is Speculum v1.3.0 or newer.

Route B — requirements

Why a token for a public package: org.speculum:mirror-api is public, but GitHub's Maven registry still requires authentication for every download. This is a registry policy, not a permission on the package.

Credentials for GitHub Packages

Keep them out of the repository. Either put them in your user Gradle properties:

# ~/.gradle/gradle.properties
gpr.user=<your-github-username>
gpr.token=<a-PAT-with-read:packages>

…or export them instead:

export GITHUB_ACTOR=<your-github-username>
export GITHUB_TOKEN=<a-PAT-with-read:packages>
Never commit the token. The repository's own gradle.properties deliberately contains no credentials — only a comment pointing here.

Build

git clone https://github.com/pierrejochem/SpeculumExampleModule.git
cd SpeculumExampleModule
./gradlew jar

The JAR lands in build/libs/SpeculumExampleModule.jar. Check the thin-JAR contract — it should hold only the example classes and the service file, with no Compose or mirror-api classes inside:

unzip -l build/libs/SpeculumExampleModule.jar

./gradlew build additionally runs ktlint, detekt and the test suite.

Deploy and run

Point the deployToMirror task at a Speculum checkout or install:

./gradlew deployToMirror -Pspeculum.pluginsDir=/path/to/Speculum/plugins

The SPECULUM_PLUGINS_DIR environment variable does the same. With neither set the JAR is copied to build/plugins/ for you to move yourself.

Then start the mirror the usual way (see Speculum's Getting Started). The module is discovered on startup — nothing to register.

Choosing a mirror-api version

mirror-api is published on every v* tag of the Speculum project; the artifact version is the tag without its leading v. Set it in gradle/libs.versions.toml:

[versions]
mirrorApi = "1.3.0"

Pin the oldest version whose API you actually use — a module compiled against an older mirror-api still loads in a newer host, because added SPI methods carry default implementations. settingsSchema() is the reason this project needs 1.3.0.