Files
zeekayandHanzo Dev 01d52f0aec
Hanzo CI/CD / cicd (push) Failing after 54m52s
CI/CD / cicd (push) Failing after 54m52s
Declare operations on the group that serves them
zip's registration is a verb method on the group, so an operation reads as the
group answering it rather than a function taking the group, and a route that is
not a typed operation says which method it answers with. The package-level form
and the Router interface it existed to accept any target through are both gone.

The toolchain and the pin move together, deliberately. Below zip v1.36.50 the
JSON encoder was selected on a build tag, so a repo whose go directive rises
first writes a wire that is neither the old release's nor the new one's until the
pin catches up.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 06:03:33 -07:00

107 lines
3.5 KiB
Go

// Package kms — canonical client interface for Hanzo KMS.
//
// import kms "github.com/hanzoai/kms/sdk/go"
// var v kms.Vault = kms.NewClient(cfg)
package kms
import (
"context"
"errors"
"time"
)
// Typed errors. Callers branch via errors.Is.
var (
ErrSecretNotFound = errors.New("kms: secret not found")
ErrVersionMismatch = errors.New("kms: version mismatch")
ErrVersionNotFound = errors.New("kms: version not found")
ErrSecretLocked = errors.New("kms: secret locked")
ErrRotateUnsupported = errors.New("kms: rotation unsupported by backend")
)
// Vault is the secrets-storage surface. Namespaced at construction;
// every method takes leaf names. Goroutine-safe.
type Vault interface {
// Kind reports the backend identifier
// (hanzo-kms | aws-secrets-manager | hashicorp-vault | doppler | env).
Kind() string
// Get returns the secret value. Empty + non-nil error on missing.
// The empty + nil case is never returned.
Get(ctx context.Context, name string) ([]byte, error)
// GetWithMeta is Get plus the metadata sidecar.
GetWithMeta(ctx context.Context, name string) ([]byte, SecretMeta, error)
// Put writes a new version. PutOpts.IfVersion provides
// compare-and-set: when non-empty, the write returns
// ErrVersionMismatch unless the current version matches.
Put(ctx context.Context, name string, value []byte, opts PutOpts) (version string, err error)
// Rotate generates new material atomically. kind selects the
// generator (api_key | webhook_secret | dsn_password | jwt_signing_key).
// Backends without managed rotation return ErrRotateUnsupported;
// callers fall back to Put with their own generator.
//
// Dual-read window: the prior version remains readable via
// GetVersion(prev) until prevValidUntil. Implementations MUST
// honor this window for stateless rotations.
Rotate(ctx context.Context, name, kind string) (rotation Rotation, err error)
// GetVersion reads a specific historical version. Used by callers
// holding stale credentials during a rotation grace window.
GetVersion(ctx context.Context, name, version string) ([]byte, error)
// List paginates leaf names under prefix. Cursor-based.
List(ctx context.Context, prefix string, opts ListOpts) (*ListPage, error)
}
// PutOpts configures a Put.
type PutOpts struct {
// IfVersion is the expected current version. Empty disables the
// compare-and-set. Mismatch returns ErrVersionMismatch.
IfVersion string
// Tags overwrite the metadata Tags sidecar. Empty leaves Tags
// unchanged.
Tags map[string]string
}
// Rotation is the result of a Rotate call.
type Rotation struct {
Name string
NewValue []byte
NewVersion string
PreviousVersion string
// PreviousValidUntil names when the prior version stops resolving
// via Get/GetWithMeta (still readable via GetVersion). Zero means
// no grace window — backends that hard-cut MUST document the
// breakage in operator runbooks.
PreviousValidUntil time.Time
}
// SecretMeta is the sidecar returned by GetWithMeta.
type SecretMeta struct {
Name string
Version string
CreatedAt time.Time
UpdatedAt time.Time
// Tags reserved keys:
// environment — sandbox | production
// rotated_by — last-rotation actor id
// rotation_due — RFC3339 timestamp
Tags map[string]string
}
// ListOpts is the cursor-pagination shape.
type ListOpts struct {
Cursor string
Limit int // implementations cap at 1000
}
// ListPage is one slice of a List call.
type ListPage struct {
Items []string
NextCursor string
}