Skip to content

Build from source

View Markdown ↗

Most users should download a release. This page is for contributors, custom artifacts, and unreleased changes.

Install JDK 25, then:

Terminal window
git clone https://github.com/bloxbean/yano.git
cd yano
./gradlew :app:quarkusBuild -PskipSigning=true
cd app
./yano.sh start:devnet

Choose the branch or tag containing the changes you want before building. For current namespace examples, use a checkout containing org.yanoproject; pre12 predates that rename.

The remaining commands build release-style distributions from the checked-out source tree.

Run commands from the repository root.

  • JDK 25.
  • Docker with the Compose plugin for Docker distributions and container native builds.
  • GraalVM 25 with native-image support for host native builds.

Use -PskipSigning=true for local builds that do not publish artifacts.

The public REST prefix is fixed during Quarkus augmentation. The sole supported build input is -PyanoApiPrefix=<path>; omitting it uses /api/v1. The value is at most 256 characters and must be / or a canonical absolute path made of unescaped [A-Za-z0-9._~-]+ segments, with no empty, . or .. segment or trailing slash.

For example, build a JVM distribution whose API is rooted at /bf:

Terminal window
./gradlew :app:yanoDistZip -PyanoApiPrefix=/bf -PskipSigning=true

The build generates literal REST configuration, reserves quarkus.http.root-path=/, and emits both the raw META-INF/yano-api-prefix-v1 marker and immutable /ui/plugins/api-prefix.json from that value. Do not change yano.api-prefix, quarkus.resteasy.path, or quarkus.http.root-path in launch configuration. Runtime-style system properties or environment variables for those keys are rejected during a build, and launch-time drift aborts before node or plugin initialization. Changing the prefix always means building a new JVM, native, or container artifact.

The packaged contract gate must pass independently for the default, a custom prefix, and the canonical root:

Terminal window
./gradlew :app:packagedApiPrefixContractSmoke -PskipSigning=true
./gradlew :app:packagedApiPrefixContractSmoke -PyanoApiPrefix=/bf \
-PskipSigning=true
./gradlew :app:packagedApiPrefixContractSmoke -PyanoApiPrefix=/ \
-PskipSigning=true

Each invocation builds and tests the matching artifact. The gate verifies its raw marker, dashboard discovery JSON, positive route, and fail-fast behavior for launch-time drift in either prefix property and the reserved HTTP root.

Build the JVM zip:

Terminal window
./gradlew :app:yanoDistZip -PskipSigning=true

Output:

app/build/distributions/yano-<version>.zip

The zip contains yano.jar, yano.sh, config files, network genesis files, plugin directory scaffolding, and the JVM-only offline plugin catalog tool under tools/yano-plugins/. It also contains the repository LICENSE and a normalized CycloneDX 1.6 inventory at sbom/yano.cdx.json; packaging fails if an external Maven component lacks license metadata.

After extracting the zip, start a network with the optional history archive by composing the bundled projection profile after the network profile:

Terminal window
./yano.sh start:preprod,projection
# or
./yano.sh start:mainnet,projection

The profile writes to DuckLake. The archive is fresh-sync only: it is built from genesis and there is no partial-coverage mode, so it needs an empty storage directory rather than an existing node’s. History is not supported by the native-image distribution.

After extracting the JVM zip, validate or inspect one or more plugin JARs without loading provider code:

Terminal window
./tools/yano-plugins/bin/yano-plugins validate plugins/example.jar
./tools/yano-plugins/bin/yano-plugins inspect --format table plugins/example.jar
./tools/yano-plugins/bin/yano-plugins inspect --format json plugins/example.jar
# Windows: tools\yano-plugins\bin\yano-plugins.bat validate plugins\example.jar

The CLI is also available as a standalone application distribution:

Terminal window
./gradlew :plugin-catalog:distZip -PskipSigning=true
unzip plugin-catalog/build/distributions/yano-plugins-<version>.zip \
-d /tmp/yano-plugins
/tmp/yano-plugins/yano-plugins-<version>/bin/yano-plugins validate plugin.jar

See plugin-catalog/README.md for policy options and stable exit codes, and PLUGIN_OPERATIONS.md for deployment authentication, health, metrics, and dashboard guidance.

Verify the final uber-JAR, its merged catalog/manifests, and JVM directory loading with the build-only conformance bundle:

Terminal window
./gradlew :app:packagedJvmPluginCatalogSmoke -PskipSigning=true

This task intentionally uses the default includeNativePluginConformanceFixture=false: the fixture must be absent from the application index so startup can prove it was selected from the external plugin directory. The task starts an isolated one-member app chain and asserts all ten catalog contribution kinds (NodePlugin plus nine typed SPIs), protected operations REST, the plugin health group, Prometheus metrics, and dashboard assets. The fixture’s adversarial TCCL handoff also proves plugin callbacks crossed catalog facades.

Build a native zip for the current host platform:

Terminal window
./gradlew :app:yanoNativeDistZip \
-Dquarkus.native.enabled=true \
-Dquarkus.package.jar.enabled=false \
-PskipSigning=true

Output:

app/build/distributions/yano-native-<version>-<platform>.zip

Examples:

yano-native-0.1.0-pre4-macos-arm64.zip
yano-native-0.1.0-pre4-linux-x64.zip
yano-native-0.1.0-pre4-linux-arm64.zip

