commit 9a9520c4ada7caf0492497fce19edac9e2648da9
parent f4948427d48fe7aa1b7cdc6de58a370870354807
Author: triesap <tyson@radroots.org>
Date: Sun, 6 Sep 2026 23:46:23 +0000
build: publish real Lib Nix outputs
- Replace fixture-owned defaults with the public library release bundle.
- Expose governed overlays and shared service helpers on exact systems.
- Require typed nonsecret environment inputs at helper construction.
- Reject non-normal credential paths and multiline service arguments.
Diffstat:
10 files changed, 253 insertions(+), 37 deletions(-)
diff --git a/build/nix/library.nix b/build/nix/library.nix
@@ -0,0 +1,53 @@
+{
+ lib,
+ pkgs,
+ version,
+}:
+let
+ root = ../..;
+ releaseSource = lib.fileset.toSource {
+ inherit root;
+ fileset = lib.fileset.unions [
+ ../../Cargo.toml
+ ../../Cargo.lock
+ ../../CHANGELOG.md
+ ../../LICENSE-APACHE
+ ../../LICENSE-MIT
+ ../../README.md
+ ../../contracts
+ ../../crates
+ ];
+ };
+ package = pkgs.runCommand "radroots-lib-release-bundle-${version}" { } ''
+ install -d "$out/share/radroots-lib"
+ cp -R ${releaseSource}/. "$out/share/radroots-lib/"
+ cat > "$out/share/radroots-lib/release-bundle.json" <<EOF
+ {"artifact":"public_library_workspace_release_bundle","package":"radroots","version":"${version}"}
+ EOF
+ '';
+ inspector = pkgs.writeShellApplication {
+ name = "radroots-lib-bundle-inspect";
+ runtimeInputs = [ pkgs.coreutils ];
+ text = ''
+ set -euo pipefail
+ test -f ${package}/share/radroots-lib/Cargo.toml
+ test -f ${package}/share/radroots-lib/Cargo.lock
+ exec cat ${package}/share/radroots-lib/release-bundle.json
+ '';
+ };
+in
+{
+ inherit package;
+ app = {
+ type = "app";
+ program = "${inspector}/bin/radroots-lib-bundle-inspect";
+ meta.description = "Inspect the installed Radroots Lib release bundle";
+ };
+ check = pkgs.runCommand "radroots-lib-release-bundle-check" { } ''
+ test -f ${package}/share/radroots-lib/Cargo.toml
+ test -f ${package}/share/radroots-lib/Cargo.lock
+ test -f ${package}/share/radroots-lib/release-bundle.json
+ test ! -e ${package}/share/radroots-lib/build/nix/service/fixture-service
+ touch "$out"
+ '';
+}
diff --git a/build/nix/service/apps.nix b/build/nix/service/apps.nix
@@ -20,6 +20,7 @@ assert lib.assertMsg (
assert lib.assertMsg (lib.isDerivation toolchain) "toolchain must be a derivation";
assert lib.assertMsg (
builtins.isAttrs nativeInputs
+ && (nativeInputs.schema or null) == "radroots.service.native-inputs.v1"
&& builtins.isList (nativeInputs.nativeBuildInputs or null)
&& builtins.isList (nativeInputs.buildInputs or null)
&& builtins.isAttrs (nativeInputs.environment or null)
diff --git a/build/nix/service/checks.nix b/build/nix/service/checks.nix
@@ -21,6 +21,7 @@ assert lib.assertMsg (builtins.pathExists source) "source must exist";
assert lib.assertMsg (builtins.pathExists cargoLock) "cargoLock must exist";
assert lib.assertMsg (
builtins.isAttrs nativeInputs
+ && (nativeInputs.schema or null) == "radroots.service.native-inputs.v1"
&& builtins.isList (nativeInputs.nativeBuildInputs or null)
&& builtins.isList (nativeInputs.buildInputs or null)
&& builtins.isAttrs (nativeInputs.environment or null)
diff --git a/build/nix/service/compose.nix b/build/nix/service/compose.nix
@@ -14,6 +14,7 @@ assert lib.assertMsg (
assert lib.assertMsg (lib.isDerivation package) "package must be a derivation";
assert lib.assertMsg (
builtins.isAttrs nativeInputs
+ && (nativeInputs.schema or null) == "radroots.service.native-inputs.v1"
&& builtins.isList (nativeInputs.nativeBuildInputs or null)
&& builtins.isList (nativeInputs.buildInputs or null)
&& builtins.isAttrs (nativeInputs.environment or null)
diff --git a/build/nix/service/devshell.nix b/build/nix/service/devshell.nix
@@ -13,6 +13,7 @@ assert lib.assertMsg (
assert lib.assertMsg (lib.isDerivation toolchain) "toolchain must be a derivation";
assert lib.assertMsg (
builtins.isAttrs nativeInputs
+ && (nativeInputs.schema or null) == "radroots.service.native-inputs.v1"
&& builtins.isList (nativeInputs.nativeBuildInputs or null)
&& builtins.isList (nativeInputs.buildInputs or null)
&& builtins.isAttrs (nativeInputs.environment or null)
diff --git a/build/nix/service/fixture.nix b/build/nix/service/fixture.nix
@@ -9,7 +9,10 @@ let
nativeInputs = service.mkNativeInputs {
nativeBuildInputs = [ pkgs.coreutils ];
environment = {
- RADROOTS_SERVICE_FIXTURE = "1";
+ RADROOTS_SERVICE_FIXTURE = {
+ classification = "nonsecret";
+ value = "1";
+ };
};
};
fixtureSource = ./fixture-service;
@@ -351,6 +354,42 @@ let
nativeInputs = { };
}).nativeInputs
);
+ invalidNativeInputDefinitions =
+ map
+ (
+ environment:
+ builtins.tryEval (builtins.deepSeq (service.mkNativeInputs { inherit environment; }) true)
+ )
+ [
+ {
+ LEGACY_VALUE = "untyped";
+ }
+ {
+ CLASSIFIED_SECRET = {
+ classification = "secret";
+ value = "redacted";
+ };
+ }
+ {
+ EXTRA_FIELD = {
+ classification = "nonsecret";
+ value = "value";
+ unexpected = true;
+ };
+ }
+ {
+ API_TOKEN = {
+ classification = "nonsecret";
+ value = "redacted";
+ };
+ }
+ {
+ OVERLONG_VALUE = {
+ classification = "nonsecret";
+ value = lib.concatStrings (lib.replicate 4097 "a");
+ };
+ }
+ ];
invalidServicePackage = builtins.tryEval (
(service.mkServicePackage {
inherit nativeInputs toolchain;
@@ -384,7 +423,10 @@ let
cargoLock = fixtureSource + "/Cargo.lock";
servicePackage = "fixture-service";
nativeInputs = service.mkNativeInputs {
- environment.CARGO_PROFILE = "dev";
+ environment.CARGO_PROFILE = {
+ classification = "nonsecret";
+ value = "dev";
+ };
};
}).outPath
);
@@ -439,7 +481,10 @@ let
checkArgs
// {
nativeInputs = service.mkNativeInputs {
- environment.${variable} = "override";
+ environment.${variable} = {
+ classification = "nonsecret";
+ value = "override";
+ };
};
}
)).check.outPath
@@ -506,7 +551,12 @@ let
(service.mkServiceApps (
appArgs
// {
- nativeInputs = service.mkNativeInputs { environment."INVALID-NAME" = "value"; };
+ nativeInputs = service.mkNativeInputs {
+ environment."INVALID-NAME" = {
+ classification = "nonsecret";
+ value = "value";
+ };
+ };
}
)).default.program
))
@@ -514,7 +564,12 @@ let
(service.mkServiceApps (
appArgs
// {
- nativeInputs = service.mkNativeInputs { environment.PATH = "/tmp"; };
+ nativeInputs = service.mkNativeInputs {
+ environment.PATH = {
+ classification = "nonsecret";
+ value = "/tmp";
+ };
+ };
}
)).default.program
))
@@ -554,7 +609,12 @@ let
(service.mkServiceDevShell (
devShellArgs
// {
- nativeInputs = service.mkNativeInputs { environment."INVALID-NAME" = "value"; };
+ nativeInputs = service.mkNativeInputs {
+ environment."INVALID-NAME" = {
+ classification = "nonsecret";
+ value = "value";
+ };
+ };
}
)).drvPath
))
@@ -562,7 +622,12 @@ let
(service.mkServiceDevShell (
devShellArgs
// {
- nativeInputs = service.mkNativeInputs { environment.RUSTC = "/tmp/rustc"; };
+ nativeInputs = service.mkNativeInputs {
+ environment.RUSTC = {
+ classification = "nonsecret";
+ value = "/tmp/rustc";
+ };
+ };
}
)).drvPath
))
@@ -853,6 +918,36 @@ let
(
baseNixosModuleConfiguration
// {
+ instances.primary.credentials.token = "/run//operator/token";
+ }
+ )
+ (
+ baseNixosModuleConfiguration
+ // {
+ instances.primary.credentials.token = "/run/operator/./token";
+ }
+ )
+ (
+ baseNixosModuleConfiguration
+ // {
+ instances.primary.credentials.token = "/run/operator/../token";
+ }
+ )
+ (
+ baseNixosModuleConfiguration
+ // {
+ instances.primary.credentials.token = "/run/operator/token/";
+ }
+ )
+ (
+ baseNixosModuleConfiguration
+ // {
+ instances.primary.credentials.token = "/";
+ }
+ )
+ (
+ baseNixosModuleConfiguration
+ // {
instances.primary.credentials.token = "/${lib.concatStrings (lib.replicate 4096 "a")}";
}
)
@@ -898,6 +993,7 @@ let
(lib.replicate 65 "argument")
[ (lib.concatStrings (lib.replicate 4097 "a")) ]
[ "argument\nvalue" ]
+ [ "argument\rvalue" ]
];
maximumNixosModule = service.mkServiceNixosModule (
nixosModuleArguments
@@ -956,6 +1052,11 @@ assert
assert nativeInputs.nativeBuildInputs == [ pkgs.coreutils ];
assert nativeInputs.buildInputs == [ ];
assert nativeInputs.environment.RADROOTS_SERVICE_FIXTURE == "1";
+assert
+ nativeInputs.environmentContract.RADROOTS_SERVICE_FIXTURE == {
+ classification = "nonsecret";
+ value = "1";
+ };
assert outputs.serviceName == "fixture_service";
assert outputs.packages.default == package;
assert (pkgs.stdenv.isLinux -> outputs.packages.oci == ociImage);
@@ -1010,6 +1111,7 @@ assert invalidName.success == false;
assert defaultOverride.success == false;
assert invalidPackage.success == false;
assert invalidNativeInputs.success == false;
+assert lib.all (result: result.success == false) invalidNativeInputDefinitions;
assert invalidServicePackage.success == false;
assert invalidBinaryName.success == false;
assert invalidReleaseProfile.success == false;
diff --git a/build/nix/service/native-inputs.nix b/build/nix/service/native-inputs.nix
@@ -7,8 +7,28 @@
assert lib.assertMsg (builtins.isList nativeBuildInputs) "nativeBuildInputs must be a list";
assert lib.assertMsg (builtins.isList buildInputs) "buildInputs must be a list";
assert lib.assertMsg (builtins.isAttrs environment) "environment must be an attribute set";
+assert lib.assertMsg (lib.all (name: builtins.match "^[A-Za-z_][A-Za-z0-9_]*$" name != null) (
+ builtins.attrNames environment
+)) "environment names must be shell identifiers";
+assert lib.assertMsg (lib.all (
+ name: builtins.match ".*(CREDENTIAL|PASSWORD|PRIVATE_KEY|SECRET|TOKEN).*" (lib.toUpper name) == null
+) (builtins.attrNames environment)) "environment names must not identify secret material";
+assert lib.assertMsg (lib.all (
+ entry:
+ builtins.isAttrs entry
+ &&
+ builtins.attrNames entry == [
+ "classification"
+ "value"
+ ]
+ && entry.classification == "nonsecret"
+ && builtins.isString entry.value
+ && builtins.stringLength entry.value <= 4096
+) (builtins.attrValues environment)) "environment values must be bounded typed nonsecret values";
{
+ schema = "radroots.service.native-inputs.v1";
nativeBuildInputs = lib.unique nativeBuildInputs;
buildInputs = lib.unique buildInputs;
- inherit environment;
+ environment = lib.mapAttrs (_: entry: entry.value) environment;
+ environmentContract = environment;
}
diff --git a/build/nix/service/nixos-module.nix b/build/nix/service/nixos-module.nix
@@ -67,12 +67,20 @@ let
builtins.stringLength name <= 128 && builtins.match "^[A-Za-z0-9][A-Za-z0-9_.-]*$" name != null;
validCredentialPath =
path:
+ let
+ segments = if builtins.isString path then lib.splitString "/" path else [ ];
+ in
builtins.isString path
+ && builtins.stringLength path > 1
&& builtins.stringLength path <= 4096
&& lib.hasPrefix "/" path
+ && lib.last segments != ""
+ && lib.all (segment: segment != "" && segment != "." && segment != "..") (lib.drop 1 segments)
&& path != "/nix/store"
&& !(lib.hasPrefix "/nix/store/" path)
- && builtins.match "^[^:\n]+$" path != null;
+ && !(lib.hasInfix ":" path)
+ && !(lib.hasInfix "\r" path)
+ && !(lib.hasInfix "\n" path);
validCredentials =
instance:
builtins.length (builtins.attrNames instance.credentials) <= 32
@@ -91,7 +99,8 @@ let
argument:
builtins.isString argument
&& builtins.stringLength argument <= 4096
- && builtins.match "^[^\n]*$" argument != null
+ && !(lib.hasInfix "\r" argument)
+ && !(lib.hasInfix "\n" argument)
) command;
package = cfg.package;
assertions = [
diff --git a/build/nix/service/package.nix b/build/nix/service/package.nix
@@ -27,6 +27,7 @@ assert lib.assertMsg (
) "releaseProfile must be release or a release-prefixed Cargo profile";
assert lib.assertMsg (
builtins.isAttrs nativeInputs
+ && (nativeInputs.schema or null) == "radroots.service.native-inputs.v1"
&& builtins.isList (nativeInputs.nativeBuildInputs or null)
&& builtins.isList (nativeInputs.buildInputs or null)
&& builtins.isAttrs (nativeInputs.environment or null)
diff --git a/flake.nix b/flake.nix
@@ -25,14 +25,33 @@
imports = [ inputs.treefmt-nix.flakeModule ];
systems = import ./build/nix/service/systems.nix;
- flake.nixosModules.default =
- (import ./build/nix/service/nixos-module.nix { lib = inputs.nixpkgs.lib; })
- {
- serviceName = "fixture_service";
- binaryName = "fixture-service";
- packageFor = pkgs: self.packages.${pkgs.stdenv.hostPlatform.system}.default;
- commandForInstance = _: [ "--help" ];
+ flake.lib = {
+ supportedSystems = import ./build/nix/service/systems.nix;
+ mkServiceHelpers =
+ system:
+ assert inputs.nixpkgs.lib.assertMsg (builtins.elem system (
+ import ./build/nix/service/systems.nix
+ )) "service helpers support only the governed Nix systems";
+ let
+ pkgs = import inputs.nixpkgs {
+ inherit system;
+ overlays = [ inputs.rust-overlay.overlays.default ];
+ };
+ in
+ import ./build/nix/service {
+ crane = inputs.crane;
+ lib = inputs.nixpkgs.lib;
+ inherit pkgs;
};
+ };
+
+ flake.overlays.default = final: _previous: {
+ radroots-lib =
+ assert inputs.nixpkgs.lib.assertMsg
+ (builtins.elem final.stdenv.hostPlatform.system self.lib.supportedSystems)
+ "the Radroots Lib overlay supports only the governed Nix systems";
+ self.packages.${final.stdenv.hostPlatform.system}.default;
+ };
perSystem =
{
@@ -62,43 +81,51 @@
crane = inputs.crane;
inherit lib pkgs toolchains;
};
+ library = import ./build/nix/library.nix {
+ inherit lib pkgs;
+ inherit (common) version;
+ };
serviceFixture = import ./build/nix/service/fixture.nix {
inherit lib pkgs service;
nixosSystem = inputs.nixpkgs.lib.nixosSystem;
toolchain = toolchains.stable;
};
+ fixtureChecks = lib.mapAttrs' (
+ name: value: lib.nameValuePair "service-fixture-${name}" value
+ ) serviceFixture.outputs.checks;
in
{
treefmt = import ./treefmt.nix;
- apps =
- (import ./build/nix/apps.nix {
- inherit
- common
- config
- lib
- pkgs
- toolchains
- ;
- })
- // serviceFixture.outputs.apps;
+ apps = {
+ default = library.app;
+ }
+ // (import ./build/nix/apps.nix {
+ inherit
+ common
+ config
+ lib
+ pkgs
+ toolchains
+ ;
+ });
checks = lib.filterAttrs (_: value: value != null) (
(import ./build/nix/checks.nix {
inherit common pkgs;
})
- // serviceFixture.outputs.checks
+ // fixtureChecks
+ // {
+ release-bundle = library.check;
+ }
);
- devShells =
- (import ./build/nix/devshells.nix {
- inherit common pkgs toolchains;
- })
- // {
- service-fixture = serviceFixture.outputs.devShells.default;
- };
+ devShells = import ./build/nix/devshells.nix {
+ inherit common pkgs toolchains;
+ };
- packages = serviceFixture.outputs.packages // {
+ packages = {
+ default = library.package;
xtask = common.xtaskPackage;
};
};