Hanzo

Hanzo KMS

Key Management Service for secrets, machine-identity access, and threshold signing keys

Hanzo KMS

API reference · Hanzo KMS API → — every endpoint, generated from the OpenAPI spec.

Hanzo KMS is the key management service for the Hanzo platform. It stores secrets and API keys, hands them to workloads through short-lived machine-identity tokens, syncs them into Kubernetes, and manages threshold signing keys backed by Hanzo MPC.

APIhttps://kms.hanzo.ai
API prefix/v1/kms
AuthMachine-identity login → bearer JWT (verified against Hanzo IAM)
K8s syncKMSSecret CRD via the kms-operator
Sourcegithub.com/hanzoai/kms

Hanzo KMS exposes a single surface under /v1/kms. Secrets are addressed by org / path / name with the environment as a query parameter. There is no plaintext-secret-in-config anywhere — workloads authenticate with a machine identity and read values at runtime.

Authentication

KMS uses machine identities: a non-human clientId + clientSecret minted in Hanzo IAM. Exchange them for a short-lived access token, then send that token as a bearer on every request.

# Log in (clientId/clientSecret come from a K8s Secret or your secret store — never inline them)
ACCESS_TOKEN=$(curl -sS -X POST https://kms.hanzo.ai/v1/kms/auth/login \
  -H 'Content-Type: application/json' \
  -d "{\"clientId\":\"$KMS_CLIENT_ID\",\"clientSecret\":\"$KMS_CLIENT_SECRET\"}" \
  | jq -r .accessToken)

Response:

{ "accessToken": "<RS256 JWT>", "expiresIn": 86400, "tokenType": "Bearer" }

The token is a standard JWT whose signature, issuer, and audience are validated against IAM's JWKS. Its owner claim is the organization it can act on — a token can only read secrets under its own {org}.

Secret hierarchy

A secret is addressed by four coordinates:

org   /  path        /  name              ? env
hanzo /  providers   /  OPENAI_API_KEY    ? env=production
  • org — the organization slug; must match the token's owner claim.
  • path — a folder within the org (e.g. /, /providers, /db).
  • name — the exact secret key.
  • env — a query parameter; defaults to default when omitted.

KMS reads are exact-name — there is no "list all secrets in a folder" endpoint. Enumerate the keys your workload needs (the KMSSecret CRD does exactly this).

Reading & writing secrets

All routes are under https://kms.hanzo.ai/v1/kms/orgs/{org}/secrets.

MethodPathPurpose
GET/v1/kms/orgs/{org}/secrets/{path}/{name}?env=Read a secret value + version
POST/v1/kms/orgs/{org}/secretsCreate/upsert ({path,name,env,value}) → {ok,version}
PATCH/v1/kms/orgs/{org}/secrets/{path}/{name}Update (optimistic concurrency — see below)
DELETE/v1/kms/orgs/{org}/secrets/{path}/{name}?env=Delete

Read

curl -sS "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=production" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
{ "secret": { "value": "<secret value>" }, "version": 3 }

A root-path secret omits the folder segment: …/secrets/STRIPE_SECRET_KEY?env=production.

Create

curl -sS -X POST "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "path": "/providers", "name": "OPENAI_API_KEY", "env": "production", "value": "<new value>" }'
{ "ok": true, "version": 1 }

Update (optimistic concurrency)

Updates require the current version, supplied either as an If-Match header or version in the body. A missing precondition returns 428 Precondition Required; a stale version returns 409 Conflict with the current version.

curl -sS -X PATCH "https://kms.hanzo.ai/v1/kms/orgs/hanzo/secrets/providers/OPENAI_API_KEY?env=production" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "If-Match: 3" \
  -H "Content-Type: application/json" \
  -d '{ "value": "<rotated value>" }'

Threshold signing keys

When KMS is wired to an MPC vault, it manages distributed signing keys whose private material is never assembled in one place:

MethodPathPurpose
POST/v1/kms/keys/generateDistributed key generation (DKG) for a validator/signing key set
GET/v1/kms/keys · /v1/kms/keys/{id}List / inspect key sets
POST/v1/kms/keys/{id}/signThreshold sign (e.g. bls)
POST/v1/kms/keys/{id}/rotateReshare / rotate shares

These operations require an admin or MPC-scoped token. See Hanzo MPC for the underlying threshold scheme.

Kubernetes integration

In a cluster, you never call the API directly — you declare a KMSSecret and the kms-operator logs in with a machine identity, reads the named keys, and writes a Kubernetes Secret you reference as normal:

apiVersion: secrets.lux.network/v1alpha1
kind: KMSSecret
metadata:
  name: app-kms-sync
  namespace: hanzo
spec:
  hostAPI: https://kms.hanzo.ai
  resyncInterval: 60
  authentication:
    universalAuth:
      credentialsRef:
        secretName: app-kms-auth        # K8s Secret holding clientId/clientSecret
        secretNamespace: hanzo
      secretsScope:
        projectSlug: hanzo               # -> {org}
        envSlug: production              # -> ?env=production
        secretsPath: /providers          # -> {path}
        keys:                            # enumerated; no list endpoint
          - OPENAI_API_KEY
          - ANTHROPIC_API_KEY
  managedSecretReference:
    secretName: app-secrets
    secretNamespace: hanzo
    secretType: Opaque
    creationPolicy: Orphan

See the KMSSecret CRD reference for the full spec.

Next Steps

How services authenticate to KMS and refresh tokens

Complete Kubernetes sync reference

The pattern every Hanzo service uses to consume secrets

Threshold signing backend for KMS-managed keys

How is this guide?

Last updated on

On this page