Quickstart
Log in with a machine identity, write a secret, read it back
KMS quickstart
Four calls end to end. Everything here runs against https://api.hanzo.ai, the one host for every Hanzo service; https://kms.hanzo.ai serves the identical routes.
Get a machine identity
A machine identity is a client-credentials application in Hanzo IAM — a clientId and clientSecret that belong to your org, not to a person. Register one there, then keep the pair out of source control: in a cluster it lives in a Kubernetes Secret, on a laptop in your shell environment.
export KMS_CLIENT_ID=…
export KMS_CLIENT_SECRET=…Exchange it for a token
ACCESS_TOKEN=$(curl -sS -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){ "accessToken": "<JWT>", "expiresIn": 3600, "tokenType": "Bearer" }The token's owner claim is your org. That claim — not anything in the URL — decides whose secrets the following calls reach.
A 401 here means the credential did not authenticate; 502 means IAM was unreachable; 503 means this deployment has no issuer configured. The response deliberately carries no upstream detail, so do not read a 401 as "this clientId exists but the secret is wrong."
Write a secret
curl -sS -X POST https://api.hanzo.ai/v1/kms/secrets \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"path":"/providers","name":"OPENAI_API_KEY","env":"prod","value":"sk-…"}'{ "stored": true, "name": "OPENAI_API_KEY", "env": "prod" }env is required on a write and has no default. It is part of the storage key, so a defaulted write would land in env=default while every reader resolving env=prod keeps serving the stale value. The write refuses rather than split the record.
The value is sealed before it is written — a fresh per-secret data key, itself wrapped by the master key — so plaintext never reaches disk. The receipt confirms what was written and does not echo the value.
Read it back
curl -sS "https://api.hanzo.ai/v1/kms/secrets/providers/OPENAI_API_KEY?env=prod" \
-H "Authorization: Bearer $ACCESS_TOKEN"{ "name": "OPENAI_API_KEY", "env": "prod", "value": "sk-…" }A secret at your org root omits the folder: …/secrets/STRIPE_SECRET_KEY?env=prod.
See what you hold
curl -sS "https://api.hanzo.ai/v1/kms/secrets?path=/providers&env=prod" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"secrets": [ { "name": "OPENAI_API_KEY", "path": "/providers", "env": "prod" } ],
"total": 1,
"names": ["OPENAI_API_KEY"]
}This is an enumeration, not a bulk read — names, paths, environments and sealing schemes come back, values never do. Omit env to see every environment; omit path to see the whole org.
Delete
curl -sS -X DELETE "https://api.hanzo.ai/v1/kms/secrets/providers/OPENAI_API_KEY?env=prod" \
-H "Authorization: Bearer $ACCESS_TOKEN"{ "deleted": true, "name": "OPENAI_API_KEY", "env": "prod" }Deleting a secret that is not there is a 404, not a silent success, so you can tell a real deletion from a typo.
Check the service
curl -sS https://api.hanzo.ai/v1/kms/health{ "status": "ok", "ready": true, "signing": false, "service": "kms" }200 only when the store is open and a master key is configured. A 503 with ready:false names which of the two is missing — and those are exactly the states in which the secret calls above will refuse.
Next steps
All seven routes, addressing rules, failure modes
Token lifetime, scoping, and rotation
Stop calling the API — declare the sync instead
How is this guide?