Files
zeekay f3fbb687d2 purge: rip remaining Infisical residue — docs, env examples, repo boilerplate
Following the earlier AdminShell+CollectionCRUD rewrite (PR #14) which
already deleted backend/, backend-go/, cloudformation/, helm-charts/,
nginx/, sink/, wasm/, upgrade-impact/, e2e/, migration/, package.json
and switched the frontend to a 184K Hanzo-first build, complete the
cleanup by deleting the remaining Infisical-era residue:

  - docs/                                   (1.0G Infisical mintlify docs)
  - .env.example, .env.dev.example,
    .env.migration.example, .env.test.example
                                            (legacy Postgres/Redis envs;
                                             no Go code references them)
  - .eslintignore, .husky/                  (TS toolchain leftovers)
  - CODE_OF_CONDUCT.md, CONTRIBUTING.md,
    SECURITY.md                             (Infisical repo boilerplate)

Working tree drops from ~1.0G to <600K of content (1.5G total is .git/
which `git gc --aggressive --prune=now` shrinks locally; the remote
keeps its history shape — anyone pulling gets the lean checkout).

Build + test verification:
  go build ./...                            (4 cmd binaries: green)
  go test ./... -count=1 -short             (all packages: ok)

Kept: cmd/, pkg/, sdk/, frontend/ (AdminShell), schema/, examples/,
root Go (audit.go, auth.go, consensus.go, embed.go, jwks.go, mount.go,
versioning.go + tests), Dockerfile, Dockerfile.kms-fetch, Makefile,
LLM.md, CLAUDE.md, DEPRECATED.md, SOVEREIGN-KMS-ARCHITECTURE.md,
TFHE-KMS-ARCHITECTURE.md, LICENSE, README.md, VERSION, .github/
(8 Hanzo workflows: build, build-kms-fetch, check-fe-ts-and-lint,
ci, pr-preview, release, validate-pr-title, workflow-sanity).
2026-06-07 14:23:12 -07:00

114 lines
2.8 KiB
Go

// Copyright (C) 2020-2026, Hanzo AI Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package kms
import (
"encoding/json"
"fmt"
"net/url"
)
// inviteMemberRequest is the JSON payload sent to add a member.
type inviteMemberRequest struct {
MemberPubKey []byte `json:"member_pub_key"`
WrappedCEK []byte `json:"wrapped_cek"`
}
// memberListResponse is the JSON response from ListMembers.
type memberListResponse struct {
Members []string `json:"members"`
}
// InviteMember wraps the CEK with the new member's HPKE public key
// (ML-KEM-768+X25519 hybrid) and distributes the wrapped CEK to the MPC nodes.
// Returns the wrapped CEK so the caller can deliver it out-of-band if needed.
func (c *Client) InviteMember(memberPubKey []byte) ([]byte, error) {
cek, err := c.getCEK()
if err != nil {
return nil, err
}
defer clear(cek)
wrapped, err := WrapCEKForMember(cek, memberPubKey)
if err != nil {
return nil, fmt.Errorf("kms: invite member: %w", err)
}
payload := inviteMemberRequest{
MemberPubKey: memberPubKey,
WrappedCEK: wrapped,
}
data, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("kms: invite member: marshal: %w", err)
}
path := fmt.Sprintf("/v1/orgs/%s/zk/members", url.PathEscape(c.orgSlug))
if err := c.broadcastPost(path, data); err != nil {
return nil, fmt.Errorf("kms: invite member: %w", err)
}
return wrapped, nil
}
// RevokeMember removes a member's access. This rotates the CEK and re-wraps
// for all remaining members. The member ID should be the IAM user identifier.
func (c *Client) RevokeMember(memberID string) error {
if memberID == "" {
return fmt.Errorf("kms: member ID must not be empty")
}
path := fmt.Sprintf("/v1/orgs/%s/zk/members/%s",
url.PathEscape(c.orgSlug),
url.PathEscape(memberID),
)
// Broadcast DELETE to all nodes.
type result struct {
err error
}
ch := make(chan result, len(c.nodes))
for _, node := range c.nodes {
go func(addr string) {
ch <- result{err: c.deleteRequest(addr + path)}
}(node)
}
var successes int
var lastErr error
for range c.nodes {
r := <-ch
if r.err != nil {
lastErr = r.err
} else {
successes++
}
}
if successes < c.threshold {
return fmt.Errorf("kms: revoke member: only %d of %d nodes responded (need %d): %w",
successes, len(c.nodes), c.threshold, lastErr)
}
return nil
}
// ListMembers returns the IDs of all members with access to this org's secrets.
func (c *Client) ListMembers() ([]string, error) {
path := fmt.Sprintf("/v1/orgs/%s/zk/members", url.PathEscape(c.orgSlug))
body, err := c.quorumGet(path)
if err != nil {
return nil, fmt.Errorf("kms: list members: %w", err)
}
var resp memberListResponse
if err := json.Unmarshal(body, &resp); err != nil {
return nil, fmt.Errorf("kms: list members: unmarshal: %w", err)
}
return resp.Members, nil
}