Reference documentation for the Nix module system and Oyasai scope used in the platform.
Oyasai scope
The Oyasai scope provides a curated set of packages and build tools.
Structure
oyasai-scope = lib.makeScope pkgs.newScope (
scopeSelf: {
# Build tools
gradle = /* ... */;
gradle2nix = /* ... */;
jdk = /* ... */;
jre = /* ... */;
# Builders
oyasaiPurpur = /* ... */;
oyasaiDockerTools = /* ... */;
# Plugin system
plugins-batch = /* ... */;
plugins = /* ... */;
# Server packages
oyasai-minecraft-main = /* ... */;
oyasai-plugin-registry = /* ... */;
}
);
gradle
Gradle 9 configured with Java 25.
gradle = pkgs.gradle_9.override { java = scopeSelf.jdk; };
gradle2nix
Builder for Gradle projects with dependency locking.
gradle2nix.buildGradlePackage {
pname = "plugins";
version = "0.0.0";
src = /* ... */;
lockFile = ./gradle.lock;
gradleBuildFlags = [ "build" ];
}
JDK/JRE
Temurin JDK 25 for building.
Temurin JRE 25 for running servers.
jdk = pkgs.javaPackages.compiler.temurin-bin.jdk-25;
jre = pkgs.javaPackages.compiler.temurin-bin.jre-25;
Plugin system
plugins-batch
All plugins are built together in a single derivation:
plugins-batch = scopeSelf.gradle2nix.buildGradlePackage {
pname = "plugins";
version = "0.0.0";
src = /* filtered source */;
inherit (scopeSelf) gradle;
buildJdk = scopeSelf.jdk;
lockFile = ../gradle.lock;
gradleBuildFlags = [ "build" ];
installPhase = ''
mkdir -p $out
cp plugins/*/build/libs/*.jar $out
'';
};
Individual plugins
Each plugin is derived from the batch build:
plugins = lib.mapAttrs' (
name: _:
lib.nameValuePair (lib.toLower name) (
pkgs.runCommand name { } ''
mkdir -p $out
cp ${plugins-batch}/${name}.jar $out
''
)
) (builtins.readDir ../plugins);
Access plugins:
with oyasai-plugins; [
oyasaiutilities
oyasaiadmintools
vertex
]
Package creation
callPackage pattern
oyasaiPurpur = callPackage ./oyasai-purpur.nix { };
The called file receives scope arguments automatically:
# nix/oyasai-purpur.nix
{
lib,
stdenv,
jre,
purpur-version,
...
}:
{ name, version, plugins ? [], ... }:
# Build logic
packagesFromDirectoryRecursive
Automatically import all packages from a directory:
lib.packagesFromDirectoryRecursive {
inherit callPackage;
directory = ../packages;
}
This imports:
packages/foo.nix as foo
packages/bar/default.nix as bar
Flake outputs
packages
Exposed packages:
packages = lib.filterAttrs (_: availableOnSystem) {
inherit (oyasaiScope)
oyasai-minecraft-main
oyasai-minecraft-marzipan
oyasai-minecraft-minimal
oyasai-plugin-registry
oyasai-push-nix-images
;
};
legacyPackages
All plugins are available:
legacyPackages.oyasai-plugins = oyasaiScope.plugins;
checks
All derivations become build checks:
checks = lib.concatMapAttrs (
k: v: lib.optionalAttrs (availableOnSystem v) {
"build-${k}" = v;
}
) (lib.filterAttrs (_: lib.isDerivation) (
oyasaiScope // oyasaiScope.plugins
));
Run checks:
Development shells
devShells
devShells.default = pkgs.mkShell {
buildInputs = with oyasai-scope; [
gradle
gradle2nix-cli
jdk
nodejs
terraform
];
};
Enter the shell:
System filtering
availableOnSystem = lib.meta.availableOn { inherit system; };
packages = lib.filterAttrs (_: availableOnSystem) { /* ... */ };
passthru = lib.optionalAttrs stdenv.hostPlatform.isLinux {
docker = /* Docker image only on Linux */;
};
Overriding packages
override
myServer = oyasai-minecraft-minimal.override {
plugins = [ /* custom plugin list */ ];
};
overrideAttrs
myServer = oyasai-minecraft-minimal.overrideAttrs (old: {
extraJavaArgs = old.extraJavaArgs ++ [ "-Xdebug" ];
});
Best practices
Use callPackage
Automatically inject dependencies from the scope.
Filter sources
Only include necessary files in derivations.
Lock dependencies
Use gradle.lock and flake.lock for reproducibility.
Test with checks
Include all packages in flake checks.
See also