mirror of
https://github.com/hanzoai/kms.git
synced 2026-10-17 11:18:16 +00:00
Docker Build & Push (Reusable) / create-manifest (push) Blocked by required conditions
Docker Build & Push (Reusable) / tag-single-arch (push) Blocked by required conditions
Docker Build & Push (Reusable) / deploy (push) Blocked by required conditions
Docker Build & Push (Reusable) / sbom (push) Blocked by required conditions
Docker Build & Push (Reusable) / build-amd64 (push) Waiting to run
Docker Build & Push (Reusable) / build-arm64 (push) Skipped
Build kms / build (push) Waiting to run
Release / Build binaries (linux/amd64) (push) Waiting to run
Release / GitHub Release (push) Blocked by required conditions
Release / Multi-arch Docker (ghcr.io/hanzoai/kms) (push) Blocked by required conditions
Release / Notify universe (auto-bump pin) (push) Blocked by required conditions
Release / Build binaries (linux/arm64) (push) Failing after 0s
Release / Build binaries (darwin/arm64) (push) Failing after 0s
Release / Build binaries (darwin/amd64) (push) Failing after 0s
A Hanzo node is named by a wallet: one secp256k1 keypair is its identity on the settlement L1, the account it stakes from, the account its work rewards pay to, and the row the fleet view lists it under. One key with four uses is a key whose copy is a forged identity and a spendable stake, so the node does not hold it. The KMS generates it, seals it, and returns it to nobody; the node asks for signatures. The measurement is not compared, it is required. A key is sealed with store.Seal at path="node", name=address, env=measurement, so the image is the envelope's associated data, and unseal overwrites all three coordinates with what the CALLER presents. A wrong image is a GCM authentication failure, not a policy check that a later edit could drop. The signing surface is a closed set. Every digest is keccak-256 over a fixed-length preimage led by a purpose tag and committing to the address, the sealed measurement and a strictly increasing epoch, so no digest minted here is an RLP transaction hash: the wallet cannot be spent through this surface, and a captured signature cannot be replayed forward. Signatures are canonical (low-s), so one statement has one encoding. Rotation is a change of identity, because a wallet's key cannot change under a fixed address: the outgoing key signs a succession naming its successor, that statement is recorded where it stays readable, and the outgoing material is destroyed in the same transaction. Revocation destroys material and keeps the row, so a slashed node stays attributable. custody is a library, not a surface. It holds keys and enforces what is a property of a key; it derives no permission from a credential and never sees one. That follows the division this repository already made for secrets: it answers who signed a token, and the org-scoped plane with its authorization lives in cloud (apps/kms). Paths therefore carry no tenant — the server reads it from the credential, as the secret surface does. custody/mount_test.go is the reference mount, written out in full and driven by the real client, so the serving side has a worked example rather than a description. The contract has one definition. sdk/go/node holds the statements, the wire types and the verification routine, with no store, transport or server dependency; the KMS, the node daemon and the chain indexer all import it, and custody builds its digests by calling the same package a verifier does. Callers reach the surface through sdk/go/kmsclient rather than hand-rolling requests, and refusals arrive as *kmsclient.Failure carrying the status. Verification takes the payload the caller already holds — a receipt carries the measurement it attests but never the payload it was given. On hardware without SEV-SNP or TDX the node declares its measurement, which pins the key to one image and detects drift but does not prove the image. A claim of a hardware TEE with no linked verifier is refused rather than downgraded: recording "attested" on an unchecked claim would make the fleet view a lie. docs/custody.tex states what is enforced today against what a TEE would add, and what a caller is assumed to do. Co-authored-by: Hanzo Dev <[email protected]>