call it Hanzo Git, which is its name

"Forge" was a borrowed word for the thing that hosts the repos, schedules the
builds and issues the tokens. That thing is Hanzo Git. The word named nothing a
reader could go look at, and it had already drifted: the same paragraph could
say "the forge" and "git.hanzo.ai" and leave you to work out they were one
thing.

forge.go becomes git.go for the same reason — the file reads what Hanzo Git
knows that the run list does not.

Where a second mention would just repeat the name, it says "the server"
instead. Left alone: "unforgeable" and "cannot be forged", which are the other
word entirely and mean what they say.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-20 19:39:04 -07:00
co-authored by Hanzo Dev
parent 56dd04c706
commit 628fddb5d1
9 changed files with 30 additions and 30 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ runs:
- name: Fetch imgver
shell: bash
# The action ref is the script ref: an action pinned to @v1 runs v1's
# imgver. The forge first, because the forge is where this repo is written
# imgver. Hanzo Git first, because that is where this repo is written
# and GitHub is its mirror.
#
# `hanzo/ci` IS NOT THIS REPO. It is the tenant-org fork, and its v1 has no
+2 -2
View File
@@ -19,7 +19,7 @@ description: Publish a built static export to the Hanzo PaaS Sites plane.
# ONE credential, read from ONE place. The deploy key comes from KMS, which is
# where a secret lives: it rotates by resealing, and every site that publishes
# reads the same value the moment it changes. It was `secrets.HANZO_DEPLOY_TOKEN`
# on the forge — one copy per org that nobody can read back, so nobody could tell
# on Hanzo Git — one copy per org that nobody can read back, so nobody could tell
# it had stopped resolving, and cloud answered 403 on every call for the repos
# still carrying it while the one repo that had moved to KMS published fine.
#
@@ -62,7 +62,7 @@ runs:
- name: Fetch site
shell: bash
# The action ref is the script ref: an action pinned to @v1 runs v1's
# site. Both forges, because this repo is served from each.
# site. Both hosts, because this repo is served from each.
run: |
set -euo pipefail
ref="${GITHUB_ACTION_REF:-v1}"
+4 -4
View File
@@ -45,7 +45,7 @@ since has built.
Two readings the page depends on, both from a run's JOBS rather than its one
conclusion. A run that fails at `gate` built nothing; a run that fails at
`receipt` has already built, pinned and proved the release live. Both report
`failure`. And a commit the forge never constructed a run for is `absent`, not
`failure`. And a commit Hanzo Git never constructed a run for is `absent`, not
failed — there is no log to open, so it is drawn as a different shape.
It reads and never writes: no deploy, no retry, no promotion. Drive a sync at
@@ -55,7 +55,7 @@ cd.hanzo.ai.
`ci` reads the cluster through its own ServiceAccount — get and list on
workloads, nothing else, no stored credential — and reads `hanzo/universe`
through the forge token it already holds. In `charts/app/values/hanzo/ci.yaml`:
through the Hanzo Git token it already holds. In `charts/app/values/hanzo/ci.yaml`:
```yaml
rbac:
@@ -101,7 +101,7 @@ That's it. The build/test/publish logic lives here, once.
### `.hanzo/workflows/`, and nothing left in `.github/workflows/`
The forge collects workflows from the **first** entry of `WORKFLOW_DIRS` that
Hanzo Git collects workflows from the **first** entry of `WORKFLOW_DIRS` that
exists — not the union. So the moment one file lands in `.hanzo/workflows/`,
every remaining file under `.github/workflows/` stops running. It stops
*silently*: the checks that would go red are the ones no longer running, so the
@@ -117,7 +117,7 @@ comm -23 <(ls .github/workflows) <(ls .hanzo/workflows) # anything listed is d
Moving a file is not reviving it. Two things break on the way across:
- **`runs-on: ubuntu-latest` matches no runner in this fleet**, deliberately —
this forge hosts ~1400 mirrored repos whose upstream workflows all ask for it,
Hanzo Git hosts ~1400 mirrored repos whose upstream workflows all ask for it,
and advertising it hands the fleet to their CI. An unmatched label does not
fail, it **queues until the timeout**. Use `hanzo-build-linux-amd64`, or
another label declared in the `git-runner-config` ConfigMap (ns `hanzo`).
+2 -2
View File
@@ -264,7 +264,7 @@ case "$kcode" in
echo "X-Org-Id header; CI cannot set that header, cloud derives it from this"
echo "bearer. Mint a replacement with POST $api/v1/keys {\"type\":\"secret\"} as a"
echo "member of the owning org, then set it as the ORG-level Actions secret"
echo "HANZO_DEPLOY_TOKEN on git.hanzo.ai — the forge is what reads"
echo "HANZO_DEPLOY_TOKEN on git.hanzo.ai — that is what reads"
echo ".hanzo/workflows, not GitHub." ;;
esac
@@ -303,7 +303,7 @@ post() {
# success. So a new site needs no out-of-band `POST /v1/projects`; committing the
# workflow is enough. The repo is linked so the deploy's lifecycle events name a
# repo and reach the notify subscriptions keyed on it — the enqueue itself no
# longer requires a link; SITE_REPO overrides the origin for a forge
# longer requires a link; SITE_REPO overrides the origin for a
# checkout whose remote is not the canonical repo.
repo="${SITE_REPO:-$(git remote get-url origin 2>/dev/null || true)}"
pcode=$(post "$work"/sd-proj.json "$api/v1/projects" \
+1 -1
View File
@@ -16,7 +16,7 @@ import (
//
// It talks to the apiserver over plain HTTP because that is what the apiserver
// is: a JSON REST API this service can read with the same client it already
// points at the forge. A generated Kubernetes client would be the first
// points at Hanzo Git. A generated Kubernetes client would be the first
// dependency this repo has ever had, carried to spend two verbs on three
// resources.
//
+8 -8
View File
@@ -22,7 +22,7 @@ import (
// Every arrow is a job of the pipeline this repo publishes, so a service is
// current exactly when all four agree, and each way they can disagree names the
// arrow that did not happen. Reading all four from the systems that own them —
// the forge, the universe repo, the cluster — is what makes this a measurement
// Hanzo Git, the universe repo, the cluster — is what makes this a measurement
// rather than a restatement of the last log line the pipeline wrote about itself.
//
// Three matching values are NOT health, which is the whole reason head is read:
@@ -48,11 +48,11 @@ type Version struct {
func (v Version) known() bool { return v.Tag != "" || v.Digest != "" }
// Build is what the forge did with one commit.
// Build is what Hanzo Git did with one commit.
type Build struct {
// State is success | failure | running | absent. `absent` is not a kind of
// failure and is kept apart from one: a failing run is a build that ran and
// said no, while an absent run is the forge never having constructed a run
// said no, while an absent run is Hanzo Git never having constructed a run
// for the commit at all — a workflow it cannot parse or a reference it
// cannot resolve. There is no log to open for the second, so a page that
// draws them the same sends you looking for one that does not exist.
@@ -96,7 +96,7 @@ type Service struct {
Name string `json:"name"` // cloud
Namespace string `json:"namespace"` // hanzo
Image string `json:"image"` // ghcr.io/hanzoai/cloud
Org string `json:"org"` // forge owner; empty when the repo is unresolved
Org string `json:"org"` // Hanzo Git owner; empty when the repo is unresolved
Repo string `json:"repo"` // hanzo-inc/cloud
Head Head `json:"head"`
@@ -237,7 +237,7 @@ type live struct {
Want int
}
// link is one repo as the forge accounts for it.
// link is one repo as Hanzo Git accounts for it.
type link struct {
Repo string
Org string
@@ -256,7 +256,7 @@ type link struct {
// against another's running version, which manufactures drift that is not there.
//
// So the namespace and name carry the row, the cluster is matched on the same
// pair, and the IMAGE is what joins to the forge — several services legitimately
// pair, and the IMAGE is what joins to Hanzo Git — several services legitimately
// share one repo, and then they share its head and its last build too.
func assemble(pins []pin, lives []live, links map[string]link) []Service {
at := func(namespace, name string) string { return namespace + "/" + name }
@@ -421,8 +421,8 @@ func orgsOfServices(services []Service) []string {
// watch refreshes the fleet snapshot on its own interval.
//
// It is slower than the run poll because the values it reads move on deploy
// cadence rather than push cadence, and each cycle costs the forge a read per
// service. Nothing about a dashboard justifies loading the forge that schedules
// cadence rather than push cadence, and each cycle costs Hanzo Git a read per
// service. Nothing about a dashboard justifies loading the server that schedules
// every build in the fleet harder than the fleet itself does.
func watch(ctx context.Context, logger *slog.Logger, src *gitSource, cl *cluster, cache *fleetCache, cfg config) {
refresh := func() {
+2 -2
View File
@@ -78,14 +78,14 @@ func TestShippedRunStillCountsAsBuilt(t *testing.T) {
}
// TestAbsentIsNotFailure keeps the two apart. A failing run can be opened and
// read; a commit the forge never built a run for cannot, and sending someone to
// read; a commit Hanzo Git never built a run for cannot, and sending someone to
// look for a log that does not exist is its own delay.
func TestAbsentIsNotFailure(t *testing.T) {
old := time.Now().Add(-time.Hour)
if got := verdict(Run{}, nil, old); got.State != "absent" {
t.Errorf("no run for an hour-old commit = %q; want absent", got.State)
}
// A commit that has only just landed has no run yet because the forge is
// A commit that has only just landed has no run yet because Hanzo Git is
// still constructing one. Calling that absent would alarm on every push.
if got := verdict(Run{}, nil, time.Now()); got.State != "running" {
t.Errorf("no run for a commit pushed seconds ago = %q; want running", got.State)
+8 -8
View File
@@ -10,20 +10,20 @@ import (
"time"
)
// forge.go reads the two things the forge knows that the run list does not: what
// git.go reads the two things Hanzo Git knows that the run list does not: what
// hanzoai/universe declares, and which commit last produced a proved image.
//
// Both come from the same host over the same token this service already holds.
// The universe repo lives on the forge, so the declared state of the fleet is a
// The universe repo lives on Hanzo Git, so the declared state of the fleet is a
// file read — no second credential, no checkout, no clone on disk.
// A commit that has just landed has no run for a moment. Below this age a
// missing run is the forge still constructing one, and calling that absent would
// missing run is Hanzo Git still constructing one, and calling that absent would
// alarm on every push.
const settling = 5 * time.Minute
// How far back the walk looks for the last commit that built. Every step is a
// read against the forge, and past this depth the answer is already "this has not
// read against Hanzo Git, and past this depth the answer is already "this has not
// built in a long time", which the count says without another call.
const reach = 12
@@ -197,7 +197,7 @@ func (g *gitSource) links(ctx context.Context, logger *slog.Logger, pins []pin)
//
// Where that repo does not exist the publisher sits in another org — ghcr.io/
// hanzoai/cloud is built by hanzo-inc/cloud — and there the name alone is
// ambiguous, because half a dozen orgs on this forge hold a repo called cloud.
// ambiguous, because half a dozen orgs on Hanzo Git hold a repo called cloud.
// Then the declared tag decides: the repo carrying the tag the fleet is running
// is the repo that cut it. An image no candidate can be shown to publish stays
// unlinked, and the page says so rather than showing a guess.
@@ -267,7 +267,7 @@ func (g *gitSource) holds(ctx context.Context, repo, tag string) bool {
return g.getJSON(ctx, "/v1/repos/"+repo+"/tags/"+url.PathEscape(tag), &out) == nil && out.Name != ""
}
// link reads the forge end of the line: what we wrote, and what was last proved.
// link reads the Hanzo Git end of the line: what we wrote, and what was last proved.
func (g *gitSource) link(ctx context.Context, repo, branch string) (link, error) {
org, _ := splitFullName(repo)
c := link{Repo: repo, Org: org}
@@ -421,7 +421,7 @@ func (g *gitSource) built(ctx context.Context, repo string, r Run, at time.Time)
// which job failed, and whether an artifact exists, is what the page turns on.
func verdict(r Run, jobs []job, at time.Time) Build {
if r.ID == 0 {
// No run for this commit. A push that has only just landed is the forge
// No run for this commit. A push that has only just landed is Hanzo Git
// still constructing one; older than that, the run is genuinely absent —
// a workflow that could not be parsed or a reference that would not
// resolve, which leaves no failed run to open.
@@ -503,7 +503,7 @@ func (g *gitSource) jobs(ctx context.Context, repo string, run int64) ([]job, er
}
// each runs fn over items with a small fixed fan-out. The cap is deliberately
// low: these are reads against the forge that schedules every build in the
// low: these are reads against the server that schedules every build in the
// fleet, and a dashboard is never worth degrading it.
func each[T any](ctx context.Context, items []T, fn func(context.Context, T) error) error {
const workers = 4
+2 -2
View File
@@ -180,7 +180,7 @@ type config struct {
// universe is the repo holding the declared state of the fleet, and
// fleetRefresh is how often the four values are re-read. The fleet interval
// is longer than the run interval because these values move on deploy
// cadence, not push cadence, and each cycle costs the forge a read per
// cadence, not push cadence, and each cycle costs Hanzo Git a read per
// service.
universe string
fleetRefresh time.Duration
@@ -454,7 +454,7 @@ func poll(ctx context.Context, logger *slog.Logger, src *gitSource, cache *runCa
}
// Fan out, bounded. The cap is small on purpose: this is a read against
// the forge that schedules every build in the fleet, and a dashboard is
// the server that schedules every build in the fleet, and a dashboard is
// never worth degrading it.
const workers = 6
var (