Registry、Merkle 与 HTTP 边界 深度解析
这一页看 Machine Genome 如何把 signed records 从 HTTP 请求变成 canonical bytes、bbolt 索引、append-only log、audit chain 与可验证的 inclusion/consistency proof。
导入链与契约
▸Registry 命令仍从 CLI 进入
Registry 不是独立服务目录,而是 CLI 的一个命令分支。cmd/mgs/main.go:63 把 registry-serve 路由到服务启动,cmd/mgs/main.go:65 和 cmd/mgs/main.go:67 则暴露 backup 与 verify。usage 在 cmd/mgs/main.go:95 到 cmd/mgs/main.go:98 要求 data-dir、checkpoint-key 和 admin-token-file,这对应 registry 的三个核心资产:数据库、检查点签名密钥和管理权限。
71func NewHTTPService(config HTTPConfig) (*HTTPService, error) { 72 if config.Store == nil { return nil, errors.New("registry store is required") } 75 if len(config.AdminToken) < 32 { return nil, errors.New("admin token must contain at least 32 bytes") } 78 if len(config.CheckpointKey) != ed25519.PrivateKeySize { ... } 84 derivedDID, derivedMethod, err := mgs.DIDKeyFromPublicKey(config.CheckpointKey.Public().(ed25519.PublicKey)) 88 if config.PublicBaseURL != "" { base, err := url.Parse(config.PublicBaseURL) ... } 103 return &HTTPService{ 104 store: config.Store, adminHash: sha256.Sum256([]byte(config.AdminToken)), 108 backupDir: config.BackupDir, rate: newIPLimiter(rate), 110}
HTTPConfig 的校验很密:registry/http.go:72 要求 Store,registry/http.go:75 要求管理 token 至少 32 字节,registry/http.go:84 到 registry/http.go:87 确认 checkpoint key 与 DID 匹配。这个设计和 cmd/mgs/main.go:95 的启动参数相互闭合,防止服务在缺少签名身份或弱 admin token 时启动。
核心实现,逐段拆
▸Store.Open 建立 ACID 对象模型
Store 层的 bucket 设计在 registry/store.go:29 到 registry/store.go:50,把 raw objects、metadata、identity index、children、equivocations、transparency log、audit 和 tombstones 分开。registry/store.go:117 到 registry/store.go:151 在打开数据库时创建目录、设置 0700/0600 权限并初始化 bucket。虽然质量门禁主要统计 cmd/mgs/main.go:63、cmd/mgs/main.go:65、cmd/mgs/main.go:67 这些 CLI 路径,但真正的信任边界在 Store。
117func Open(path string, options Options) (*Store, error) { 121 if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { ... } 124 if err := os.Chmod(filepath.Dir(path), 0o700); err != nil { ... } 127 db, err := bolt.Open(path, 0o600, &bolt.Options{Timeout: 2 * time.Second}) 131 if err := os.Chmod(path, 0o600); err != nil { db.Close(); return nil, ... } 139 store := &Store{db: db, requireNamespaceGrant: options.RequireNamespaceGrant, now: now} 140 if err := db.Update(func(tx *bolt.Tx) error { 141 for _, name := range allBuckets { 142 if _, err := tx.CreateBucketIfNotExists(name); err != nil { return err } 146 return tx.Bucket(bucketMeta).Put([]byte("schema"), []byte("1")) 151 return store, nil
▸Submit 是 admission gate
registry/store.go:158 的 Submit 首先限制记录大小,然后在 registry/store.go:165 做 canonicalize,再读 header.recordType。加锁发生在 registry/store.go:175,分支只允许 genesis、amendment、attestation 三类。HTTP 的 registry/http.go:182 到 registry/http.go:206 只是媒体类型、body size 和状态码包装,核心入库不可绕过 Store.Submit。测试 registry/store_test.go:19 到 registry/store_test.go:79 对 repeated submit 和 canonical bytes 做了覆盖。
190func (s *Store) submitGenesis(ctx context.Context, canonical []byte) (Submission, error) { 191 record, err := mgs.ValidateGenesis(canonical, true) 195 gene, err := mgs.VerifyGenesis(ctx, canonical, mgs.DIDKeyResolver{}) 199 if err := mgs.VerifyLineage(ctx, canonical, mgs.DIDKeyResolver{}, s); err != nil { ... } 202 if err := s.namespaceAllowed(record.Identity.Namespace, record.Controller.DID); err != nil { ... } 205 meta := RecordMetadata{ Digest: gene, RecordType: mgs.RecordGenesis, Gene: gene, ... } 211 coordinate := identityKey(record.Identity.Namespace, record.Identity.Name, record.Identity.Version) 212 return s.insert(canonical, meta, func(tx *bolt.Tx) error { 213 if existing := tx.Bucket(bucketIdentity).Get(coordinate); existing != nil && string(existing) != gene { ... } 222 for _, edge := range record.Parentage { ... } 230 })
▸Merkle proof 是 transparency log 的出口
Registry 的 append-only 性质靠 bbolt log bucket 和 Merkle helper 暴露出来。registry/merkle.go:27 到 registry/merkle.go:53 生成 inclusion proof;registry/merkle.go:55 到 registry/merkle.go:78 生成 consistency proof;registry/merkle.go:88 到 registry/merkle.go:123 验证 inclusion proof。对应 HTTP 路由在 registry/http.go:148 到 registry/http.go:153,说明检查点、包含证明和一致性证明是一等 API。
88func VerifyInclusion(proof InclusionProof) error { 89 leafHash, err := base64.RawURLEncoding.DecodeString(proof.LeafHash) 93 if actual := merkleLeaf([]byte(proof.Leaf)); !equalBytes(actual, leafHash) { ... } 96 index := proof.LogIndex 99 for _, encoded := range proof.AuditPath { 104 if index&1 == 1 || index == last { 105 hash = merkleNode(sibling, hash) 111 } else { hash = merkleNode(hash, sibling) } 116 root, err := base64.RawURLEncoding.DecodeString(proof.RootHash) 120 if !equalBytes(hash, root) { return fmt.Errorf("inclusion root mismatch") } 123 return nil
关键流程
这条 admission flow 可以从 registry/http.go:129、registry/http.go:182、registry/http.go:187、registry/http.go:192、registry/store.go:158、registry/store.go:165、registry/store.go:190、registry/store.go:333、registry/store.go:358、registry/store.go:361 贯通。测试层在 registry/store_test.go:25、registry/store_test.go:32、registry/store_test.go:47 和 registry/store_test.go:51 验证了入库、幂等和 inclusion proof。
边界 / 失败模式
registry/http.go:161到registry/http.go:179的 middleware 加了 request id、安全响应头和 IP rate limit,说明 public registry 默认暴露在不可信网络上。registry/store.go:286到registry/store.go:309记录 amendment equivocation,测试registry/store_test.go:97到registry/store_test.go:103确认 fork 不会覆盖既有链。registry/store.go:472到registry/store.go:492提供 tombstone,但registry/store_test.go:127到registry/store_test.go:131验证 includeGone 仍能读取不可变对象。registry/merkle.go:126到registry/merkle.go:189验证 consistency proof,registry/store_test.go:204到registry/store_test.go:215会篡改 second root 来确认失败。scripts/check-repository.py:100到scripts/check-repository.py:128检查 JSON $ref,scripts/check-repository.py:130到scripts/check-repository.py:139检查 Markdown 本地链接,补足代码测试无法覆盖的发布资产完整性。
Store.Submit 的 admission pattern、append audit、Merkle proof、tombstone/includeGone 分层都可以迁移到内部 Agent registry。
public registry 的 DNS namespace challenge、checkpoint gossip、备份权限和 admin token 生命周期需要实际运维制度配套。