Skip to main content
The Root.io Gradle Plugin intercepts dependency resolution and transparently substitutes vulnerable dependencies with Root-patched versions from pkg.root.io/maven/. You declare the versions you know, and the plugin handles the rest—no changes to your dependency declarations required.

Root.io Gradle Plugin

View the source code, report issues, or contribute to the Root.io Gradle Plugin.

Prerequisites

Installation

The plugin is published to the Gradle Plugin Portal. Apply the plugin in build.gradle.kts:
plugins {
    id("io.root.patcher") version "0.2.0"
}
If your pluginManagement block explicitly lists repositories, make sure gradlePluginPortal() is included:
pluginManagement {
    repositories {
        gradlePluginPortal()
    }
}

Option 2: Root.io Maven Repository

Add the Root.io Gradle Plugin repository to settings.gradle.kts:
pluginManagement {
    repositories {
        maven {
            url = uri("https://pkg.root.io/gradle-plugins")
            credentials {
                username = "token"
                password = providers.environmentVariable("ROOTIO_API_KEY").get()
            }
        }
        gradlePluginPortal()
    }
}
Apply the plugin in build.gradle.kts:
plugins {
    id("io.root.patcher") version "0.2.0"
}

Option 3: Local Maven Repository

For testing or air-gapped environments:
cd rootio_gradle_plugin/
make publish-local
Then add mavenLocal() to your pluginManagement repositories:
pluginManagement {
    repositories {
        mavenLocal()
        gradlePluginPortal()
    }
}

Option 4: Private JFrog Artifactory

Publish and consume the plugin through your own JFrog Artifactory instance - useful when you need full control over the registry or want to keep builds air-gapped from the public internet.

Publishing

Set the following environment variables and run make publish:
export ARTIFACTORY_URL=https://your-instance.jfrog.io/artifactory/your-repo
export ARTIFACTORY_USER=your-username
export ARTIFACTORY_PASSWORD=your-api-key
make publish
This publishes both the plugin JAR and the Gradle plugin marker artifact (io.root.patcher:io.root.patcher.gradle.plugin) that Gradle requires for plugins {} block resolution.

Consuming

Add the Artifactory repository to pluginManagement in your settings.gradle.kts:
pluginManagement {
    repositories {
        maven {
            url = uri("https://your-instance.jfrog.io/artifactory/your-repo")
            credentials {
                username = providers.environmentVariable("JFROG_USERNAME").orNull
                password = providers.environmentVariable("JFROG_TOKEN").orNull
            }
        }
    }
}
Then apply the plugin as usual in build.gradle.kts:
plugins {
    id("io.root.patcher") version "0.2.0"
}

Using JFrog as a Proxy for pkg.root.io

If your organization routes all artifact traffic through an internal proxy, you can also point the plugin at your JFrog instance for resolving patched artifacts - instead of reaching pkg.root.io directly. In JFrog, create a remote Maven repository with the remote URL set to https://pkg.root.io/maven and configure your Root.io API key as the upstream password. JFrog will handle authentication to pkg.root.io on behalf of your builds - individual developers never need a Root.io API key. In your build.gradle.kts, override pkgUrl and supply JFrog credentials via pkgUsername/pkgPassword:
rootio {
    pkgUrl.set("https://your-instance.jfrog.io/artifactory/your-remote-repo")
    pkgUsername.set(providers.environmentVariable("JFROG_USERNAME").get())
    pkgPassword.set(providers.environmentVariable("JFROG_TOKEN").get())
}
pkgUsername/pkgPassword control only where patched artifacts are downloaded from, not where the plugin itself is resolved.

How It Works

When you run a build, the plugin will:
  1. Query Root.io API for patches on your declared dependencies
  2. Substitute patched versions if available
  3. Register pkg.root.io/maven/ as a Maven repository
  4. Cache results locally at .gradle/rootio-cache/

Configuration

Configure the plugin in build.gradle.kts:
rootio {
    apiKey.set("your-api-key-here")  // Optional if using env vars
    apiUrl.set("https://api.root.io")  // Default
    pkgUrl.set("https://pkg.root.io/maven")  // Default; override to use a proxy
    pkgUsername.set("...")  // Optional; overrides apiKey for pkg repo auth
    pkgPassword.set("...")  // Optional; overrides apiKey for pkg repo auth
    ttlHours.set(24)  // Cache TTL (0 = no cache)
    maxRetries.set(3)  // Max retry attempts
    retryBaseDelayMs.set(1000)  // Exponential backoff base
    allowInsecurePkgRepo.set(false)  // Allow HTTP (test only)
    ignore = ["com.example:my-lib@1.0.0-root.io.5"]  // Skip specific patch versions
}

Configuration Properties

