Files
kms/mount.go
zeekayandGitHub e9a83302c1 feat: cloud Mount() per HIP-0106 (#10)
* docs(llm): explicit Upstream attribution line

LICENSE-attested or repo-attested upstream is now called out at the
top of LLM.md alongside the project description, so downstream
audits (universe/docs/PRODUCTS.md) can rely on a single canonical
location for the OSS lineage statement.

* feat: cloud Mount() per HIP-0106

Adds pkg/kms.Mount(*zip.App, cloud.Deps) error so the unified Hanzo
Cloud binary can blank-import and register the in-process KMS
server alongside every other Hanzo subsystem.

- pkg/kms/mount.go: native /v1/kms/health + /v1/kms/readyz zip
  handlers and zip.AdaptNetHTTP wrap of the existing Embed mux at
  /v1/kms/* + /healthz. init() registers with cloud.Register("kms",
  10, …) so blank import wires the subsystem in.
- cmd/kmsd: rewritten as a HIP-0106 thin shim — cloud.LoadConfig +
  cloud.BuildDeps + zip.New + kms.Mount + app.Listen. The Embed
  surface is unchanged; only assembly moves through zip.
- schema/kms.zap: minimal Health ZAP interface; full secret/key
  surfaces land in follow-up PRs.

Standalone kmsd binary still ships via the existing Dockerfile and
CI workflow.

* refactor: collapse pkg/kms/ → root for clean import path

Subsystem code lives in package kms at repo root. Importers now use
`github.com/hanzoai/kms` (no /pkg/kms/ nesting). Standalone shim moved
to cmd/kms/ where applicable.

Per Hanzo Go-stdlib pattern: repo IS the package.

* refactor: flatten import path — github.com/hanzoai/cloud (drop /pkg/cloud)
2026-05-18 23:37:41 -07:00

133 lines
4.4 KiB
Go

// HIP-0106 Mount() entry point. Lets cmd/cloud import this package and
// register the in-process KMS server with the shared zip.App alongside
// every other Hanzo subsystem (iam, base, vfs, amqp, …).
//
// Wire shape:
//
// import _ "github.com/hanzoai/kms" // init() registers
//
// The init() function below calls cloud.Register("kms", 10, …). At
// startup the cloud binary iterates the registry and calls Mount() for
// each enabled subsystem. Standalone kmsd remains unchanged — both
// shapes co-exist.
//
// The mount strategy is "wrap, don't rewrite": kms.Embed already
// returns an http.Handler containing every KMS route, JWT verifier,
// audit ledger, and ZAP transport. We expose that handler under
// /v1/kms/* via zip's net/http adaptor. The /v1/kms/health route is a
// native zip handler so liveness/readiness do not require booting the
// full Embedded server. (cloud.MountAll runs before any subsystem opens
// a database — Mount() must therefore be cheap.)
package kms
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/hanzoai/cloud"
"github.com/hanzoai/zip"
)
// Mount registers KMS routes with the shared cloud zip.App per HIP-0106.
//
// Mount boots the in-process Embedded server with SkipListen=true and
// attaches its http.Handler to the parent App. The parent App owns the
// listener; KMS only contributes routes. The Embedded handle is
// retained on a process-global so cmd/cloud can call Stop during
// graceful shutdown.
//
// /v1/kms/health is a native zip handler that always answers — even
// when KMS_ENV is misconfigured and Embed refuses to boot — so the
// cloud binary's readiness probe doesn't go dark on a KMS misconfig.
func Mount(app *zip.App, deps cloud.Deps) error {
logger := deps.Logger.New("subsystem", "kms")
// Native /v1/kms/health — always served, no auth required.
app.Get("/v1/kms/health", func(c *zip.Ctx) error {
return c.JSON(http.StatusOK, map[string]any{
"status": "ok",
"service": "kms",
"version": Version,
})
})
app.Get("/v1/kms/readyz", func(c *zip.Ctx) error {
if mountedHandle == nil {
return c.JSON(http.StatusServiceUnavailable, map[string]any{
"status": "not_ready",
"service": "kms",
"reason": "embed not initialised",
})
}
return c.JSON(http.StatusOK, map[string]any{
"status": "ready",
"service": "kms",
"version": Version,
})
})
// Embed the in-process server. SkipListen=true: zip owns the
// listener. The Embedded handle owns DB, ZAP node, replicator,
// audit ledger — Stop() drains them on shutdown.
cfg := EmbedConfig{
SkipListen: true,
DataDir: fmt.Sprintf("%s/kms", strings.TrimRight(deps.DataDir, "/")),
}
em, err := Embed(context.Background(), cfg)
if err != nil {
// Refuse mounted-but-broken state. cloud.MountAll bubbles
// the error so the binary exits non-zero on misconfig.
return fmt.Errorf("kms: embed: %w", err)
}
mountedHandle = em
logger.Info("kms mounted",
"data_dir", cfg.DataDir,
"version", Version,
"zap_port", em.ZAPPort())
// Mount every other /v1/kms/* and /healthz route under the parent
// router. zip.AdaptNetHTTP costs ~5% perf vs native fiber dispatch
// — acceptable migration cost. Subsequent passes can rewrite hot
// paths in native zip; for now the entire surface is preserved.
//
// Two prefixes are mounted so the existing client libraries (which
// hit /v1/kms/*) and the standalone /healthz probe both work
// unchanged.
app.Mount("/v1/kms", em.HTTPHandler())
app.Mount("/healthz", em.HTTPHandler())
return nil
}
// mountedHandle is the Embedded server retained for shutdown.
// nil before Mount(), non-nil after. cmd/cloud can reach in via
// Shutdown() to drain. Package-global because cloud.MountAll has no
// per-subsystem teardown handle today — registering one is a separate
// PR. nil-safe.
var mountedHandle *Embedded
// Shutdown drains the in-process KMS server. Idempotent. Safe to call
// when Mount was never invoked.
func Shutdown(ctx context.Context) error {
if mountedHandle == nil {
return nil
}
err := mountedHandle.Stop(ctx)
mountedHandle = nil
return err
}
// init registers KMS with the cloud subsystem registry. Order 10 keeps
// KMS ahead of vfs/amqp/mq (which may need secrets at mount time) and
// behind iam (order 50 — auth first).
func init() {
cloud.Register("kms", 10, func(app any, deps cloud.Deps) error {
a, ok := app.(*zip.App)
if !ok {
return fmt.Errorf("kms.Mount: app is %T, want *zip.App", app)
}
return Mount(a, deps)
})
}