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
- Shamir Secret Sharing — Split master key across N parties (M-of-N reconstruction)
- Transit Encryption — Encrypt/decrypt data without exposing keys (envelope encryption)
- Leases — Time-bound secret access with automatic revocation
- Seal/Unseal — Cold-start protection requiring quorum to activate
- ACL Policies — Fine-grained access control per path/operation
- Token Management — Scoped, renewable auth tokens with TTL
- Dynamic Secrets — On-demand credential generation (DB users, cloud IAM)
- TFHE Bridge — Fully homomorphic encryption for compute-on-encrypted-secrets (in development)
- 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
- KMS instance at
kms.hanzo.aior self-hosted - Universal Auth credentials (client ID + secret) for machine access
- Project slug minimum 5 characters (e.g.,
hanzo-paasnotpaas)
Quick reference
| Item | Value |
|---|---|
| UI | https://kms.hanzo.ai |
| API | https://api.hanzo.ai/v1/kms/kms (also https://api.hanzo.ai/v1/kms) |
| Auth | Universal Auth (client ID + secret) |
| K8s CRD | KMSSecret |
| Go SDK | github.com/hanzoai/kms-go-sdk |
| Node SDK | @hanzo/kms-node-sdk |
| Python SDK | hanzo-kms (pip) |
| Repo | github.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.
| Method | Path | What it does |
|---|---|---|
| GET | /v1/kms/health | Liveness + whether the store holds a master key |
| GET | /v1/kms/config | The console SPA's runtime config (issuer, login path) |
| POST | /v1/kms/auth/login | Exchange {clientId, clientSecret} for {accessToken, expiresIn, tokenType}. Public, rate-limited per source IP |
| GET | /v1/kms/secrets | List your org's secret metadata — no values, no ciphertext. ?path= narrows, ?env= selects (omit for every environment) |
| POST | /v1/kms/secrets | Upsert 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_KEYThe 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: tokenCI/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
| Issue | Cause | Solution |
|---|---|---|
| Slug error on project creation | Slug < 5 chars | Use hanzo-paas not paas |
| Universal Auth login fails | Wrong client credentials | Regenerate in KMS UI |
| KMSSecret not syncing | Operator not running | Check kms-operator pod |
| Token key mismatch | Custom bootstrap template | Set additionalOrganizationsTokenSecretKey |
Related Skills
hanzo/hanzo-id.md- IAM (KMS uses IAM for user auth)hanzo/hanzo-vault.md- PCI card tokenizationhanzo/hanzo-platform.md- PaaS (uses KMS for secrets)hanzo/hanzo-universe.md- Production K8s manifests
How is this guide?