Maven
The proxy implements the Maven repository protocol, so any client that resolves from a Maven repository can build through ShieldedStack.
Endpoint: <proxy-url>/maven/
Authentication: the API key, sent as the password in HTTP basic auth. The username is not checked; _ is the conventional placeholder.
Configuration below is written for Maven, Gradle, and sbt. Each has its own resolver configuration and its own set of scopes that the main configuration does not cover, which is where dependencies most often escape the proxy.
This route proxies Maven Central. It does not proxy the Gradle Plugin Portal, which Gradle resolves separately.
In ~/.m2/settings.xml:
<settings> <servers> <server> <id>shieldedstack</id> <username>_</username> <password>YOUR_API_KEY_HERE</password> </server> </servers> <mirrors> <mirror> <id>shieldedstack</id> <name>ShieldedStack Maven Proxy</name> <url>PROXY_URL/maven/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors></settings>Replace PROXY_URL with the base URL. The samples on this page use a bare PROXY_URL rather than <proxy-url> so that the file stays valid XML before you substitute it.
The <id> on the server and the mirror must match, which is how Maven attaches the credentials to the mirror.
<mirrorOf>*</mirrorOf> is what stops repositories declared inside a build from bypassing the proxy. The trade-off is that an artifact the configured upstream does not carry will fail rather than fall back to another repository.
Gradle
Section titled “Gradle”In settings.gradle:
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { maven { name = "shieldedstack" url = uri("<proxy-url>/maven/") credentials { username = "_" password = System.getenv("SHIELDEDSTACK_API_KEY") } } }}This governs project dependency repositories in this build only. pluginManagement.repositories, legacy buildscript.repositories, buildSrc, and included builds each have their own repository scope and need reviewing separately. Enforcing this across an organization is a job for a centrally managed init script rather than per-project configuration.
sbt keeps the resolver and the credential apart, and both halves have a detail that decides whether it works.
In build.sbt:
externalResolvers := Seq( "shieldedstack" at "<proxy-url>/maven/")
credentials += Credentials( "ShieldedStack Package Proxy", "<proxy-host>", "_", sys.env.getOrElse("SHIELDEDSTACK_API_KEY", ""))externalResolvers := assigns over the default resolver list, which is what removes Maven Central. The familiar resolvers += ... adds to it instead and leaves Central in place, so builds keep succeeding while resolving around the proxy. This is sbt’s equivalent of Maven’s <mirrorOf>*</mirrorOf>.
The first argument to Credentials is the authentication realm and has to match what the proxy sends back, character for character. That value is ShieldedStack Package Proxy. A realm that does not match means the credential is never offered and the request goes out anonymous, which surfaces as a 401 rather than as a configuration error. The second argument is the host on its own, with no scheme and no port.
To keep the key out of the build file on a workstation, put it in ~/.sbt/.credentials:
realm=ShieldedStack Package Proxyhost=<proxy-host>user=_password=YOUR_API_KEY_HEREand reference the file instead:
credentials += Credentials(Path.userHome / ".sbt" / ".credentials")Plugins resolve in the meta-build, which has its own resolver scope. Repeat the externalResolvers assignment in project/build.sbt for plugin resolution, and again in project/project/build.sbt if the meta-build itself has plugins.
Committing the configuration
Section titled “Committing the configuration”Gradle’s and sbt’s forms above already read the key from the environment, so settings.gradle and build.sbt are safe to commit as written. An sbt ~/.sbt/.credentials file holds the key in plain text and stays on the machine.
For Maven, commit a settings.xml.template that references the environment variable, and generate the real file from it:
<settings> <servers> <server> <id>shieldedstack</id> <username>_</username> <password>${env.SHIELDEDSTACK_API_KEY}</password> </server> </servers> <mirrors> <mirror> <id>shieldedstack</id> <url>PROXY_URL/maven/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors></settings>Verify
Section titled “Verify”mvn -q dependency:tree./gradlew dependenciessbt updateThe resolved artifacts appear under Packages in the Portal, attributed to the project name on the API key.
Names in package filters
Section titled “Names in package filters”Filters name an artifact as groupId:artifactId, for example com.google.guava:guava. Maven Central coordinates are case-sensitive, so an allow rule or vulnerability exception applies only to the exact coordinates: an allow for com.google.guava:Guava does not cover com.google.guava:guava. Deny rules match any casing. A wildcard such as com.example.* covers every group under that prefix. See Package names for how every ecosystem compares names.
Troubleshooting
Section titled “Troubleshooting”401 or 403. Confirm the mirror <id> matches the server <id>, and that the credentials are username: _ with the API key as the password. In sbt, confirm the realm string is exactly ShieldedStack Package Proxy and that the host carries no scheme or port, since a mismatch on either makes sbt send the request unauthenticated.
Some dependencies still come from elsewhere. Confirm <mirrorOf>*</mirrorOf>. In Gradle, check the scopes FAIL_ON_PROJECT_REPOS does not cover: plugin management, buildscript, buildSrc, and included builds. In sbt, check that externalResolvers is assigned with := rather than added to with +=, and that project/build.sbt carries the same assignment for plugins.
Diagnosing a resolution failure. mvn -X dependency:tree for Maven. For Gradle, ./gradlew dependencies --info, which reports repository failures without printing credentials. For sbt, sbt "show externalResolvers" prints the resolver list actually in effect.