Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/libstore/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <curl/curl.h>
#include <nlohmann/json.hpp>

#include <limits.h>

#ifndef _WIN32
# include <sys/utsname.h>
#endif
Expand Down Expand Up @@ -267,6 +269,20 @@ const ExternalBuilder * Settings::findExternalDerivationBuilderIfSupported(const
return nullptr;
}

std::optional<std::string> Settings::getHostName()
{
if (hostName != "")
return hostName;

#ifndef _WIN32
char hostname[_POSIX_HOST_NAME_MAX + 1];
if (gethostname(hostname, sizeof(hostname)) == 0)
return std::string(hostname);
#endif

return std::nullopt;
}

std::string nixVersion = PACKAGE_VERSION;

const std::string determinateNixVersion = DETERMINATE_NIX_VERSION;
Expand Down
10 changes: 10 additions & 0 deletions src/libstore/include/nix/store/globals.hh
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,16 @@ public:
* derivation, or else returns a null pointer.
*/
const ExternalBuilder * findExternalDerivationBuilderIfSupported(const Derivation & drv);

Setting<std::string> hostName{
this,
"",
"host-name",
R"(
The name of this host for recording build provenance. If unset, the Unix host name is used.
)"};

std::optional<std::string> getHostName();
};

// FIXME: don't use a global variable.
Expand Down
12 changes: 11 additions & 1 deletion src/libstore/include/nix/store/provenance.hh
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@ struct BuildProvenance : Provenance
*/
OutputName output;

/**
* The hostname of the machine on which the derivation was built, if known.
*/
std::optional<std::string> buildHost;

/**
* The provenance of the derivation, if known.
*/
std::shared_ptr<const Provenance> next;

// FIXME: do we need anything extra for CA derivations?

BuildProvenance(const StorePath & drvPath, const OutputName & output, std::shared_ptr<const Provenance> next)
BuildProvenance(
const StorePath & drvPath,
const OutputName & output,
std::optional<std::string> buildHost,
std::shared_ptr<const Provenance> next)
: drvPath(drvPath)
, output(output)
, buildHost(std::move(buildHost))
, next(std::move(next))
{
}
Expand Down
9 changes: 7 additions & 2 deletions src/libstore/provenance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ nlohmann::json BuildProvenance::to_json() const
{"type", "build"},
{"drv", drvPath.to_string()},
{"output", output},
{"buildHost", buildHost},
{"next", next ? next->to_json() : nlohmann::json(nullptr)},
};
}
Expand All @@ -18,8 +19,12 @@ Provenance::Register registerBuildProvenance("build", [](nlohmann::json json) {
std::shared_ptr<const Provenance> next;
if (auto p = optionalValueAt(obj, "next"); p && !p->is_null())
next = Provenance::from_json(*p);
return make_ref<BuildProvenance>(
StorePath(getString(valueAt(obj, "drv"))), getString(valueAt(obj, "output")), next);
std::optional<std::string> buildHost;
if (auto p = optionalValueAt(obj, "buildHost"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would also be a good idea to include the native architecture, if that's easy to get...

Copy link
Collaborator Author

@edolstra edolstra Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to that, we should probably record the system attribute of the derivation, so we can query what system a store path is for. Currently there is no way to get that info unless you have the deriver around.

buildHost = p->get<std::optional<std::string>>();
auto buildProv = make_ref<BuildProvenance>(
StorePath(getString(valueAt(obj, "drv"))), getString(valueAt(obj, "output")), buildHost, next);
return buildProv;
});

nlohmann::json CopiedProvenance::to_json() const
Expand Down
3 changes: 2 additions & 1 deletion src/libstore/unix/build/derivation-builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1868,7 +1868,8 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
newInfo.deriver = drvPath;
newInfo.ultimate = true;
if (drvProvenance)
newInfo.provenance = std::make_shared<const BuildProvenance>(drvPath, outputName, drvProvenance);
newInfo.provenance =
std::make_shared<const BuildProvenance>(drvPath, outputName, settings.getHostName(), drvProvenance);
store.signPathInfo(newInfo);

finish(newInfo.path);
Expand Down
2 changes: 1 addition & 1 deletion src/nix/provenance-show.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ R""(
# nix provenance show /run/current-system
/nix/store/k145bdxhdb89i4fkvgdisdz1yh2wiymm-nixos-system-machine-25.05.20251210.d2b1213
← copied from cache.flakehub.com
← built from derivation /nix/store/w3p3xkminq61hs00kihd34w1dglpj5s9-nixos-system-machine-25.05.20251210.d2b1213.drv (output out)
← built from derivation /nix/store/w3p3xkminq61hs00kihd34w1dglpj5s9-nixos-system-machine-25.05.20251210.d2b1213.drv (output out) on build-machine
← instantiated from flake output github:my-org/my-repo/6b03eb949597fe96d536e956a2c14da9901dbd21?dir=machine#nixosConfigurations.machine.config.system.build.toplevel
```

Expand Down
6 changes: 4 additions & 2 deletions src/nix/provenance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ struct CmdProvenanceShow : StorePathsCommand
provenance = copied->next;
} else if (auto build = std::dynamic_pointer_cast<const BuildProvenance>(provenance)) {
logger->cout(
"← built from derivation " ANSI_BOLD "%s" ANSI_NORMAL " (output " ANSI_BOLD "%s" ANSI_NORMAL ")",
"← built from derivation " ANSI_BOLD "%s" ANSI_NORMAL " (output " ANSI_BOLD "%s" ANSI_NORMAL
") on " ANSI_BOLD "%s" ANSI_NORMAL,
store.printStorePath(build->drvPath),
build->output);
build->output,
build->buildHost.value_or("unknown host").c_str());
provenance = build->next;
} else if (auto flake = std::dynamic_pointer_cast<const FlakeProvenance>(provenance)) {
// Collapse subpath/tree provenance into the flake provenance for legibility.
Expand Down
1 change: 1 addition & 0 deletions tests/functional/common/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ gc-reserved-space = 0
substituters =
flake-registry = $TEST_ROOT/registry.json
show-trace = true
host-name = test-host
include nix.conf.extra
trusted-users = $(whoami)
${_NIX_TEST_EXTRA_CONFIG:-}
Expand Down
4 changes: 3 additions & 1 deletion tests/functional/flakes/provenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ builder=$(nix eval --raw "$flake1Dir#packages.$system.default._builder")
# Building a derivation should have tree+subpath+flake+build provenance.
[[ $(nix path-info --json --json-format 1 "$outPath" | jq ".\"$outPath\".provenance") = $(cat <<EOF
{
"buildHost": "test-host",
"drv": "$(basename "$drvPath")",
"next": {
"flakeOutput": "packages.$system.default",
Expand Down Expand Up @@ -92,6 +93,7 @@ nix copy --from "file://$binaryCache" "$outPath" --no-check-sigs
{
"from": "file://$binaryCache",
"next": {
"buildHost": "test-host",
"drv": "$(basename "$drvPath")",
"next": {
"flakeOutput": "packages.$system.default",
Expand Down Expand Up @@ -124,7 +126,7 @@ EOF
[[ $(nix provenance show "$outPath") = $(cat <<EOF
$outPath
← copied from file://$binaryCache
← built from derivation $drvPath (output out)
← built from derivation $drvPath (output out) on test-host
← instantiated from flake output git+file://$flake1Dir?ref=refs/heads/master&rev=$rev#packages.$system.default
EOF
) ]]
Expand Down