Hanzo
Hanzo Skills Reference

Hanzo KMS - Key & Secret Management

Hanzo KMS is the centralized secret management platform for the Hanzo ecosystem — Universal Auth for machine-to-machine access, per-org root key isolation, and K8s-native secret sync via the KMSSecret CRD. Live at `kms.hanzo.ai`.

Overview

Hanzo KMS is the centralized secret management platform for the Hanzo ecosystem. It holds every secret the platform uses — API keys, database credentials, signing keys — encrypted at rest under a per-organization root key, and delivers them to workloads through Universal Auth (machine-to-machine) or K8s-native sync via the KMSSecret CRD. Live at kms.hanzo.ai. Nothing else in the stack stores a secret.

Why Hanzo KMS?

  • Zero plaintext secrets: All secrets encrypted at rest, synced to K8s
  • Universal Auth: Machine-to-machine authentication for CI/CD
  • RBAC: Per-org, per-project, per-environment access control
  • Auto-rotation: Scheduled secret rotation with audit trail
  • Multi-language SDKs: Go, Node.js, Python clients

Implementation

TypeScript/Node.js backend. Repo: hanzoai/kms. Implements nine Vault-style subsystems, so a team already fluent in HashiCorp Vault concepts will find the same primitives here.

Vault Subsystems

  1. Shamir Secret Sharing — Split master key across N parties (M-of-N reconstruction)
  2. Transit Encryption — Encrypt/decrypt data without exposing keys (envelope encryption)
  3. Leases — Time-bound secret access with automatic revocation
  4. Seal/Unseal — Cold-start protection requiring quorum to activate
  5. ACL Policies — Fine-grained access control per path/operation
  6. Token Management — Scoped, renewable auth tokens with TTL
  7. Dynamic Secrets — On-demand credential generation (DB users, cloud IAM)
  8. TFHE Bridge — Fully homomorphic encryption for compute-on-encrypted-secrets (in development)
  9. Per-Org Root Key Isolation — Each organization has its own root encryption key

When to use

  • Storing API keys, database credentials, tokens
  • Syncing secrets to K8s workloads
  • CI/CD pipeline secret injection
  • Multi-org secret isolation
  • Auditing secret access

Hard requirements

  1. KMS instance at kms.hanzo.ai or self-hosted
  2. Universal Auth credentials (client ID + secret) for machine access
  3. Project slug minimum 5 characters (e.g., hanzo-paas not paas)

Quick reference

ItemValue
UIhttps://kms.hanzo.ai
APIhttps://api.hanzo.ai/v1/kms/kms (also https://api.hanzo.ai/v1/kms)
AuthUniversal Auth (client ID + secret)
K8s CRDKMSSecret
Go SDKgithub.com/hanzoai/kms-go-sdk
Node SDK@hanzo/kms-node-sdk
Python SDKhanzo-kms (pip)
Repogithub.com/hanzoai/kms

REST surface

Seven routes, all under /v1/kms. There is no other HTTP surface — cross-org access exists only in-process, never over HTTP.

MethodPathWhat it does
GET/v1/kms/healthLiveness + whether the store holds a master key
GET/v1/kms/configThe console SPA's runtime config (issuer, login path)
POST/v1/kms/auth/loginExchange {clientId, clientSecret} for {accessToken, expiresIn, tokenType}. Public, rate-limited per source IP
GET/v1/kms/secretsList your org's secret metadata — no values, no ciphertext. ?path= narrows, ?env= selects (omit for every environment)
POST/v1/kms/secretsUpsert one secret: {path?, name, env, value}. env is required on a write and has no default
GET/v1/kms/secrets/{subpath}/{name}Read one value. ?env= selects; the response body is the only place the value appears
DELETE/v1/kms/secrets/{subpath}/{name}Delete one secret. ?env= selects

Admission is fail-closed and in order: validated member (403), org that is a DNS-1123 label (400), store holding a master key (503) — all decided before any record is touched.

One-file quickstart

CLI (fetch secrets)

# Login — the broker exchanges your machine credential for an IAM bearer
export KMS_TOKEN=$(curl -s -X POST https://api.hanzo.ai/v1/kms/auth/login \
  -H "Content-Type: application/json" \
  -d '{"clientId": "'$KMS_CLIENT_ID'", "clientSecret": "'$KMS_CLIENT_SECRET'"}' \
  | jq -r '.accessToken')