The zip contains the native yano executable, yano.sh, config files, and network genesis files, plus the same LICENSE and release SBOM. It deliberately has no plugin directory or yano-plugins JVM runtime: native images cannot load JARs dynamically. Run the standalone JVM CLI on a JDK 25 operator/build host when offline validation is needed. Native Yano embeds only retained core providers. Optional Yano X state machines, connectors, and products are supported by the JVM distribution, not by augmenting the native build.

After the native zip task finishes, verify the final executable that it copied into the distribution:

Terminal window
./gradlew :app:nativePluginCatalogSmoke -PskipSigning=true

The smoke task regenerates the current packaged-JVM index and compares both its byte SHA-256 and selected-catalog fingerprint with the native executable’s startup provenance record. This makes an executable built with different plugin catalog inputs fail even if it starts and reports healthy; it is not a digest of unrelated application code. The same rule applies to -PyanoApiPrefix: pass the identical value to the native build, distribution, and smoke commands. A native prefix cannot be changed after image generation. Use -PyanoNativeBinary=<path> only to verify another executable built from the same catalog inputs. Release workflows always package first, smoke the resulting app/build/yano (or yano.exe), and only then upload the zip; this prevents a distribution-triggered native rebuild from replacing an executable that was already tested.

Maintainers can additionally exercise native reachability for every typed app-chain plugin SPI with the non-published conformance fixture:

Terminal window
./gradlew :app:quarkusBuild \
-PincludeNativePluginConformanceFixture=true \
-Dquarkus.native.enabled=true \
-Dquarkus.package.jar.enabled=false \
-PskipSigning=true
./gradlew :app:nativePluginCatalogSmoke \
-PincludeNativePluginConformanceFixture=true \
-PskipSigning=true

This property is a verification-only build input. Do not use the resulting binary as a release artifact; the dedicated CI job neither publishes nor packages it. The smoke starts an isolated one-member, no-peer app chain and asserts all ten catalog contribution kinds (NodePlugin plus nine typed SPIs) through structured status, protected operations REST, the plugin health group, Prometheus metrics, and dashboard assets. It also retains the catalog-provenance and ignored-directory-JAR checks.

For a Linux native binary from macOS, use Quarkus container native build. This is useful when preparing a Linux Docker native context locally:

Terminal window
./gradlew :app:yanoNativeDistZip \
-Dquarkus.native.enabled=true \
-Dquarkus.package.jar.enabled=false \
-Dquarkus.native.container-build=true \
-Dquarkus.native.builder-image=container-registry.oracle.com/graalvm/native-image:25i3 \
-PskipSigning=true

When Oracle’s native-image builder is selected, Gradle adds --gc=G1 and sets the container workdir to /project.

Build the Docker compose zip:

Terminal window
./gradlew :app:yanoDockerDistZip \
-PyanoDockerReleaseVersion=0.1.0-pre4 \
-PyanoDockerImageTag=0.1.0-pre4 \
-PskipSigning=true

Output:

app/build/distributions/yano-docker-0.1.0-pre4.zip

For local Docker image testing, use a local image tag:

Terminal window
./gradlew :app:yanoDockerDistZip \
-PyanoDockerReleaseVersion=0.1.0-pre4 \
-PyanoDockerImageTag=local \
-PskipSigning=true

The compose zip contains yano.sh, yano.bat, compose files, editable config/application.yml, editable config/network, logs, and plugins. Network-specific chainstate directories are created by the launcher on start or restart.

Docker images are built from Gradle-prepared artifact contexts:

Terminal window
./gradlew :app:prepareYanoDockerJvmContext -PskipSigning=true
Terminal window
./gradlew :app:prepareYanoDockerNativeContext \
-Dquarkus.native.enabled=true \
-Dquarkus.package.jar.enabled=false \
-Dquarkus.native.container-build=true \
-Dquarkus.native.builder-image=container-registry.oracle.com/graalvm/native-image:25 \
-PskipSigning=true

See docker/BUILD_FROM_SOURCE.md for full Docker image build and smoke-test commands.

Check zip contents:

Terminal window
unzip -l app/build/distributions/yano-*.zip | head
unzip -l app/build/distributions/yano-native-*.zip | head
unzip -l app/build/distributions/yano-docker-*.zip | head

Run JVM distribution:

Terminal window
unzip app/build/distributions/yano-<version>.zip -d /tmp/yano-jvm
cd /tmp/yano-jvm/yano-<version>
YANO_AUTO_SYNC_START=false ./yano.sh start

Run native distribution:

Terminal window
unzip app/build/distributions/yano-native-<version>-<platform>.zip -d /tmp/yano-native
cd /tmp/yano-native/yano-native-<version>-<platform>
YANO_AUTO_SYNC_START=false ./yano.sh start

Run Docker compose distribution with local images:

Terminal window
unzip app/build/distributions/yano-docker-<version>.zip -d /tmp/yano-docker
cd /tmp/yano-docker/yano-docker-<version>
./yano.sh config
./yano.sh start
curl -fsS http://localhost:7070/q/health/ready
./yano.sh stop

The normal node distribution includes its own UI where supported by the release. For a separately hosted console built from current source:

Terminal window
./gradlew :console-ui:consoleZip

Extract console-ui/build/distributions/yano-console-ui-<version>.zip under the static server’s /ui path. See console hosting for API routing and CORS.