Public-flip audit: module URL + README humanization

git.sulkta.coop → git.sulkta.com (matches the live public Forgejo endpoint).
README dropped AI-agent positioning + emoji status table; kept all
technical content (DST, MuSig aggregation, exit codes, MCP tool table).
This commit is contained in:
Cobb Hayes 2026-05-27 11:29:05 -07:00
parent 9d6c7cffbe
commit 7b9b09133b
7 changed files with 63 additions and 82 deletions

100
README.md
View file

@ -2,46 +2,38 @@
Pure-Go client for the Cardano [Mithril](https://mithril.network) protocol.
Mithril is Cardano's stake-based certified-snapshot system — it lets a new
node bootstrap the chain from a cryptographically-verified snapshot
instead of replaying every block from genesis.
Mithril is Cardano's stake-based certified-snapshot system. A new node
bootstraps the chain from a cryptographically-verified snapshot instead
of replaying every block from genesis.
The official [`mithril-client`](https://github.com/input-output-hk/mithril)
is Rust. `mithril-go` is a pure-Go reimplementation that produces a single
static binary with no CGo, no upstream Rust runtime, and an MCP-native
tool surface for AI agents — useful for:
- Embedding Mithril bootstrap into Go-based Cardano tooling (alongside
`gouroboros`, `dingo`, etc.)
- Constrained ARM/embedded targets where shipping the Rust binary + its
deps is overkill
- Operators who prefer a single `go install`-able helper
- AI agents (Claude Code, Cursor, Zed, ...) that want to verify Mithril
certs as a callable tool
The reference [`mithril-client`](https://github.com/input-output-hk/mithril)
is Rust. `mithril-go` is a pure-Go reimplementation: single static binary,
no CGo, no `blst`, no upstream Rust runtime. Also ships an MCP stdio
server for agent use.
## Status
**Working consensus-correct verification against live mainnet and preprod.**
Consensus-correct verification against live mainnet and preprod.
| Capability | Status |
|---|---|
| Aggregator REST client | ✅ list, show, cert, walk-chain |
| Resumable HTTP download (SHA hook + progress) | |
| Streamed zstd+tar extract (tar-slip defended) | |
| Genesis Ed25519 verification | ✅ live mainnet + preprod |
| STM BLS12-381 aggregate verification | ✅ live mainnet + preprod |
| Lottery-win threshold (Taylor series, big.Rat) | |
| Merkle batch-proof verification (Blake2b-256) | |
| AVK chaining + epoch + hash continuity | |
| **Full chain verification (genesis → head)** | ✅ live mainnet + preprod |
| Per-immutable SHA manifest verification | |
| MCP stdio server (8 tools) | |
| Full immutables-download loop (8000+ files) | |
| Aggregator REST client (list, show, cert, walk-chain) | done |
| Resumable HTTP download (SHA hook + progress) | done |
| Streamed zstd+tar extract (tar-slip defended) | done |
| Genesis Ed25519 verification | done |
| STM BLS12-381 aggregate verification | done |
| Lottery-win threshold (Taylor series, big.Rat) | done |
| Merkle batch-proof verification (Blake2b-256) | done |
| AVK chaining + epoch + hash continuity | done |
| Full chain verification (genesis → head) | done |
| Per-immutable SHA manifest verification | done |
| MCP stdio server (8 tools) | done |
| Full immutables-download loop (8000+ files) | pending |
## Quick start
```bash
go build ./cmd/mithril-go # produces a 9.5 MB single static binary
go build ./cmd/mithril-go # ~9.5 MB single static binary
mithril-go info -network mainnet
mithril-go list -network mainnet
@ -79,27 +71,24 @@ internal/
verify/ Genesis Ed25519 verification
```
## What was non-obvious (so future-you doesn't have to dig)
Three things in the upstream Rust that aren't documented anywhere
prominent:
## Upstream details that aren't documented
1. **DST is empty.** Mithril's BLS hash-to-G1 uses an empty domain
separation tag, not the IETF standard `BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_`.
The Rust calls `blst.verify(sig, msg, &[], &[], pk, ...)` — the
`&[]` is the empty DST.
2. **The signed message is `signed_message_bytes || mt_root_bytes`**,
separation tag, not the IETF standard
`BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_`. The Rust calls
`blst.verify(sig, msg, &[], &[], pk, ...)` — the `&[]` is the empty DST.
2. **Signed message is `signed_message_bytes || mt_root_bytes`**,
not `signed_message` alone. The Merkle commitment root is appended
before BLS verify.
3. **Aggregation is MuSig-style scalar-weighted, not plain.**
3. **Aggregation is MuSig-style scalar-weighted**, not plain.
`t_i = Blake2b-128(Blake2b-128(σ_0‖…‖σ_{n-1}) ‖ be_u64(i))`,
then `aggr_sig = Σ t_i·sig_i` and `aggr_vk = Σ t_i·vk_i`.
Plain summation does not interop with blst.
Plus the `protocol_message` hash is SHA-256 over key-then-value, with
keys ordered by **Rust enum declaration order**, not alphabetical.
The `protocol_message` hash is SHA-256 over key-then-value, with keys
ordered by **Rust enum declaration order**, not alphabetical.
## Machine / LLM usage
## JSON + exit codes
Every query command accepts `-json`:
@ -119,12 +108,11 @@ Stable exit codes:
| 5 | signature verification failure (genesis or STM) |
| 130 | canceled (SIGINT) |
These are the contract — existing codes won't renumber.
Existing codes won't renumber.
### MCP server
## MCP server
`mithril-go mcp` brings up a Model Context Protocol stdio server.
Compatible with any MCP client (Claude Code, Cursor, Zed, custom agents).
Tools exposed:
@ -135,15 +123,9 @@ Tools exposed:
| `mithril_show_snapshot` | Detail for a snapshot (or `latest`) |
| `mithril_get_certificate` | Cert by hash (or `head`) |
| `mithril_walk_cert_chain` | Walk previous_hash from head to genesis |
| `mithril_verify_certificate` | Ed25519 OR STM BLS verify, dispatched by cert kind |
| `mithril_verify_certificate` | Ed25519 or STM BLS verify, dispatched by cert kind |
| `mithril_verify_chain` | Full chain verify with per-step report |
| `mithril_verify_genesis` | Walk to genesis + Ed25519 verify (legacy single-purpose tool) |
Example agent flow:
```
agent: tools/call mithril_verify_chain {network: mainnet}
→ {verified: true, length: 89, genesis_hash: "...", steps: [...]}
```
| `mithril_verify_genesis` | Walk to genesis + Ed25519 verify |
## Dependencies
@ -165,14 +147,14 @@ go test -tags live ./... # hits live preprod aggregator
## Verified against live networks (latest run)
```
mainnet chain 89 STM + 1 genesis
mainnet head 59 signers, 1972 wins
mainnet genesis Ed25519
preprod chain 89 STM + 1 genesis
preprod head 2 signers, 11 wins
preprod genesis Ed25519
mainnet chain 89 STM + 1 genesis ok
mainnet head 59 signers, 1972 wins ok
mainnet genesis Ed25519 ok
preprod chain 89 STM + 1 genesis ok
preprod head 2 signers, 11 wins ok
preprod genesis Ed25519 ok
```
## License
Apache-2.0. See `LICENSE`. Matches the upstream Mithril project.
Apache-2.0. See `LICENSE`. Matches upstream Mithril.

View file

@ -25,14 +25,14 @@ import (
"text/tabwriter"
"time"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/artifact"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/chain"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/manifest"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/mcp"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/verify"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/artifact"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/chain"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/manifest"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/mcp"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/verify"
)
const version = "1.0.1"

View file

@ -4,12 +4,12 @@ import (
"context"
"fmt"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/chain"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/mcp"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/verify"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/chain"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/mcp"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/verify"
)
func errString(e error) string {

2
go.mod
View file

@ -1,4 +1,4 @@
module git.sulkta.coop/Sulkta-Coop/mithril-go
module git.sulkta.com/Sulkta-Coop/mithril-go
go 1.26

View file

@ -24,10 +24,10 @@ import (
"io"
"net/http"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.coop/Sulkta-Coop/mithril-go/internal/verify"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/aggregator"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/networks"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/stm"
"git.sulkta.com/Sulkta-Coop/mithril-go/internal/verify"
)
// Step is the per-cert record in a chain-verification report.

View file

@ -16,8 +16,7 @@
// 4. Lottery check: for each (index, sigma), evaluate_dense_mapping < threshold(stake)
// 5. Threshold: total distinct lottery wins >= k
//
// Phases 2-5 are stubbed in verify.go pending the BLS crypto sprint.
// This package's current role is rock-solid decoding.
// Phases 2-5 are implemented in verify.go (BLS, lottery, Merkle, threshold).
package stm
import (

View file

@ -7,8 +7,8 @@
// 2. Subsequent certificates carry an STM (Stake-based Threshold Multi-
// signature) aggregate signature over BLS12-381.
//
// Ed25519 (genesis) verification is fully implemented here. STM verification
// is stubbed pending the BLS crypto sprint.
// Ed25519 (genesis) verification lives here. STM BLS verification is in
// the sibling internal/stm package.
package verify
import (