LLM.md is the source and stays the source; CLAUDE.md is a symlink to it, so there is
one file and it cannot drift from itself. What changes is that the link is committed
rather than left for each checkout to recreate.
Left untracked, the doc was conditional on a step outside the repo: a fresh clone had
no CLAUDE.md, nothing loaded LLM.md, and an agent worked here having never read what
this repo already explains. Most of the estate already tracks theirs; this is the rest
agreeing with them.
This package kept its own HTTP secret plane — routes, an audit log, version CAS
and a JWT gate — beside the one cloud already serves at
/v1/kms/orgs/{org}/secrets. Two doors onto the same store means two places
where a tenant boundary can be got wrong, and only one of them was reviewed as
the authorization surface.
What is left is storage and the ZAP transport, whose authorizer is a signed
consensus-authority snapshot and which stays disabled unless that snapshot is
present. This package now makes no authorization decision at all: IAM owns
identity, cloud folds the caller's validated org into the storage key so one
tenant's path cannot name another's record, and secrets are sealed at rest with
the replicator streaming encrypted backups to S3.
go.sum carried a stale pair for luxfi/vm v1.3.1 — the tag was republished, and
the entries here predated it, so nothing in the module could build. The values
now match the public checksum database, which six other repos in the estate
already agreed with.
Co-authored-by: Hanzo Dev <[email protected]>
A caller has to tell "this tenant brought no key" from "the store could not
answer": the first says fall through to another custody, the second says stop.
Both arrived as untyped errors, so the only way to ask was to read the text —
and the non-200 branch echoes the response body, so any fault whose body
happened to say "not found" was read as an absent secret and sent the caller
somewhere less guarded.
ErrSecretNotFound was already declared, with a comment promising callers would
branch via errors.Is. Wrap it on the three paths that mean a secret is genuinely
absent. A tenant miss keeps its own untyped error: it is a fault, not an absence,
and reporting it as one would restore the bug in a new place.
Co-authored-by: Hanzo Dev <[email protected]>
Release / Build binaries (darwin/arm64) (push) Failing after 0s
Release / Build binaries (darwin/amd64) (push) Failing after 0s
Release / Build binaries (linux/arm64) (push) Failing after 0s
Every module in this graph is public and notarized, so `go mod download`
resolves through the module proxy and verifies against sum.golang.org.
Both Dockerfiles drop the build ARG and the git credential rewrite; the
release workflow stops passing a token as a build argument and stops
setting GOPRIVATE/GONOSUMDB for the binary matrix.
Root go.sum gains the module hashes for the versions MVS selects across
the sdk/go replace, which `go build` requires under -mod=readonly.
luxfi/kms v1.12.16 gives the hybrid handshake a second output beside the
session key: a binding that names the channel, computable only by the two
endpoints that ran it. Every envelope carries the binding of the session
it is sent on, and the server honours it only there. A request carried to
some other peer is inert — it cannot be re-signed, and no other channel
can produce the name it holds.
RequireSession goes away with it. A client without a session now has
nothing to address a request to and cannot speak at all, so the dial
agrees a hybrid session or fails; that is the shape of the code rather
than the value of a field.
List builds each answer from the coordinate the record came back with.
A path is a subtree root, so a result can sit below the one that was
asked for, and re-joining the query prefix would name it somewhere it is
not.
Co-authored-by: Hanzo Dev <[email protected]>
The HTTP path addressed /v1/kms/orgs/{org}/secrets. The server mounts
/v1/kms/secrets and takes the org from the credential the request is made with,
so every HTTP-transport call reached nothing — measured against the live edge,
where the mounted path answers a JSON 403 and the org path a bare 404.
Reading the tenant from the credential is also the only place it can be trusted
from: a segment is a name the caller picks, and one identity naming another
org's secrets should not be a request this client can compose.
The four tests that covered these calls asserted the org path against a mock
server, which answers whatever path it is sent — so they were green the whole
time the transport was broken. They now pin what the server mounts, and a test
beside them fails if an org ever steers the URL again.
Co-authored-by: Hanzo Dev <[email protected]>
Everything this client moves is a secret, so the dial now requires the
X25519 + ML-KEM-768 handshake to have produced an AEAD session, and
requires that session to have run ML-KEM rather than X25519 alone. A peer
that agrees neither gets no client and no request.
Hybrid rather than post-quantum alone because both halves have to fall
before the channel does: a break in the elliptic curve and a quantum
computer are two different futures, and a key read today is stored today.
It is a constant rather than a setting. There is no caller here for whom a
plaintext channel is the right answer, and a setting is a thing an
adversary gets to influence by making the handshake fail.
luxfi/kms v1.12.15 for the RequireSession dial, luxfi/keys v1.4.2 for a
service identity that keeps its signing key past the first signature.
Co-authored-by: Hanzo Dev <[email protected]>
Work landed on GitHub while CI reads the forge, so each side carried commits the
other did not: the forge had the module-proxy fetch, the recorded go.sum and the
sdk/go minio removal; GitHub had kms building its own zip.App. A force-push
either way would have dropped one of them silently.
go.mod conflicted only on version skew — zap v1.2.7/zapdb v1.10.0 against
v1.2.6/v1.10.6 — resolved to the higher of each, which is what version selection
picks anyway. go.sum was reconciled by re-tidying rather than by choosing a side.
Before that, every module@version the two go.sum files shared (339 in the root,
275 in sdk/go) was checked for hash disagreement: none, so no re-tag hid here.
go build ./... clean, go test ./... green.
Co-authored-by: Hanzo Dev <[email protected]>
github.com/hanzoai/cloud lost the v0.x tags it was published under — GitHub
carries v1.801.31 and nothing else — so GOPRIVATE, which implies GONOPROXY,
sent go mod download to a revision that is no longer there and build-amd64
died on "unknown revision v0.1.1". The proxy still holds it, and holds the
same bytes: the zip hashes to the h1: already in go.sum.
Dockerfile.kms-fetch never had the problem because its env assignments bind
to the git config they precede rather than to go mod download, so its graph
has always come through the proxy. Deleted there too, so both files read the
way they behave.
Every package failed to set up, so no test ran:
FAIL github.com/hanzoai/kms [setup failed]
luxfi/[email protected]/blackhole.go:6:8: missing go.sum entry for module
providing package github.com/luxfi/…
Three indirect dependencies had moved under their own patch ranges —
container, geth and vm — and go.sum still held the older hashes. `go mod tidy`
records what the build resolves to.
Verified: go build ./... clean, go test ./... passes.
This module could not build at all. luxfi/[email protected] was re-tagged after
publication, so its bytes and its recorded hash disagree permanently:
verifying github.com/luxfi/[email protected]/go.mod: checksum mismatch
SECURITY ERROR
and that is unrecoverable in place — the proxy and checksum database are
append-only. It also blocks its own escape: `go get luxfi/[email protected]` has to load
the current module graph first, which means verifying v1.3.1, which is the thing
that fails. So the upgrade is done by editing the requirement directly and
dropping the abandoned version's two go.sum lines. That is not accepting the
re-tag; it is leaving the version behind. Nothing here now references v1.3.1.
With the module able to resolve again, zapdb, database, geth and container come
up to the releases where each stopped pulling minio through hanzos3/go-sdk,
whose `replace` redirect Go ignores outside a main module. go.mod names zero
minio modules.
This matters beyond the tree: sdk/go is published as
github.com/hanzoai/kms/sdk/go and external consumers resolve it directly, so the
root module going clean earlier did nothing for them while this one was stuck.
build and vet pass — the first time this module has compiled in a cold cache.
Nothing here ever imported minio — zero .go files reference it. It arrived
through luxfi/[email protected].0, which required hanzos3/go-sdk, which pointed at our
checksum forks with `replace`. Go ignores `replace` in a non-main module, so that
redirect reached none of zapdb's consumers and minio-go, crc64nvme and md5-simd
resolved for real. zapdb v1.10.6 requires hanzos3/go by name, which does travel.
sdk/go is NOT fixed here and could not be. It is a separate module and its build
is already broken on an unrelated supply-chain problem:
verifying github.com/luxfi/[email protected]: checksum mismatch
downloaded: h1:U2Bv7IDRFv8JtjUARfY53ZnOPRB2iPrPHSMDCQ+T0Js=
go.sum: h1:rLCbygaajehVkUoJ5czwhpUAJaC5J5okq3p+j2QSPSo=
That tag was re-pointed after publication, so its recorded hash and its bytes no
longer agree and `go get` cannot run in that module at all. Verified identical on
an untouched worktree of this same commit before concluding it. Leaving sdk/go
alone rather than reaching for GONOSUMDB or hand-editing go.sum — either would
paper over a re-tag, which is the one failure mode that must stay loud.
So: root module clean, submodule blocked behind luxfi/vm. Its published
sdk/[email protected] still carries minio for external consumers until that is resolved.
The coupling was one string. Mount(app, deps) took hanzoai/cloud's Deps and
read exactly two fields out of it: a logger, which luxfi/log already publishes
as a process default and which Embed has always used, and a data root.
Everything else in that struct crossed the boundary to be discarded. An init()
then pushed this package into cloud's registry so cloud could call Mount back.
That points the dependency the wrong way. It makes the graph circular, since
cloud imports kms in order to mount it, and it fixes the set of possible hosts
at one: two editions of hanzoai/cloud declare the same module path, so
cloud.Deps names a different type in each, and a package that takes it can be
composed by one of them and never the other.
kms.App(root) now builds the app and returns it, and a host composes it with
app.Use. Routes are absolute, so composition neither moves nor shadows them;
the bridge test runs through a host app with middleware in front of it to hold
that. Teardown travels with the app rather than with the process: the Embedded
server drains on a shutdown hook, and zip makes a parent drain the children it
composed, so kms.Shutdown and the package global it read are gone. The readyz
branch that answered 503 when that global was nil went with them, since a
failed Embed returns an error and no app at all.
The daemon read the host's config too, and that was a live bug: it bound
$CLOUD_LISTEN, default :8080, while its own image sets KMS_LISTEN=:8443 and its
HEALTHCHECK curls 8443. It now reads its own, through the one function that
resolves that address for both the daemon and Embed.
zip moves from v1.10.0 to v1.27.2, the version the host already selects,
because Use taking a Component is what makes an app composable at all.
middleware.Logger and the KMS_LOG_LEVEL=debug opt-in around it go with the
bump; zip logs every request itself now, so that report is no longer something
a binary can be missing.
Dropping cloud drops what it dragged in. goja, esbuild and a sourcemap parser
leave the graph: a secret store no longer links a JavaScript engine.
Both go.sum files carried a pre-retag hash for luxfi/vm v1.3.1 that no longer
matches the bits the proxy serves, which failed every build outside the
workspace. Corrected against the checksum database, which agrees with the
download.
Co-authored-by: Hanzo Dev <[email protected]>
These diverged on 2026-07-27 and never came back. The two sides touched
disjoint files, which is why neither noticed: the forge took six commits to
.hanzo/workflows/release.yml, go.mod and go.sum (dropping endpoints and cache
backends this forge does not implement), and GitHub took one to README.md
saying the repo is archived and superseded.
Merged rather than picking a winner. Nothing conflicts, and both statements are
true — the CI had to be made to work on the forge, and the repo is superseded.
Co-authored-by: Hanzo Dev <[email protected]>
Every build job passed and only the release job failed, with:
::error::404 page not found
`generate_release_notes: true` makes softprops/action-gh-release call
POST <api>/repos/<owner>/<repo>/releases/generate-notes
which is a GitHub-only API. Measured against this forge:
GET /v1/repos/luxfi/consensus/releases -> 200
POST /v1/repos/luxfi/consensus/releases/generate-notes -> 404 "404 page not found"
The body is Go`s bare 404 text, which the action surfaces verbatim -- so the
error names no endpoint and reads as a broken release step or a bad token. The
base path was never wrong: the forge serves its API at /v1 and v1.26.29 already
advertises that (services/actions/context.go builds api_url as AppURL + "v1").
Releases still publish; only the auto-generated notes are gone, which the forge
cannot produce anyway.
Co-authored-by: Hanzo Dev <[email protected]>
type=gha resolves its backend from an Actions runtime token that git.hanzo.ai
does not issue in a form buildx can parse, and the exporter is configured as
part of the solve -- so it fails the build rather than degrading to an uncached
one. Same defect fixed in hanzoai/o11y. ignore-error keeps a cold cache from
404ing the first build.
Co-authored-by: Hanzo Dev <[email protected]>
The docker job runs on hanzo-build-linux-amd64, so `secrets.GITHUB_TOKEN` is a
git.hanzo.ai token presented to ghcr.io, which has never seen it:
Error response from daemon: Get "https://ghcr.io/v2/": denied: denied
That pair is GitHub`s own idiom and correct there; it is simply not a credential
on a self-hosted pool. orgs/hanzoai provisions GHCR_USER + GHCR_TOKEN.
Co-authored-by: Hanzo Dev <[email protected]>
The module graph could not be loaded at all: transitive requirements name
versions whose tags were wiped or whose pseudo-versions point at commits that
history rewrites removed, so `go list -m all` fails with "unknown revision" or
"invalid pseudo-version". Builds still passed because those modules supply no
package that gets compiled — the breakage is graph-level, which is exactly the
kind that only shows up on a clean checkout.
`exclude` is the mechanism for "this version is unusable": MVS then selects the
next higher one instead of trying to fetch a revision that is gone. Bumping the
requirement alone does not work, because Go still reads the excluded version
go.mod while building the graph.
Co-authored-by: Hanzo Dev <[email protected]>
Both the zip and the go.mod hash recorded here were captured from a direct VCS
fetch made while GOPRIVATE covered github.com/luxfi/*, which switches off the
proxy and checksum verification together. The tag moved afterwards, so what we
wrote down no longer matches sum.golang.org.
proxy.golang.org still serves the originally-published bytes and they verify
against the log, so dropping the two lines and re-tidying restores the build
with no version change.
Co-authored-by: Hanzo Dev <[email protected]>
The description already said so; the README still opened by describing a live
product. A reader landing here now sees where it went before anything else.
This workflow runs on git.hanzo.ai, which presents as GHES, and the v4 action
refuses before doing any work:
GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and
download-artifact@v4+ are not currently supported on GHES.
v4 moved to an artifact API the forge does not implement, so this is a hard
refusal rather than a transient upload error — retrying could never help. The
damaging part is how it presents: the build step passes, the artifact step
fails, and the job reports "failed", so a green build reads as a broken one and
the artifact is silently never produced.
Found on hanzoai/chains, where every VM in a 15-way matrix compiled and then
discarded its binary at this step. Swept the estate for the same pattern.
Co-authored-by: Hanzo Dev <[email protected]>
The first pass left three classes of config that BOTH tsc 5.9 and tsc 7
reject. Each was proven against both compilers before changing:
TS5090 A `paths` target must be relative once `baseUrl` is gone. The
first pass skipped the "./" prefix wherever baseUrl pointed at
the config's own directory, reasoning it was semantically
equivalent. It is not — without baseUrl a non-relative target
is rejected outright, by 5.9 as well as 7.
TS5110 `moduleResolution: node16` requires `module: node16`. The first
pass mapped commonjs projects to node16 resolution alone, which
BROKE those configs for the current toolchain. Both are now set.
TS5102 `downlevelIteration` is also removed in TS7; it was missing from
the dead-flag list.
Verified: repos that tsc 7 previously refused (base-studio, js-sdk, kv-js)
now report zero config errors on tsc 7 AND tsc 5.9.
Co-authored-by: Hanzo Dev <[email protected]>
TypeScript 7 is the native Go compiler and removes `baseUrl` and
`moduleResolution: node|node10`. Both appear here, so `tsc` from TS7
refuses the config outright (TS5102 / TS5108) and cannot typecheck.
`paths` targets resolve relative to `baseUrl` when it is set and relative
to the tsconfig file otherwise. Every `baseUrl` folded here already
pointed at the config's own directory, so dropping it moves nothing and
the targets are left byte-identical. Where a baseUrl pointed elsewhere,
each affected target was rewritten as join(baseUrl, target).
`moduleResolution` was chosen from the declared `module`: commonjs ->
node16, esnext/preserve -> bundler. Configs whose `module` is unset or
exotic were left alone rather than guessed at.
The result is accepted by BOTH toolchains, so nothing has to upgrade
TypeScript in lockstep. Verified on hanzo/chat packages/api: tsc 5.9
779 -> 778 errors (no regression), and tsc 7.0.2 now runs the project
in 2s where it previously refused the config.
Co-authored-by: Hanzo Dev <[email protected]>
Superseded: the Hanzo GitHub App pushes a webhook, so the forge tracks GitHub
without a per-repo workflow. This file called git.hanzo.ai/api/v1/.../mirror-sync
— a Gitea API for a system we no longer drive — and would sit inert in every repo.
One mechanism, in one place, instead of ~350 copies of a cron.
http v0.3.0 replaces the JSON header map in the ZAP frame with
length-prefixed name/value pairs ([u32 count] then [u32 nameLen][name]
[u32 valueLen][value]), taking decode to 0 allocs/op. That is a wire
break — a v0.2.x peer cannot talk to a v0.3.0 peer — so every Hanzo
module moves together. Deployment is operator-managed GitOps, so
merging aligns the code without shipping anything.
No source change was needed: mount.go already fronts the net/http
subtree with app.All("/v1/kms/*", zip.AdaptNetHTTP(h)) rather than the
removed App.Mount(prefix, http.Handler), and this module builds no ZAP
transport of its own.
That bridge was the one path the frame change touches and the one path
no test covered — every other suite calls the handler directly, in
process, so a framing regression would have landed silently.
TestMountBridge_NetHTTPOverZIP now serves the mount.go route shape over
a real TCP listener and drives it with a real net/http client, asserting
method, path, query, request headers, body, status, response headers
and repeated response headers all survive the round trip. Verified live
by mutation: drop the request header at the client and the test fails
with `request hdr = "" ... did not survive the bridge`.
Co-authored-by: Hanzo Dev <[email protected]>
zip v1.9.0 repurposes App.Mount(prefix, addr) as prefix -> remote-address
proxying (unix/tcp ZAP), so it no longer takes an http.Handler. The
in-process KMS surface goes on the documented net/http bridge instead:
a wildcard route carrying zip.AdaptNetHTTP — byte-identical to what the
old Mount expanded to (All(prefix+"/*")), so /healthz and /v1/kms/* keep
resolving exactly as before, bare prefix included (fiber marks a trailing
greedy wildcard optional).
Also pulls zap-proto/fiber/v3 v3.2.1 in place of gofiber/fiber/v3 v3.2.0
as zip's router, transitively.
go build + go test green across every package this module owns
(github.com/hanzoai/kms, cmd/{kms,kms-fetch,kmsd,smoke-zap}) and the
sdk/go module.
Co-authored-by: Hanzo Dev <[email protected]>
Four failures, both causes the same shape: state that belongs to the whole
process being read or written as if it belonged to one test.
TestEnvRequired_* (x3) failed with 401 on requests whose tokens were fine.
Embed applies ExpectedIssuer/ExpectedAudience/JWKSURL to the process-global
authConfig — that is what Embed is FOR — and stopping the instance does not put
it back. So after TestEmbed the process no longer accepted sharedIssuer, and
verification reached for a JWKS server TestEmbed had already closed. Every later
test minting a shared-issuer token got 401, and the only reason it was those
three is that they sort after TestEmbed. They passed in isolation, which is
what made it look like their problem.
jwtTestEnv.cleanup already restores exactly this; the embedded path had been
missed. Registered before jwks.Close so it runs last (t.Cleanup is LIFO).
TestConnMemory measured runtime.NumGoroutine() — process-wide — and divided it
by the connections under test. Run alone that ratio is 1.00; run inside the
package it also counts badger compactors, httptest servers and whatever else is
still up, and it read 1.15 against a [0.95, 1.05] band. The test was failing for
goroutines it does not own. It now takes a goroutine baseline and reports the
delta, which is what the heap half of the same function has always done two
lines above.
Package green, twice.
Co-authored-by: Hanzo Dev <[email protected]>
git.hanzo.ai is canonical and reads .hanzo/workflows, so build.yml,
build-kms-fetch.yml and pr-preview.yml move there by changing directory and
nothing else — same GitHub Actions syntax, same runs-on, byte-identical files.
Three more lanes had been deleted from .github/workflows without a replacement
anywhere, which silently removed this repo's only gate and the only producer of
its release binaries and semver image tags. There was no red run to show it,
because a workflow that does not exist cannot fail. Restored byte-for-byte from
history at the path the forge reads:
ci.yml go test -race ./... + builds kmsd and kms
release.yml v* tags: 4-way binary matrix, release, multi-arch image
check-fe-ts-and-lint.yml frontend typecheck on PRs
release.yml's notify-universe job is left exactly as it was; it is already
continue-on-error, so a missing UNIVERSE_DISPATCH_TOKEN on the forge degrades to
a yellow job instead of failing the release.
Not restored: validate-pr-title.yml, a conventional-commit title gate inherited
with an upstream PR template. Dropped: workflow-sanity.yml, which triggers on
paths .github/workflows/** and so can never fire again once the lanes live under
.hanzo. Neither builds, tests or publishes anything.
sync.yml is replaced with the canonical copy — the variant here predated it and
had drifted.
Co-authored-by: Hanzo Dev <[email protected]>
git.hanzo.ai mirrors this repo by PULL on a ~10-minute interval, and arcd runs
CI/CD there — so every push waited out that interval before anything built.
This asks Gitea to pull HEAD immediately.
Latency only: the repo already mirrors via the App webhook, so a missing
HANZO_GIT_TOKEN or a failed curl is non-fatal and never fails the push.
Idempotent (mirror-sync just pulls HEAD) and concurrency-coalesced.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
This workflow ran on a self-hosted actions-runner-controller pool. ARC is
decommissioned: CI executes inside git.hanzo.ai (Gitea Actions, .hanzo/workflows)
and delivery is cd.hanzo.ai reconciling the reviewed image pin in
hanzoai/universe. GitHub is a mirror and runs nothing.
Removed rather than repointed — there is no GitHub-side pool to move to, and
leaving it would keep ARC load-bearing.
kmsd built the OTLP HTTP exporter, which pulls gRPC and protobuf even on the
HTTP transport — otlptracehttp shares otlpconfig with the gRPC one.
luxfi/trace v1.3.0 exports over ZAP natively: a JSON SpanBatch inside a ZAP
envelope, read by hanzo/o11y/pkg/zapreceiver.
This module's graph is now free of both: otlp and google.golang.org/grpc are
each zero in go.mod. Knobs are O11Y_ENDPOINT / O11Y_TRACES_ENDPOINT /
O11Y_SERVICE_NAME, with O11Y_ENDPOINT=default resolving to o11y.hanzo.svc:4317.
Posture unchanged: opt-in, non-fatal, no dial at construction.
The Keys page labeled the two validator slots "BLS public key" and "Corona
public key". Those names do not describe the cryptography: the "BLS" slot is
secp256k1 signed via CGGMP21 threshold ECDSA (not bls12-381), and the "Corona"
slot is ed25519 signed via FROST (not post-quantum, not the Corona R-LWE
lattice protocol). Relabel to state the actual scheme so operators are not
misled into a bls12-381/PQ assumption.
Co-authored-by: Hanzo Dev <[email protected]>
Provably-dead exported accessor: no in-repo callers (only cmd/kmsd and
mount.go consume the root package) and no external importer of
github.com/hanzoai/kms across the tree uses it. Its backing httpAddr
field was write-only and its comment was wrong (stored the raw config
addr, never a resolved listener address). Removes field, assignment,
and accessor. Build+vet green.
The binaries job built with GOPRIVATE='' so go verified the first-party
luxfi/* modules against the public sumdb — force-moved upstream tags never
match, so every release failed with 'checksum mismatch / SECURITY ERROR'
(luxfi/[email protected]). Set GOPRIVATE + GONOSUMDB for luxfi/* + hanzoai/* and add a
git-config token (GH_PAT, GITHUB_TOKEN fallback) so the modules resolve directly
via git and skip the sumdb. Dockerfile: GONOSUMCHECK (removed by Go) -> GONOSUMDB,
guard the token, x-access-token form; docker job passes GH_PAT so it can read
luxfi.
Latest v1.x of the canonical KMS server. luxfi/keys (v1.4.1) and
luxfi/crypto (v1.20.2) were already at latest v1.x via the module graph.
luxfi/mpc stays out of the graph by design: threshold signing is delegated
over ZAP to an external MPC daemon (cfg.MPCAddr / MPCVaultID) through
luxfi/kms's pkg/mpc client; this module never links the standalone daemon.
GOWORK=off go build/vet/test green; test delta vs HEAD = 0.
Swap the audit-trail SQLite driver from modernc.org/sqlite to hanzoai/
sqlite, the dual-backend drop-in that registers the "sqlite" driver name
under both CGO (mattn+SQLCipher) and !cgo (modernc) builds.
- audit.go: blank import + storage doc comment rebranded.
- red_hardening_test.go: swap the test-side blank import too. Both files
had to move together: under CGO=1 hanzoai/sqlite registers "sqlite" via
mattn while a lingering direct modernc import ALSO registers it, causing
`panic: sql: Register called twice for driver sqlite` at test init.
Swapping both leaves a single registration under both build configs.
Security: SQLite in KMS backs ONLY the plaintext audit trail (KMS_AUDIT_DB);
secrets are MPC-rooted and never stored in SQLite. The audit DB is opened
with a nil key via sql.Open("sqlite", ...&_pragma=...), so it stays
plaintext under both backends — the swap does not change the at-rest path.
DSN is already modernc-style (_pragma=NAME(VALUE)); no conversion needed.
Co-authored-by: hanzo-dev <[email protected]>
The kms Go SDK module directly imports luxfi/keys (NewServiceIdentity /
ServiceIdentity). Its v1.2.0 pin was force-moved; adopt the stable v1.4.1,
regenerate go.sum from the current remote. Builds clean (CGO_ENABLED=0).
Co-authored-by: Hanzo Dev <[email protected]>
kms pinned luxfi/keys v1.4.0 whose remote tag was force-moved (content
5153d639→80a3745a → go.sum checksum mismatch), plus zap-proto/http v0.1.0
(also force-moved) and geth v1.16.98 (deleted tag). Only keys v1.4.1 is
byte-stable; adopting it floors the unified luxfi stack and regenerates
go.sum from the current remote. kmsd + kms (pure-Go CGO_ENABLED=0, ZapDB)
build clean; the ServiceIdentity surface (keys.NewServiceIdentity) is
unchanged (v1.4.1 = local v1.4.0 identity work + geth bump).
go.mod/go.sum only.
Co-authored-by: Hanzo Dev <[email protected]>