clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

Publish a forge name and a made-up machine, not the host #9

12 files changed, +122 -17
MREADME.md12++++++-
Mbin/forge53+++++++++++++++++++++++++++---
Mguide/index.html2+-
Mlib/forge.ts4++
Mlib/insights.ts2+-
Mlib/render.ts4+-
Msite/_includes/insights-body.njk2+-
Msite/repo/commit.njk2+-
Msite/repo/releases.njk2+-
Msite/repo/sites.njk2+-
Atest/scenarios/publish-who.sh49++++++++++++++++++++++++++++
Mtest/scenarios/release-page.sh5++-
M README.md +10 -2
@@ -361,7 +361,7 @@ script .forge/ci 99a0d5e...
361361 result pass362362 exit 0363363 duration 41s364-runner dami@mbp364+runner cat@early-yak-00365365 system Darwin 25.5.0 arm64366366 env host367367 log sha256:... 120 lines
@@ -369,6 +369,14 @@ log sha256:... 120 lines
369369 -----BEGIN SSH SIGNATURE-----370370 ```371371 372+`runner` is your forge name and a made-up name for the machine:373+`git config forge.user`, else the name `allowed_signers` gives your signing374+key; and `git config forge.machine`, else two words and two hex digits375+picked once from a hash of the hostname and a random salt, then kept in your376+global git config. Never the hostname or your local account, since the377+statement is published; site deploys and releases name their builder the378+same way (`Built-By`). Pages don't show either: they show who signed.379+372380 The statement is appended to `refs/notes/ci`, and `forge push` merges and373381 pushes those notes with the branch. Results are keyed by **tree**, so a374382 reworded or reordered commit with an identical tree keeps its result without
@@ -680,7 +688,7 @@ Tables: `events` (pushes, comments, approvals, builds), `builds` and
680688 `build_repos` (each line of `metrics.jsonl`), `reviews` (each line of a681689 review log: created, revisions, approvals, deleted), `review_ends` (landed682690 or closed, and when) and `ci` (each attestation: commit, tree, result,683-duration, runner, signer). A review counts as landed when its branch is691+duration, runner, signer; `ci.parquet` leaves the runner out). A review counts as landed when its branch is684692 deleted with its tip in the default branch, as `forge land` leaves it; one685693 whose tip reaches the default branch while the branch stays counts from686694 when the lake saw it.
M bin/forge +48 -5
@@ -149,6 +149,49 @@ stack_base() {
149149 done150150 }151151 152+# whoami_at: who made an attestation, site deploy or release, and where, as153+# the lines publish it: <forge user>@<machine>. The forge user is154+# git config forge.user, else the name allowed_signers gives the signing155+# key (forge.allowedsigners), else unknown; never the local account name.156+# The machine is git config forge.machine, which is a made-up name the157+# first time: two words and two hex digits picked by a hash of the158+# hostname and a random salt, stored in the global git config, so it stays159+# the same on this machine and says nothing about it. Pages show neither:160+# who signed is what they show.161+whoami_at() { printf '%s@%s' "$(forge_user)" "$(machine)"; }162+163+forge_user() {164+ u=$(git config --get forge.user 2>/dev/null || true)165+ if [ -z "$u" ]; then166+ k=$(signing_key 2>/dev/null || true)167+ case $k in *.pub) ;; *) [ -f "$k.pub" ] && k=$k.pub ;; esac168+ s=$(git config --get forge.allowedsigners 2>/dev/null || true)169+ case $s in "~/"*) s=$HOME/${s#"~/"} ;; esac170+ if [ -f "$k" ] && [ -f "$s" ]; then171+ blob=$(awk '{ print $2; exit }' "$k")172+ [ -z "$blob" ] || u=$(awk -v b="$blob" '{ for (i = 2; i <= NF; i++) if ($i == b) { split($1, p, ","); print p[1]; exit } }' "$s")173+ fi174+ fi175+ printf '%s' "${u:-unknown}"176+}177+178+machine() {179+ m=$(git config --get forge.machine 2>/dev/null || true)180+ if [ -z "$m" ]; then181+ h=$( (hostname 2>/dev/null; od -An -N16 -tx1 /dev/urandom) | { shasum -a 256 2>/dev/null || sha256sum; } | cut -c1-6)182+ m=$(printf '%s\n' "$h" | awk '{183+ a = "amber brisk calm deft eager fair gentle hazel ivory jolly keen lucid mellow nimble olive plain quiet rosy sage tidy umber vivid wry young zesty bold clear dusky early fern grand humble"184+ n = "heron otter finch badger lark marten wren stoat ibis vole puffin tern egret hare lynx newt plover raven robin seal shrike swift teal thrush viper whelk yak zebu crane dove kestrel moth"185+ split(a, A, " "); split(n, N, " ")186+ x = index("0123456789abcdef", substr($0, 1, 1)) * 16 + index("0123456789abcdef", substr($0, 2, 1))187+ y = index("0123456789abcdef", substr($0, 3, 1)) * 16 + index("0123456789abcdef", substr($0, 4, 1))188+ printf "%s-%s-%s", A[x % 32 + 1], N[y % 32 + 1], substr($0, 5, 2)189+ }')190+ git config --global forge.machine "$m" 2>/dev/null || true191+ fi192+ printf '%s' "$m"193+}194+152195 signing_key() {153196 k=${FORGE_SIGNING_KEY:-$(git config --get forge.signingkey || git config --get user.signingkey || true)}154197 if [ -z "$k" ]; then
@@ -262,7 +305,7 @@ attest_one() {
262305 echo "exit $code"263306 echo "started $started"264307 echo "duration ${dur}s"265- echo "runner $(id -un)@$(hostname -s 2>/dev/null || hostname)"308+ echo "runner $(whoami_at)"266309 echo "system $(uname -srm)"267310 echo "env $envdesc"268311 echo "log sha256:$(sha256 "$tmp/log") $(wc -l <"$tmp/log" | tr -d ' ') lines"
@@ -454,8 +497,8 @@ publish_dir() {
454497 say "site $name: unchanged"455498 return 0456499 fi457- msg=$(printf 'site %s: %s\n\nSource-Commit: %s\nSource-Ref: %s\nBuild: %s\nBuilt-By: %s@%s\n' \458- "$name" "$(git log -1 --format=%s "$src" 2>/dev/null || echo "$dir")" "$src" "$br" "$build" "$(id -un)" "$(hostname -s 2>/dev/null || hostname)")500+ msg=$(printf 'site %s: %s\n\nSource-Commit: %s\nSource-Ref: %s\nBuild: %s\nBuilt-By: %s\n' \501+ "$name" "$(git log -1 --format=%s "$src" 2>/dev/null || echo "$dir")" "$src" "$br" "$build" "$(whoami_at)")459502 # Signed with the key that signs CI results, so the forge can show who460503 # deployed it.461504 commit=
@@ -584,8 +627,8 @@ release_publish() {
584627 for f in SHA256SUMS SHA256SUMS.sig; do printf '100644 blob %s\t%s\n' "$(git hash-object -w -- "$tmp/$f")" "$f" >>"$tmp/tree"; done585628 tree=$(git mktree <"$tmp/tree")586629 rm -rf "$tmp"587- msg=$(printf 'release %s: %s\n\nTag: %s\nSource-Commit: %s\nBuilt-By: %s@%s\n' \588- "$tag" "$(git log -1 --format=%s "$src")" "$tag" "$src" "$(id -un)" "$(hostname -s 2>/dev/null || hostname)")630+ msg=$(printf 'release %s: %s\n\nTag: %s\nSource-Commit: %s\nBuilt-By: %s\n' \631+ "$tag" "$(git log -1 --format=%s "$src")" "$tag" "$src" "$(whoami_at)")589632 commit=$(printf '%s\n' "$msg" | git -c gpg.format=ssh -c "user.signingkey=$key" commit-tree -S "$tree" 2>/dev/null) ||590633 commit=$(printf '%s\n' "$msg" | git commit-tree "$tree")591634 r=$(remote)
M guide/index.html +1 -1
M lib/forge.ts +4 -0
M lib/insights.ts +1 -1
M lib/render.ts +2 -2
M site/_includes/insights-body.njk +1 -1
M site/repo/commit.njk +1 -1
M site/repo/releases.njk +1 -1
M site/repo/sites.njk +1 -1
A test/scenarios/publish-who.sh +49 -0
@@ -0,0 +1,49 @@
1+# What attestations, site deploys and releases publish about who made them:2+# the forge name the signing key has in allowed_signers (or forge.user) and3+# a made-up machine name kept in the global git config (or forge.machine);4+# never the hostname or the local account. Pages show neither, only who5+# signed.6+. "$(dirname "$0")/../lib.sh"7+user dami8+repo dami/greet9+clone dami dami/greet "$T/w/d"10+mkdir -p "$T/w/d/.forge"11+printf '#!/bin/sh\ntrue\n' >"$T/w/d/.forge/ci"12+chmod +x "$T/w/d/.forge/ci"13+at "$T/w/d" commit "first"14+export FORGE_USER=dami15+host=$(hostname -s 2>/dev/null || hostname)16+me=$(id -un)17+runner() { git -C "$T/w/d" notes --ref=ci show "$1" | sed -n 's/^runner //p' | tail -n 1; }18+19+ok at "$T/w/d" "$FORGE" attest20+r=$(runner HEAD)21+case $r in dami@[a-z]*-[a-z]*-[0-9a-f][0-9a-f]) ;; *) fail "runner is '$r'" ;; esac22+case $r in *"$host"* | "$me"@*) [ "$me" = dami ] || fail "runner '$r' names the host or the account" ;; esac23+m=$(git config --global --get forge.machine)24+eq "dami@$m" "$r"25+# The same name next time.26+at "$T/w/d" commit "second"27+ok at "$T/w/d" "$FORGE" attest28+eq "$(runner HEAD)" "$r"29+# forge.user and forge.machine win.30+at "$T/w/d" git config forge.user someone31+at "$T/w/d" git config forge.machine lab-box32+at "$T/w/d" commit "third"33+ok at "$T/w/d" "$FORGE" attest34+eq "$(runner HEAD)" someone@lab-box35+at "$T/w/d" git config --unset forge.user36+at "$T/w/d" git config --unset forge.machine37+38+# A release names its builder the same way.39+at "$T/w/d" git tag v140+mkdir "$T/f" && printf 'x\n' >"$T/f/tool"41+ok push_as dami "$T/w/d" origin main refs/notes/ci42+ok at "$T/w/d" "$FORGE" release publish v1 "$T/f/tool"43+eq "$(git --git-dir="$FORGE_ROOT/dami/greet.git" log -1 --format='%(trailers:key=Built-By,valueonly)' refs/releases/v1 | tr -d '\n')" "$r"44+45+# Pages show who signed, not the runner or the builder.46+build dami/greet47+lacks "$FORGE_WWW/dami/greet/commit/$(git -C "$T/w/d" rev-parse HEAD~1)/index.html" "$m"48+has "$FORGE_WWW/dami/greet/commit/$(git -C "$T/w/d" rev-parse HEAD~1)/index.html" 'signed by dami'49+lacks "$FORGE_WWW/dami/greet/releases/index.html" "$m"
M test/scenarios/release-page.sh +3 -2