| name | stampreport |
| description | Report VCS stamping status of running Go processes on this machine using gops and go version -m |
| allowed-tools | Bash Read |
VCS Stamp Report
Scan all running Go processes and report whether they have VCS buildinfo stamped.
Steps
- List running Go processes using
gops. On NixOS, tools aren't pre-installed — use nix run:
sudo gops
ssh <host> 'sudo nix run nixpkgs#gops'
ssh root@<host> 'nix run nixpkgs#gops'
ssh <host> 'nix run nixpkgs#gops'
- For each unique binary path in the output, inspect its build metadata:
go version -m /path/to/binary
ssh <host> 'nix run nixpkgs#go -- version -m /path/to/binary'
Look for vcs.revision and vcs.modified in the build settings.
Classifying binaries
Classify each binary by ownership — this determines what can be fixed:
- Custom Nix packages (fixable): store path contains
-unstable or matches
known repos (github.com/stapelberg/, github.com/robustirc/, github.com/Debian/dcs, etc.)
- Upstream Nix packages (not fixable here): everything else from
/nix/store/
(tailscale, node_exporter, prometheus, docker, grafana, caddy, etc.)
go install binaries (different fix): from ~/go/bin/ — these need to be
rebuilt from a clean git checkout or converted to Nix packages
Interpreting the output
- No
vcs.revision setting: Binary is NOT stamped.
vcs.revision = 40-char hex: Binary IS stamped with a git revision.
vcs.modified = true: Binary was built from a dirty working directory or
patched source.
mod version = (devel): Binary was built from a local directory or Nix
source without module version info.
How the goVcsStamping overlay works
The overlay at https://github.com/stapelberg/nix (go-vcs-stamping.nix) wraps
buildGoModule and buildGoLatestModule. When the src attribute has .rev
(or .dirtyRev) and .lastModified, the overlay:
- Creates a fake
.git/HEAD if no .git/ directory exists
- Puts a fake
git wrapper on PATH that answers Go's two VCS queries
- Adds
-buildvcs=true to GOFLAGS
The overlay does NOT need gitMinimal in nativeBuildInputs — the fake git
wrapper handles everything. gitMinimal is only useful for the optional
verification path (when source has a real .git/ directory, the overlay verifies
metadata matches before falling through to the fake wrapper).
Diagnosing unstamped Nix binaries
The most common root cause is that the flake input doesn't provide .rev metadata.
Check the flake.lock to confirm:
cd ~/machines/<host>
nix flake metadata --json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for name, node in data.get('locks',{}).get('nodes',{}).items():
locked = node.get('locked',{})
rev = locked.get('rev', locked.get('dirtyRev', ''))
typ = locked.get('type', '')
if typ in ('path','git','github'):
status = rev[:12] if rev else 'NO REV'
print(f'{name}: {status} (type={typ})')
"
If an input shows type=path with NO REV — that's the problem.
Fixing unstamped Nix binaries
Fix 1: Change path: to git+file:// (most common)
Plain path: inputs don't reliably provide .rev/.dirtyRev metadata.
Change to git+file:// so the git InputScheme provides it:
# Before (no .rev in flake.lock):
mypackage = { url = "/home/michael/mypackage"; flake = false; };
# After (.rev or .dirtyRev in flake.lock):
mypackage = { url = "git+file:///home/michael/mypackage"; flake = false; };
Warning: git+file:// only includes git-tracked files. If the project has
generated/untracked assets needed at runtime (e.g. minified CSS, built frontend),
keep path: and instead add nativeBuildInputs = [ pkgs.gitMinimal ]; to the
-pkg.nix file. This lets Go's native VCS detection read the .git/ directory
that path: inputs preserve. The overlay won't help here (no .rev), but Go
handles it directly.
Fix 2: Ensure goVcsStamping overlay is active
The overlay must be in nixpkgs.overlays on the target machine:
nixpkgs.overlays = [ stapelbergnix.overlays.goVcsStamping ];
If using pkgs-unstable for buildGoLatestModule, ensure the overlay is also
applied there:
pkgs-unstable = import nixpkgs-unstable {
inherit system;
overlays = [ stapelbergnix.overlays.goVcsStamping ];
};
Fix 3: Patched sources need explicit vcsMetadata
For sources built via runCommand (patching, merging repos), the overlay can't
auto-detect metadata from src. Pass it explicitly:
pkgs.buildGoModule {
src = patchedSrc;
vcsMetadata = { inherit (originalInput) rev lastModified; };
...
};
Fix 4: Ensure stapelbergnix is up to date
The overlay gained dirtyRev support in commit ae841f6. If inputs use
git+file:// with dirty working directories, the overlay version must include
this commit. Check with:
cd ~/machines/<host>
nix flake metadata --json | python3 -c "
import json,sys
rev = json.load(sys.stdin)['locks']['nodes']['stapelbergnix']['locked']['rev']
print(f'stapelbergnix: {rev[:12]}')
"
Update with nix flake update stapelbergnix if needed.
Gotcha: Dirty git+file:// inputs prevent Nix from writing the lock file
entirely. If nix flake update warns "not writing lock file because it has an
unlocked input", you must either clean the dirty working directory or manually
edit flake.lock to update the stapelbergnix entry.
Present results
Produce a summary table of all Go binaries found, showing: binary name, module
path, version, VCS revision (first 12 chars), and whether modified. Classify
each as custom/upstream/go-install. End with a count of stamped vs unstamped,
broken down by category.
See https://michael.stapelberg.ch/posts/2026-04-05-stamp-it-all-programs-must-report-their-version/ for all the details!