Skip to Content

Build-Time Token

Inject your FastStats token at build time so it stays out of your code and forks don't send data to your project

The setup examples pass "YOUR_TOKEN" directly to the context factory. That is fine for trying things out, but once your project lives in a public repository, a hardcoded token gets copied into every fork. Anyone who builds and runs a fork then sends their data to your FastStats project, mixing it with your own. The token also stays in the git history even after you remove it.

Instead, leave the token out of your code and let your build write it into a resource file. Gradle reads it from an environment variable (a CI secret) or a local Gradle property, and the SDK picks it up when the context is created. Forks don’t have your secret, so their builds don’t send data to your project.

Requires SDK version 0.32.2 or newer.

View full example on GitHub

How It Works

When you create a factory without a token, the SDK reads it from the resource META-INF/faststats/<id>.token in your jar. Surrounding whitespace is ignored. <id> depends on the platform:

Platform<id>Factory without token
Bukkitplugin name from plugin.ymlnew BukkitContext.Factory(plugin)
BungeeCordplugin name from plugin.ymlnew BungeeContext.Factory(plugin)
Hytaleplugin namenew HytaleContext.Factory(plugin)
Nukkitplugin name from plugin.ymlnew NukkitContext.Factory(plugin)
Velocityplugin iddon’t call .token(...)
Spongeplugin iddon’t call .token(...)
Fabric/Forge/NeoForgemod idnew FabricContext.Factory(modId) etc.
Minestomminestomnew MinestomContext.Factory()

The file name includes the id because Fabric, Forge and NeoForge load every mod from one shared class loader, so each mod needs its own file.

If the file is missing, empty or doesn’t contain a valid token, the context is created without any services: FastStats stays disabled, never sends data, and your plugin keeps working normally. With debug logging enabled, a warning names the expected resource path.

Gradle Setup

Add the following to your build script. Replace <id> with the id for your platform from the table above, for example your plugin name.

// 1. FASTSTATS_TOKEN environment variable (e.g. a CI secret)
// 2. faststats.token Gradle property (-P flag)
// 3. faststats.token file in the project directory (git-ignored)
val faststatsToken = providers.environmentVariable("FASTSTATS_TOKEN")
    .orElse(providers.gradleProperty("faststats.token"))
    .orElse(providers.fileContents(layout.projectDirectory.file("faststats.token")).asText.map { it.trim() })
    .orElse("")

tasks.processResources {
    val token = faststatsToken
    inputs.property("faststatsToken", token)

    doLast {
        val file = destinationDir.resolve("META-INF/faststats/<id>.token")
        file.parentFile.mkdirs()
        file.writeText(token.get())
    }
}

The file is written to build/, so it is never committed. Declaring the token as a task input reruns processResources whenever the token changes. The setup is compatible with the Gradle configuration cache. If you relocate FastStats with the Shadow plugin, the resource path stays the same.

Gradle skips processResources when src/main/resources is empty, so no token file is written. Plugins and mods always have a descriptor such as plugin.yml there. On Minestom, add any resource file if you don’t have one.

Maven Setup

View full Maven example on GitHub

Maven can write the file with resource filtering. Create the token file with a placeholder and commit it. Replace <id> in the file name with the id for your platform from the table above:

${faststats.token}

Then add the following to your pom.xml:

<properties>
    <!-- empty by default, so builds without a token still work -->
    <faststats.token></faststats.token>
</properties>

<profiles>
    <!-- CI: use the FASTSTATS_TOKEN environment variable when it is set -->
    <profile>
        <id>faststats-env</id>
        <activation>
            <property>
                <name>env.FASTSTATS_TOKEN</name>
            </property>
        </activation>
        <properties>
            <faststats.token>${env.FASTSTATS_TOKEN}</faststats.token>
        </properties>
    </profile>
</profiles>

<build>
    <resources>
        <!-- filter only the token file, so ${...} in other resources stays untouched -->
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>META-INF/faststats/*.token</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>false</filtering>
            <excludes>
                <exclude>META-INF/faststats/*.token</exclude>
            </excludes>
        </resource>
    </resources>
    <plugins>
        <!-- local builds: read faststats.token from a git-ignored faststats.properties file, if present -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>properties-maven-plugin</artifactId>
            <version>1.2.1</version>
            <executions>
                <execution>
                    <phase>initialize</phase>
                    <goals>
                        <goal>read-project-properties</goal>
                    </goals>
                    <configuration>
                        <files>
                            <file>${project.basedir}/faststats.properties</file>
                        </files>
                        <quiet>true</quiet>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

If you already filter all of src/main/resources (for example to put ${project.version} into your plugin.yml), you don’t need the two <resource> blocks. The token file is then filtered with everything else.

Maven takes the token from the first of these that is set:

  1. -Dfaststats.token=your-token on the command line
  2. faststats.token=your-token in a faststats.properties file next to your pom.xml. Add that file to your .gitignore.
  3. The FASTSTATS_TOKEN environment variable, for example a CI secret

If a property name has a typo, Maven leaves ${faststats.token} in the file as is. The SDK treats an invalid token in the file like a missing one and disables FastStats instead of failing on startup.

Using the Token

Create the factory without a token:

import dev.faststats.bukkit.BukkitContext;
import org.bukkit.plugin.java.JavaPlugin;

public final class ExamplePlugin extends JavaPlugin {
    // Reads META-INF/faststats/<id>.token, where <id> is the plugin name
    private final BukkitContext context = new BukkitContext.Factory(this)
            .metrics(factory -> factory.create())
            .create();

    @Override
    public void onEnable() {
        context.ready();
    }

    @Override
    public void onDisable() {
        context.shutdown();
    }
}

Local Development

With Gradle, provide the token in one of these ways when building locally (for Maven, see Maven Setup):

# Environment variable
FASTSTATS_TOKEN=your-token ./gradlew build

# Gradle property on the command line
./gradlew build -Pfaststats.token=your-token

To avoid passing it on every build, put the token in a faststats.token file in your project directory and add that file to your .gitignore. Each project keeps its own file, so this also works when you manage several projects:

your-token
faststats.token

Never put the token in your project’s own gradle.properties. That file is committed with the rest of the project. Avoid ~/.gradle/gradle.properties too: it is shared by all your projects, so every project would get the same token.

Without a token the build still succeeds and writes an empty token file. The plugin then runs with FastStats disabled.

GitHub Actions

  1. In your repository, open Settings → Secrets and variables → Actions.
  2. Click New repository secret, name it FASTSTATS_TOKEN and paste your token as the value.
  3. Pass the secret as an environment variable to the build step of your workflow. This works the same for Gradle and Maven:
- name: Build with Gradle
  run: ./gradlew build
  env:
    FASTSTATS_TOKEN: ${{ secrets.FASTSTATS_TOKEN }}

Other CI systems work the same way: store the token as a secret and expose it to the build as the FASTSTATS_TOKEN environment variable.

GitHub does not pass secrets to workflows triggered by pull requests from forks. Those builds still compile and run, but FastStats is disabled in them, so they don’t send data to your project.

Limitations

This is not meant to hide the token. Its purpose is to keep the token out of your code, so forks don’t send data to your project. The token is still stored in your jar as a plain text file, and anyone who has the jar can extract it. Project tokens can’t be rotated, so treat a token in a published jar as public.