PropertyDefaultDescription
apiKey-Root.io API key (see API Key Resolution). Also used as a fallback password for the default pkg.root.io Maven repository when pkgUsername/pkgPassword are not set.
apiUrlhttps://api.root.ioRoot.io API base URL
pkgUrlhttps://pkg.root.io/mavenMaven repository URL from which patched artifacts are resolved. Override this when routing through a proxy (see Option 4).
pkgUsername-Username for the pkg Maven repository. When set together with pkgPassword, takes precedence over apiKey for repository authentication.
pkgPassword-Password for the pkg Maven repository. When set together with pkgUsername, takes precedence over apiKey for repository authentication.
ttlHours24Cache TTL in hours (0 disables caching)
maxRetries3Max retry attempts for transient API failures
retryBaseDelayMs1000Base delay (ms) for exponential backoff
allowInsecurePkgRepofalseAllow plain HTTP for package repo (test/local only)
ignore[]Patch versions to skip, as group:artifact@patch-version. The list is sent to the API so the server returns the best available alternative. See Ignoring patch versions.

API Key Resolution

The plugin resolves the API key in this order (highest priority first):
  1. Build script: rootio { apiKey.set("...") }
  2. Environment variable: export ROOTIO_API_KEY=...
  3. JVM system property: systemProp.ROOTIO_API_KEY=... in ~/.gradle/gradle.properties
  4. .env file: ROOTIO_API_KEY=... in project root
Keep your API key out of source control. Use environment variables or JVM system properties for CI/CD.

Multi-Project Setup

Apply the plugin once at the root and propagate to all subprojects:
// Root build.gradle.kts
plugins {
    id("io.root.patcher") version "0.2.0" apply false
}

subprojects {
    apply(plugin = "io.root.patcher")

    repositories {
        mavenCentral()
    }
}

// app/build.gradle.kts
dependencies {
    implementation("io.netty:netty-all:4.1.42.Final")
}

// lib/build.gradle.kts
dependencies {
    implementation("commons-beanutils:commons-beanutils:1.9.3")
}
Run the build:
export ROOTIO_API_KEY="your-api-key-here"
./gradlew build
All subprojects will automatically use Root-patched dependencies.

Ignoring patch versions

If a specific Root.io patch version causes a regression, you can exclude it. The ignore list is sent to the Root.io API with every request — the server skips the excluded version and returns the best available alternative. Example: commons-lang3 was patched to 3.12.0-root.io.5 but it broke your build. Ignore it to fall back to 3.12.0-root.io.4:
# .rootioignore
org.apache.commons:commons-lang3@3.12.0-root.io.5
The API will respond with 3.12.0-root.io.4 instead.

Three ways to configure ignores (all merged together)

1. .rootioignore file — place in the project root directory, one entry per line:
# Comments and blank lines are ignored
org.apache.commons:commons-lang3@3.12.0-root.io.5
com.google.guava:guava@31.1-jre-root.io.2
2. Build scriptrootio { ignore = [...] }:
rootio {
    ignore = ["org.apache.commons:commons-lang3@3.12.0-root.io.5"]
}
3. Gradle property-Prootio.ignore=... (comma-separated), useful for one-off builds:
./gradlew build -Prootio.ignore=org.apache.commons:commons-lang3@3.12.0-root.io.5
The entry format is group:artifact@patch-version (exact match, case-sensitive).
Changing the ignore list for a dependency automatically invalidates any cached result for that dependency, so the API is re-queried with the updated list.

Caching

The plugin caches Root.io API responses locally at .gradle/rootio-cache/ to avoid repeated network calls. The cache is not used when:
  • The cached response is older than the TTL (default: 24 hours)
  • ttlHours.set(0) is configured (caching disabled)
  • A dependency is queried for the first time
  • The cache directory has been manually cleared
To clear the cache:
rm -rf .gradle/rootio-cache/

Detailed Flow

  1. Plugin applies to your project and creates the rootio {} configuration extension
  2. API key resolution happens in priority order (build script → env var → system property → .env)
  3. Root.io Maven repository is registered automatically with credentials
  4. Resolution hooks are installed on every resolvable configuration
  5. For each dependency, the plugin:
    • Checks the local cache (.gradle/rootio-cache/) for a valid entry (keyed on coords + ignore list)
    • If cache miss, queries Root.io API (/v3/analyze/maven) with the dependency and the ignore list
    • Substitutes the dependency coordinates if a patch is available
    • Logs the patching decision
  6. Gradle resolves the patched version from pkg.root.io/maven/

Troubleshooting

IssueSolution
401 Unauthorized from Root.io APIVerify ROOTIO_API_KEY is set correctly (see API Key Resolution)
404 Not Found for patched artifactConfirm the patched version exists in pkg.root.io/maven/ (check Root.io dashboard)
Plugin not applying patchesCheck Gradle console output for [Root.io] log messages. Ensure plugin is applied before dependencies are declared.
Network timeoutsIncrease retryBaseDelayMs and maxRetries in the rootio {} block
Cache is staleDelete .gradle/rootio-cache/ or set ttlHours.set(0) to disable caching
ResolutionStrategy conflictsRoot.io plugin uses eachDependency hooks. Ensure no other plugins conflict with dependency resolution.
Multi-project builds not patching subprojectsVerify apply(plugin = "io.root.patcher") is in the subprojects {} block
If you encounter issues, check the Gradle build output for [Root.io] prefixed log messages. Run with --info or --debug for detailed output: ./gradlew build --info