# List your org's secrets (metadata only — no values)
curl -s https://api.hanzo.ai/v1/kms/secrets \
  -H "Authorization: Bearer ${KMS_TOKEN}" \
  -G -d "path=/ci" -d "env=production"

# Read one value
curl -s "https://api.hanzo.ai/v1/kms/secrets/ci/DATABASE_URL?env=production" \
  -H "Authorization: Bearer ${KMS_TOKEN}" | jq -r '.value'

The org is taken from your validated token, never from the path or the body — a caller cannot name another tenant's namespace. env and path are also accepted under the operator's spellings, environment and secretPath.

Go SDK

import kms "github.com/hanzoai/kms-go-sdk"

client := kms.NewClient(kms.Config{
    SiteURL:      "https://kms.hanzo.ai",
    ClientID:     os.Getenv("KMS_CLIENT_ID"),
    ClientSecret: os.Getenv("KMS_CLIENT_SECRET"),
})

secret, err := client.GetSecret(kms.GetSecretOptions{
    ProjectID:   "hanzo-paas",
    Environment: "production",
    SecretName:  "DATABASE_URL",
})
fmt.Println(secret.Value)

Node.js SDK

import { KMSClient } from "@hanzo/kms-node-sdk"

const client = new KMSClient({
  siteUrl: "https://kms.hanzo.ai",
  clientId: process.env.KMS_CLIENT_ID!,
  clientSecret: process.env.KMS_CLIENT_SECRET!,
})

const secret = await client.getSecret({
  projectId: "hanzo-paas",
  environment: "production",
  secretName: "DATABASE_URL",
})

Python SDK

from hanzo_kms import KMSClient

client = KMSClient(
    site_url="https://kms.hanzo.ai",
    client_id=os.environ["KMS_CLIENT_ID"],
    client_secret=os.environ["KMS_CLIENT_SECRET"],
)

secret = client.get_secret(
    project_id="hanzo-paas",
    environment="production",
    secret_name="DATABASE_URL",
)

Core Concepts

K8s Secret Sync (KMSSecret CRD)

apiVersion: kms.hanzo.ai/v1
kind: KMSSecret
metadata:
  name: my-app-secrets
  namespace: default
spec:
  project: hanzo-paas
  environment: production
  syncInterval: 5m
  secretRef:
    name: my-app-secrets  # K8s Secret to create/update
  secrets:
    - DATABASE_URL
    - REDIS_URL
    - HANZO_API_KEY

The KMS operator (hanzoai/kms-operator) watches KMSSecret resources and auto-syncs to K8s Secrets.

Helm Chart: Auto-Bootstrap

# values.yaml for kms-standalone chart
kms:
  autoBootstrap:
    additionalOrganizations:
      - hanzo
      - lux
      - zoo
    additionalOrganizationAdminEmails:
      - admin@example.com
    additionalOrganizationsTokenSecretKey: token

CI/CD Integration

# .github/workflows/deploy.yml
jobs:
  deploy:
    steps:
      - name: Login to KMS
        run: |
          export KMS_TOKEN=$(curl -s -X POST $KMS_URL/v1/kms/auth/login \
            -H "Content-Type: application/json" \
            -d '{"clientId":"${{ secrets.KMS_CLIENT_ID }}","clientSecret":"${{ secrets.KMS_CLIENT_SECRET }}"}' \
            | jq -r '.accessToken')
          echo "KMS_TOKEN=$KMS_TOKEN" >> $GITHUB_ENV

      - name: Fetch deploy secrets
        run: |
          DOCKERHUB_TOKEN=$(curl -s "$KMS_URL/v1/kms/secrets/DOCKERHUB_TOKEN?env=production" \
            -H "Authorization: Bearer $KMS_TOKEN" | jq -r '.value')

Troubleshooting

IssueCauseSolution
Slug error on project creationSlug < 5 charsUse hanzo-paas not paas
Universal Auth login failsWrong client credentialsRegenerate in KMS UI
KMSSecret not syncingOperator not runningCheck kms-operator pod
Token key mismatchCustom bootstrap templateSet additionalOrganizationsTokenSecretKey
  • hanzo/hanzo-id.md - IAM (KMS uses IAM for user auth)
  • hanzo/hanzo-vault.md - PCI card tokenization
  • hanzo/hanzo-platform.md - PaaS (uses KMS for secrets)
  • hanzo/hanzo-universe.md - Production K8s manifests

How is this guide?

On this page